web_app.cc revision c407dc5cd9bdc5668497f21b26b09d988ab439de
1c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file.
4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/web_applications/web_app.h"
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#if defined(OS_WIN)
8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <shellapi.h>
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif  // defined(OS_WIN)
10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <algorithm>
12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <string>
13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <vector>
14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/callback.h"
16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/file_util.h"
17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/md5.h"
18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/message_loop.h"
19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/path_service.h"
20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/thread.h"
21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/scoped_ptr.h"
22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/utf_string_conversions.h"
23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/chrome_thread.h"
24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/download/download_util.h"
25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/profile.h"
26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/tab_contents/tab_contents.h"
27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/chrome_constants.h"
28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/chrome_paths.h"
29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/chrome_plugin_util.h"
30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/notification_registrar.h"
31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/notification_service.h"
32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/url_constants.h"
33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "webkit/glue/dom_operations.h"
34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#if defined(OS_LINUX)
36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/env_var.h"
37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif  // defined(OS_LINUX)
38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#if defined(OS_WIN)
40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/win_util.h"
41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "gfx/icon_util.h"
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif  // defined(OS_WIN)
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace {
45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#if defined(OS_WIN)
47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst FilePath::CharType kIconChecksumFileExt[] = FILE_PATH_LITERAL(".ico.md5");
48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Returns true if |ch| is in visible ASCII range and not one of
50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// "/ \ : * ? " < > | ; ,".
51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool IsValidFilePathChar(char16 c) {
52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (c < 32)
53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  switch (c) {
56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case '/':
57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case '\\':
58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case ':':
59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case '*':
60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case '?':
61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case '"':
62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case '<':
63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case '>':
64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case '|':
65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case ';':
66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case ',':
67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return false;
68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  };
69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;
71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Returns sanitized name that could be used as a file name
74c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochFilePath GetSanitizedFileName(const string16& name) {
75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  string16 file_name;
76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (size_t i = 0; i < name.length(); ++i) {
78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    char16 c = name[i];
79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!IsValidFilePathChar(c))
80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      c = '_';
81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    file_name += c;
83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return FilePath(file_name);
86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif  // defined(OS_WIN)
88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Returns relative directory of given web app url.
90c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochFilePath GetWebAppDir(const GURL& url) {
91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  FilePath::StringType host;
92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  FilePath::StringType scheme_port;
93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#if defined(OS_WIN)
95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  host = UTF8ToWide(url.host());
96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  scheme_port = (url.has_scheme() ? UTF8ToWide(url.scheme()) : L"http") +
97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      FILE_PATH_LITERAL("_") +
98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      (url.has_port() ? UTF8ToWide(url.port()) : L"80");
99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#elif defined(OS_POSIX)
100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  host = url.host();
101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  scheme_port = url.scheme() + FILE_PATH_LITERAL("_") + url.port();
102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif
103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return FilePath(host).Append(scheme_port);
105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Returns data directory for given web app url
108c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochFilePath GetWebAppDataDirectory(const FilePath& root_dir,
109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                const GURL& url) {
110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return root_dir.Append(GetWebAppDir(url));
111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Predicator for sorting images from largest to smallest.
114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool IconPrecedes(
115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const webkit_glue::WebApplicationInfo::IconInfo& left,
116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const webkit_glue::WebApplicationInfo::IconInfo& right) {
117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return left.width < right.width;
118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Calculates image checksum using MD5.
121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid GetImageCheckSum(const SkBitmap& image, MD5Digest* digest) {
122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(digest);
123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SkAutoLockPixels image_lock(image);
125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  MD5Sum(image.getPixels(), image.getSize(), digest);
126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#if defined(OS_WIN)
129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Saves |image| as an |icon_file| with the checksum.
130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool SaveIconWithCheckSum(const FilePath& icon_file, const SkBitmap& image) {
131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!IconUtil::CreateIconFileFromSkBitmap(image, icon_file.value()))
132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  MD5Digest digest;
135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GetImageCheckSum(image, &digest);
136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  FilePath cheksum_file(icon_file.ReplaceExtension(kIconChecksumFileExt));
138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return file_util::WriteFile(cheksum_file,
139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                              reinterpret_cast<const char*>(&digest),
140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                              sizeof(digest)) == sizeof(digest);
141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Returns true if |icon_file| is missing or different from |image|.
144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool ShouldUpdateIcon(const FilePath& icon_file, const SkBitmap& image) {
145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  FilePath checksum_file(icon_file.ReplaceExtension(kIconChecksumFileExt));
146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Returns true if icon_file or checksum file is missing.
148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!file_util::PathExists(icon_file) ||
149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      !file_util::PathExists(checksum_file))
150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return true;
151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  MD5Digest persisted_image_checksum;
153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (sizeof(persisted_image_checksum) != file_util::ReadFile(checksum_file,
154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                      reinterpret_cast<char*>(&persisted_image_checksum),
155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                      sizeof(persisted_image_checksum)))
156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return true;
157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  MD5Digest downloaded_image_checksum;
159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GetImageCheckSum(image, &downloaded_image_checksum);
160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Update icon if checksums are not equal.
162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return memcmp(&persisted_image_checksum, &downloaded_image_checksum,
163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                sizeof(MD5Digest)) != 0;
164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Saves |image| to |icon_file| if the file is outdated and refresh shell's
167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// icon cache to ensure correct icon is displayed. Returns true if icon_file
168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// is up to date or successfully updated.
169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool CheckAndSaveIcon(const FilePath& icon_file, const SkBitmap& image) {
170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (ShouldUpdateIcon(icon_file, image)) {
171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (SaveIconWithCheckSum(icon_file, image)) {
172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Refresh shell's icon cache. This call is quite disruptive as user would
173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // see explorer rebuilding the icon cache. It would be great that we find
174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // a better way to achieve this.
175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST | SHCNF_FLUSHNOWAIT,
176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                     NULL, NULL);
177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else {
178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return false;
179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;
183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif  // defined(OS_WIN)
186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Represents a task that creates web application shortcut. This runs on
188c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// file thread and schedules the callback (if any) on the calling thread
189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// when finished (either success or failure).
190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochclass CreateShortcutTask : public Task {
191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public:
192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CreateShortcutTask(const FilePath& profile_path,
193c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                     const ShellIntegration::ShortcutInfo& shortcut_info,
194c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                     web_app::CreateShortcutCallback* callback);
195c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
196c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch private:
197c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  class CreateShortcutCallbackTask : public Task {
198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   public:
199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    CreateShortcutCallbackTask(web_app::CreateShortcutCallback* callback,
200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        bool success)
201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        : callback_(callback),
202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          success_(success) {
203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Overridden from Task:
206c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    virtual void Run() {
207c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      callback_->Run(success_);
208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
209c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
210c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch   private:
211c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    web_app::CreateShortcutCallback* callback_;
212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    bool success_;
213c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  };
214c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Overridden from Task:
216c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual void Run();
217c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
218c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Returns true if shortcut is created successfully.
219c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bool CreateShortcut();
220c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
221c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Path to store persisted data for web app.
222c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  FilePath web_app_path_;
223c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
224c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Out copy of profile path.
225c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  FilePath profile_path_;
226c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
227c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Our copy of short cut data.
228c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ShellIntegration::ShortcutInfo shortcut_info_;
229c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
230c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Callback when task is finished.
231c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  web_app::CreateShortcutCallback* callback_;
232c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  MessageLoop* message_loop_;
233c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
234c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DISALLOW_COPY_AND_ASSIGN(CreateShortcutTask);
235c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch};
236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
237c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochCreateShortcutTask::CreateShortcutTask(
238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const FilePath& profile_path,
239c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const ShellIntegration::ShortcutInfo& shortcut_info,
240c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    web_app::CreateShortcutCallback* callback)
241c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    : web_app_path_(GetWebAppDataDirectory(web_app::GetDataDir(profile_path),
242c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                           shortcut_info.url)),
243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      profile_path_(profile_path),
244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      shortcut_info_(shortcut_info),
245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      callback_(callback),
246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      message_loop_(MessageLoop::current()) {
247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(message_loop_ != NULL);
248c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
249c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CreateShortcutTask::Run() {
251c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bool success = CreateShortcut();
252c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
253c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (callback_ != NULL)
254c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    message_loop_->PostTask(FROM_HERE,
255c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      new CreateShortcutCallbackTask(callback_, success));
256c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
257c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool CreateShortcutTask::CreateShortcut() {
259c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
260c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#if defined(OS_LINUX)
262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  scoped_ptr<base::EnvVarGetter> env_getter(base::EnvVarGetter::Create());
263c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::string shortcut_template;
265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!ShellIntegration::GetDesktopShortcutTemplate(env_getter.get(),
266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                                    &shortcut_template)) {
267c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
268c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
269c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ShellIntegration::CreateDesktopShortcut(shortcut_info_, shortcut_template);
270c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;  // assuming always success.
271c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#elif defined(OS_WIN)
272c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Shortcut paths under which to create shortcuts.
273c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::vector<FilePath> shortcut_paths;
274c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
275c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Locations to add to shortcut_paths.
276c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  struct {
277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const bool& use_this_location;
278c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int location_id;
279c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const wchar_t* sub_dir;
280c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } locations[] = {
281c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    {
282c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      shortcut_info_.create_on_desktop,
283c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      chrome::DIR_USER_DESKTOP,
284c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NULL
285c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }, {
286c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      shortcut_info_.create_in_applications_menu,
287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      base::DIR_START_MENU,
288c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NULL
289c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }, {
290c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      shortcut_info_.create_in_quick_launch_bar,
291c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // For Win7, create_in_quick_launch_bar means pinning to taskbar. Use
292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // base::PATH_START as a flag for this case.
293c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      (win_util::GetWinVersion() >= win_util::WINVERSION_WIN7) ?
294c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          base::PATH_START : base::DIR_APP_DATA,
295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      (win_util::GetWinVersion() >= win_util::WINVERSION_WIN7) ?
296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          NULL : L"Microsoft\\Internet Explorer\\Quick Launch"
297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
298c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  };
299c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
300c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Populate shortcut_paths.
301c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (int i = 0; i < arraysize(locations); ++i) {
302c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (locations[i].use_this_location) {
303c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      FilePath path;
304c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
305c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Skip the Win7 case.
306c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (locations[i].location_id == base::PATH_START)
307c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        continue;
308c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
309c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (!PathService::Get(locations[i].location_id, &path)) {
310c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        NOTREACHED();
311c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        return false;
312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
313c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
314c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (locations[i].sub_dir != NULL)
315c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        path = path.Append(locations[i].sub_dir);
316c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
317c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      shortcut_paths.push_back(path);
318c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
319c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
320c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
321c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bool pin_to_taskbar =
322c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      shortcut_info_.create_in_quick_launch_bar &&
323c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      (win_util::GetWinVersion() >= win_util::WINVERSION_WIN7);
324c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
325c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // For Win7's pinning support, any shortcut could be used. So we only create
326c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // the shortcut file when there is no shortcut file will be created. That is,
327c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // user only selects "Pin to taskbar".
328c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (pin_to_taskbar && shortcut_paths.empty()) {
329c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Creates the shortcut in web_app_path_ in this case.
330c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    shortcut_paths.push_back(web_app_path_);
331c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
332c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
333c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (shortcut_paths.empty()) {
334c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    NOTREACHED();
335c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
336c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
337c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
338c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Ensure web_app_path_ exists.
339c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!file_util::PathExists(web_app_path_) &&
340c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      !file_util::CreateDirectory(web_app_path_)) {
341c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    NOTREACHED();
342c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
343c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
344c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
345c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Generates file name to use with persisted ico and shortcut file.
346c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  FilePath file_name = GetSanitizedFileName(shortcut_info_.title);
347c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
348c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Creates an ico file to use with shortcut.
349c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  FilePath icon_file = web_app_path_.Append(file_name).ReplaceExtension(
350c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      FILE_PATH_LITERAL(".ico"));
351c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!CheckAndSaveIcon(icon_file, shortcut_info_.favicon)) {
352c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    NOTREACHED();
353c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
354c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
355c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
356c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  FilePath chrome_exe;
357c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
358c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    NOTREACHED();
359c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
360c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
361c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
362c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Working directory.
363c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  FilePath chrome_folder = chrome_exe.DirName();
364c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
365c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::string switches =
366c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     ShellIntegration::GetCommandLineArgumentsCommon(shortcut_info_.url,
367c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch     shortcut_info_.extension_id);
368c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::wstring wide_switchs(UTF8ToWide(switches));
369c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
370c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Sanitize description
371c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (shortcut_info_.description.length() >= MAX_PATH)
372c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    shortcut_info_.description.resize(MAX_PATH - 1);
373c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
374c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Generates app id from web app url and profile path.
375c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::wstring app_id = ShellIntegration::GetAppId(
376c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      web_app::GenerateApplicationNameFromURL(shortcut_info_.url),
377c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      profile_path_);
378c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
379c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  FilePath shortcut_to_pin;
380c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
381c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bool success = true;
382c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (size_t i = 0; i < shortcut_paths.size(); ++i) {
383c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    FilePath shortcut_file = shortcut_paths[i].Append(file_name).
384c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        ReplaceExtension(FILE_PATH_LITERAL(".lnk"));
385c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
386c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int unique_number = download_util::GetUniquePathNumber(shortcut_file);
387c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (unique_number == -1) {
388c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      success = false;
389c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      continue;
390c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else if (unique_number > 0) {
391c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      download_util::AppendNumberToPath(&shortcut_file, unique_number);
392c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
393c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
394c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    success &= file_util::CreateShortcutLink(chrome_exe.value().c_str(),
395c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        shortcut_file.value().c_str(),
396c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        chrome_folder.value().c_str(),
397c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        wide_switchs.c_str(),
398c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        shortcut_info_.description.c_str(),
399c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        icon_file.value().c_str(),
400c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        0,
401c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        app_id.c_str());
402c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
403c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Any shortcut would work for the pinning. We use the first one.
404c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (success && pin_to_taskbar && shortcut_to_pin.empty())
405c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      shortcut_to_pin = shortcut_file;
406c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
407c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
408c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (success && pin_to_taskbar) {
409c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!shortcut_to_pin.empty()) {
410c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      success &= file_util::TaskbarPinShortcutLink(
411c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          shortcut_to_pin.value().c_str());
412c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else {
413c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NOTREACHED();
414c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      success = false;
415c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
416c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
417c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
418c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return success;
419c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#else
420c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  NOTIMPLEMENTED();
421c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return false;
422c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif
423c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
424c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
425c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#if defined(OS_WIN)
426c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// UpdateShortcutWorker holds all context data needed for update shortcut.
427c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// It schedules a pre-update check to find all shortcuts that needs to be
428c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// updated. If there are such shortcuts, it schedules icon download and
429c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// update them when icons are downloaded. It observes TAB_CLOSING notification
430c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// and cancels all the work when the underlying tab is closing.
431c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochclass UpdateShortcutWorker : public NotificationObserver {
432c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public:
433c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  explicit UpdateShortcutWorker(TabContents* tab_contents);
434c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
435c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void Run();
436c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
437c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch private:
438c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Overridden from NotificationObserver:
439c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual void Observe(NotificationType type,
440c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                       const NotificationSource& source,
441c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                       const NotificationDetails& details);
442c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
443c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Downloads icon via TabContents.
444c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void DownloadIcon();
445c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
446c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Callback when icon downloaded.
447c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void OnIconDownloaded(int download_id, bool errored, const SkBitmap& image);
448c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
449c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Checks if shortcuts exists on desktop, start menu and quick launch.
450c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void CheckExistingShortcuts();
451c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
452c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Update shortcut files and icons.
453c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void UpdateShortcuts();
454c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void UpdateShortcutsOnFileThread();
455c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
456c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Callback after shortcuts are updated.
457c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void OnShortcutsUpdated(bool);
458c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
459c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Deletes the worker on UI thread where it gets created.
460c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void DeleteMe();
461c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void DeleteMeOnUIThread();
462c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
463c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  NotificationRegistrar registrar_;
464c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
465c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Underlying TabContents whose shortcuts will be updated.
466c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  TabContents* tab_contents_;
467c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
468c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Icons info from tab_contents_'s web app data.
469c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  web_app::IconInfoList unprocessed_icons_;
470c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
471c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Cached shortcut data from the tab_contents_.
472c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ShellIntegration::ShortcutInfo shortcut_info_;
473c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
474c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Our copy of profile path.
475c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  FilePath profile_path_;
476c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
477c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // File name of shortcut/ico file based on app title.
478c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  FilePath file_name_;
479c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
480c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Existing shortcuts.
481c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::vector<FilePath> shortcut_files_;
482c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
483c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DISALLOW_COPY_AND_ASSIGN(UpdateShortcutWorker);
484c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch};
485c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
486c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochUpdateShortcutWorker::UpdateShortcutWorker(TabContents* tab_contents)
487c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    : tab_contents_(tab_contents),
488c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      profile_path_(tab_contents->profile()->GetPath()) {
489c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  web_app::GetShortcutInfoForTab(tab_contents_, &shortcut_info_);
490c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  web_app::GetIconsInfo(tab_contents_->web_app_info(), &unprocessed_icons_);
491c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  file_name_ = GetSanitizedFileName(shortcut_info_.title);
492c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
493c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  registrar_.Add(this, NotificationType::TAB_CLOSING,
494c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                 Source<NavigationController>(&tab_contents_->controller()));
495c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
496c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
497c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid UpdateShortcutWorker::Run() {
498c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Starting by downloading app icon.
499c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DownloadIcon();
500c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
501c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
502c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid UpdateShortcutWorker::Observe(NotificationType type,
503c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                   const NotificationSource& source,
504c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                   const NotificationDetails& details) {
505c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (type == NotificationType::TAB_CLOSING &&
506c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      Source<NavigationController>(source).ptr() ==
507c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        &tab_contents_->controller()) {
508c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Underlying tab is closing.
509c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tab_contents_ = NULL;
510c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
511c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
512c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
513c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid UpdateShortcutWorker::DownloadIcon() {
514c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // FetchIcon must run on UI thread because it relies on TabContents
515c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // to download the icon.
516c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
517c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
518c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (tab_contents_ == NULL) {
519c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    DeleteMe();  // We are done if underlying TabContents is gone.
520c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
521c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
522c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
523c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (unprocessed_icons_.empty()) {
524c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // No app icon. Just use the favicon from TabContents.
525c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    UpdateShortcuts();
526c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
527c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
528c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
529c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  tab_contents_->fav_icon_helper().DownloadImage(
530c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      unprocessed_icons_.back().url,
531c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      std::max(unprocessed_icons_.back().width,
532c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch               unprocessed_icons_.back().height),
533c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NewCallback(this, &UpdateShortcutWorker::OnIconDownloaded));
534c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  unprocessed_icons_.pop_back();
535c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
536c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
537c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid UpdateShortcutWorker::OnIconDownloaded(int download_id,
538c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                            bool errored,
539c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                            const SkBitmap& image) {
540c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (tab_contents_ == NULL) {
541c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    DeleteMe();  // We are done if underlying TabContents is gone.
542c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
543c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
544c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
545c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!errored && !image.isNull()) {
546c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Update icon with download image and update shortcut.
547c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    shortcut_info_.favicon = image;
548c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tab_contents_->SetAppIcon(image);
549c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    UpdateShortcuts();
550c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
551c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Try the next icon otherwise.
552c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    DownloadIcon();
553c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
554c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
555c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
556c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid UpdateShortcutWorker::CheckExistingShortcuts() {
557c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
558c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
559c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Locations to check to shortcut_paths.
560c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  struct {
561c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    bool& use_this_location;
562c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int location_id;
563c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const wchar_t* sub_dir;
564c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } locations[] = {
565c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    {
566c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      shortcut_info_.create_on_desktop,
567c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      chrome::DIR_USER_DESKTOP,
568c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NULL
569c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }, {
570c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      shortcut_info_.create_in_applications_menu,
571c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      base::DIR_START_MENU,
572c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NULL
573c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }, {
574c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      shortcut_info_.create_in_quick_launch_bar,
575c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // For Win7, create_in_quick_launch_bar means pinning to taskbar.
576c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      base::DIR_APP_DATA,
577c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      (win_util::GetWinVersion() >= win_util::WINVERSION_WIN7) ?
578c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          L"Microsoft\\Internet Explorer\\Quick Launch\\User Pinned\\TaskBar" :
579c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          L"Microsoft\\Internet Explorer\\Quick Launch"
580c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
581c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  };
582c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
583c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (int i = 0; i < arraysize(locations); ++i) {
584c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    locations[i].use_this_location = false;
585c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
586c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    FilePath path;
587c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!PathService::Get(locations[i].location_id, &path)) {
588c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NOTREACHED();
589c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      continue;
590c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
591c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
592c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (locations[i].sub_dir != NULL)
593c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      path = path.Append(locations[i].sub_dir);
594c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
595c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    FilePath shortcut_file = path.Append(file_name_).
596c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        ReplaceExtension(FILE_PATH_LITERAL(".lnk"));
597c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (file_util::PathExists(shortcut_file)) {
598c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      locations[i].use_this_location = true;
599c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      shortcut_files_.push_back(shortcut_file);
600c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
601c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
602c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
603c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
604c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid UpdateShortcutWorker::UpdateShortcuts() {
605c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ChromeThread::PostTask(ChromeThread::FILE, FROM_HERE,
606c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NewRunnableMethod(this,
607c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      &UpdateShortcutWorker::UpdateShortcutsOnFileThread));
608c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
609c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
610c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid UpdateShortcutWorker::UpdateShortcutsOnFileThread() {
611c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
612c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
613c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  FilePath web_app_path = GetWebAppDataDirectory(
614c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      web_app::GetDataDir(profile_path_), shortcut_info_.url);
615c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
616c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Ensure web_app_path exists. web_app_path could be missing for a legacy
617c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // shortcut created by gears.
618c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!file_util::PathExists(web_app_path) &&
619c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      !file_util::CreateDirectory(web_app_path)) {
620c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    NOTREACHED();
621c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
622c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
623c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
624c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  FilePath icon_file = web_app_path.Append(file_name_).ReplaceExtension(
625c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      FILE_PATH_LITERAL(".ico"));
626c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CheckAndSaveIcon(icon_file, shortcut_info_.favicon);
627c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
628c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Update existing shortcuts' description, icon and app id.
629c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CheckExistingShortcuts();
630c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!shortcut_files_.empty()) {
631c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Generates app id from web app url and profile path.
632c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    std::wstring app_id = ShellIntegration::GetAppId(
633c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        web_app::GenerateApplicationNameFromURL(shortcut_info_.url),
634c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        profile_path_);
635c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
636c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Sanitize description
637c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (shortcut_info_.description.length() >= MAX_PATH)
638c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      shortcut_info_.description.resize(MAX_PATH - 1);
639c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
640c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    for (size_t i = 0; i < shortcut_files_.size(); ++i) {
641c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      file_util::UpdateShortcutLink(NULL,
642c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          shortcut_files_[i].value().c_str(),
643c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          NULL,
644c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          NULL,
645c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          shortcut_info_.description.c_str(),
646c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          icon_file.value().c_str(),
647c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          0,
648c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          app_id.c_str());
649c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
650c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
651c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
652c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  OnShortcutsUpdated(true);
653c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
654c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
655c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid UpdateShortcutWorker::OnShortcutsUpdated(bool) {
656c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DeleteMe();  // We are done.
657c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
658c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
659c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid UpdateShortcutWorker::DeleteMe() {
660c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (ChromeThread::CurrentlyOn(ChromeThread::UI)) {
661c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    DeleteMeOnUIThread();
662c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
663c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    ChromeThread::PostTask(ChromeThread::UI, FROM_HERE,
664c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NewRunnableMethod(this, &UpdateShortcutWorker::DeleteMeOnUIThread));
665c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
666c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
667c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
668c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid UpdateShortcutWorker::DeleteMeOnUIThread() {
669c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
670c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  delete this;
671c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
672c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif  // defined(OS_WIN)
673c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
674c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch};  // namespace
675c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
676c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#if defined(OS_WIN)
677c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Allows UpdateShortcutWorker without adding refcounting. UpdateShortcutWorker
678c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// manages its own life time and will delete itself when it's done.
679c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochDISABLE_RUNNABLE_METHOD_REFCOUNT(UpdateShortcutWorker);
680c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif  // defined(OS_WIN)
681c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
682c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace web_app {
683c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
684c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstd::wstring GenerateApplicationNameFromURL(const GURL& url) {
685c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::string t;
686c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  t.append(url.host());
687c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  t.append("_");
688c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  t.append(url.path());
689c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return UTF8ToWide(t);
690c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
691c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
692c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CreateShortcut(
693c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const FilePath& data_dir,
694c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const ShellIntegration::ShortcutInfo& shortcut_info,
695c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    CreateShortcutCallback* callback) {
696c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ChromeThread::PostTask(ChromeThread::FILE, FROM_HERE,
697c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      new CreateShortcutTask(data_dir, shortcut_info, callback));
698c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
699c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
700c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool IsValidUrl(const GURL& url) {
701c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  static const char* const kValidUrlSchemes[] = {
702c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      chrome::kFileScheme,
703c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      chrome::kFtpScheme,
704c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      chrome::kHttpScheme,
705c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      chrome::kHttpsScheme,
706c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      chrome::kExtensionScheme,
707c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  };
708c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
709c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (size_t i = 0; i < arraysize(kValidUrlSchemes); ++i) {
710c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (url.SchemeIs(kValidUrlSchemes[i]))
711c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return true;
712c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
713c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
714c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return false;
715c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
716c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
717c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochFilePath GetDataDir(const FilePath& profile_path) {
718c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return profile_path.Append(chrome::kWebAppDirname);
719c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
720c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
721c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#if defined(TOOLKIT_VIEWS)
722c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid GetIconsInfo(const webkit_glue::WebApplicationInfo& app_info,
723c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                  IconInfoList* icons) {
724c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(icons);
725c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
726c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  icons->clear();
727c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (size_t i = 0; i < app_info.icons.size(); ++i) {
728c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // We only take square shaped icons (i.e. width == height).
729c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (app_info.icons[i].width == app_info.icons[i].height) {
730c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      icons->push_back(app_info.icons[i]);
731c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
732c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
733c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
734c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::sort(icons->begin(), icons->end(), &IconPrecedes);
735c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
736c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif
737c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
738c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid GetShortcutInfoForTab(TabContents* tab_contents,
739c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                           ShellIntegration::ShortcutInfo* info) {
740c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(info);  // Must provide a valid info.
741c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
742c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const webkit_glue::WebApplicationInfo& app_info =
743c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      tab_contents->web_app_info();
744c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
745c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  info->url = app_info.app_url.is_empty() ? tab_contents->GetURL() :
746c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                            app_info.app_url;
747c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  info->title = app_info.title.empty() ?
748c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      (tab_contents->GetTitle().empty() ? UTF8ToUTF16(info->url.spec()) :
749c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                          tab_contents->GetTitle()) :
750c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      app_info.title;
751c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  info->description = app_info.description;
752c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  info->favicon = tab_contents->GetFavIcon();
753c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
754c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
755c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid UpdateShortcutForTabContents(TabContents* tab_contents) {
756c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#if defined(OS_WIN)
757c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // UpdateShortcutWorker will delete itself when it's done.
758c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UpdateShortcutWorker* worker = new UpdateShortcutWorker(tab_contents);
759c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  worker->Run();
760c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif  // defined(OS_WIN)
761c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
762c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
763c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch};  // namespace web_app
764