Home | History | Annotate | Download | only in web_applications
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/web_applications/web_app.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/bind_helpers.h"
      9 #include "base/file_util.h"
     10 #include "base/i18n/file_util_icu.h"
     11 #include "base/strings/string_util.h"
     12 #include "base/strings/utf_string_conversions.h"
     13 #include "base/threading/thread.h"
     14 #include "chrome/common/chrome_constants.h"
     15 #include "chrome/common/chrome_version_info.h"
     16 #include "chrome/common/extensions/extension.h"
     17 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
     18 #include "chrome/common/url_constants.h"
     19 #include "content/public/browser/browser_thread.h"
     20 #include "extensions/common/constants.h"
     21 #include "grit/chromium_strings.h"
     22 #include "ui/base/l10n/l10n_util.h"
     23 
     24 using content::BrowserThread;
     25 
     26 namespace {
     27 
     28 #if defined(TOOLKIT_VIEWS)
     29 // Predicator for sorting images from largest to smallest.
     30 bool IconPrecedes(const WebApplicationInfo::IconInfo& left,
     31                   const WebApplicationInfo::IconInfo& right) {
     32   return left.width < right.width;
     33 }
     34 #endif
     35 
     36 void DeleteShortcutsOnFileThread(
     37     const ShellIntegration::ShortcutInfo& shortcut_info) {
     38   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
     39 
     40   base::FilePath shortcut_data_dir = web_app::GetWebAppDataDirectory(
     41       shortcut_info.profile_path, shortcut_info.extension_id, GURL());
     42   return web_app::internals::DeletePlatformShortcuts(
     43       shortcut_data_dir, shortcut_info);
     44 }
     45 
     46 void UpdateShortcutsOnFileThread(
     47     const string16& old_app_title,
     48     const ShellIntegration::ShortcutInfo& shortcut_info) {
     49   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
     50 
     51   base::FilePath shortcut_data_dir = web_app::GetWebAppDataDirectory(
     52       shortcut_info.profile_path, shortcut_info.extension_id, GURL());
     53   return web_app::internals::UpdatePlatformShortcuts(
     54       shortcut_data_dir, old_app_title, shortcut_info);
     55 }
     56 
     57 }  // namespace
     58 
     59 namespace web_app {
     60 
     61 // The following string is used to build the directory name for
     62 // shortcuts to chrome applications (the kind which are installed
     63 // from a CRX).  Application shortcuts to URLs use the {host}_{path}
     64 // for the name of this directory.  Hosts can't include an underscore.
     65 // By starting this string with an underscore, we ensure that there
     66 // are no naming conflicts.
     67 static const char* kCrxAppPrefix = "_crx_";
     68 
     69 namespace internals {
     70 
     71 base::FilePath GetSanitizedFileName(const string16& name) {
     72 #if defined(OS_WIN)
     73   string16 file_name = name;
     74 #else
     75   std::string file_name = UTF16ToUTF8(name);
     76 #endif
     77   file_util::ReplaceIllegalCharactersInPath(&file_name, '_');
     78   return base::FilePath(file_name);
     79 }
     80 
     81 }  // namespace internals
     82 
     83 base::FilePath GetWebAppDataDirectory(const base::FilePath& profile_path,
     84                                       const std::string& extension_id,
     85                                       const GURL& url) {
     86   DCHECK(!profile_path.empty());
     87   base::FilePath app_data_dir(profile_path.Append(chrome::kWebAppDirname));
     88 
     89   if (!extension_id.empty()) {
     90     return app_data_dir.AppendASCII(
     91         GenerateApplicationNameFromExtensionId(extension_id));
     92   }
     93 
     94   std::string host(url.host());
     95   std::string scheme(url.has_scheme() ? url.scheme() : "http");
     96   std::string port(url.has_port() ? url.port() : "80");
     97   std::string scheme_port(scheme + "_" + port);
     98 
     99 #if defined(OS_WIN)
    100   base::FilePath::StringType host_path(UTF8ToUTF16(host));
    101   base::FilePath::StringType scheme_port_path(UTF8ToUTF16(scheme_port));
    102 #elif defined(OS_POSIX)
    103   base::FilePath::StringType host_path(host);
    104   base::FilePath::StringType scheme_port_path(scheme_port);
    105 #endif
    106 
    107   return app_data_dir.Append(host_path).Append(scheme_port_path);
    108 }
    109 
    110 base::FilePath GetWebAppDataDirectory(const base::FilePath& profile_path,
    111                                       const extensions::Extension& extension) {
    112   return GetWebAppDataDirectory(
    113       profile_path,
    114       extension.id(),
    115       GURL(extensions::AppLaunchInfo::GetLaunchWebURL(&extension)));
    116 }
    117 
    118 std::string GenerateApplicationNameFromInfo(
    119     const ShellIntegration::ShortcutInfo& shortcut_info) {
    120   if (!shortcut_info.extension_id.empty()) {
    121     return web_app::GenerateApplicationNameFromExtensionId(
    122         shortcut_info.extension_id);
    123   } else {
    124     return web_app::GenerateApplicationNameFromURL(
    125         shortcut_info.url);
    126   }
    127 }
    128 
    129 std::string GenerateApplicationNameFromURL(const GURL& url) {
    130   std::string t;
    131   t.append(url.host());
    132   t.append("_");
    133   t.append(url.path());
    134   return t;
    135 }
    136 
    137 std::string GenerateApplicationNameFromExtensionId(const std::string& id) {
    138   std::string t(web_app::kCrxAppPrefix);
    139   t.append(id);
    140   return t;
    141 }
    142 
    143 std::string GetExtensionIdFromApplicationName(const std::string& app_name) {
    144   std::string prefix(kCrxAppPrefix);
    145   if (app_name.substr(0, prefix.length()) != prefix)
    146     return std::string();
    147   return app_name.substr(prefix.length());
    148 }
    149 
    150 void CreateShortcuts(
    151     const ShellIntegration::ShortcutInfo& shortcut_info,
    152     const ShellIntegration::ShortcutLocations& creation_locations,
    153     ShortcutCreationReason creation_reason) {
    154   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    155 
    156   BrowserThread::PostTask(
    157       BrowserThread::FILE,
    158       FROM_HERE,
    159       base::Bind(base::IgnoreResult(&CreateShortcutsOnFileThread),
    160                  shortcut_info, creation_locations, creation_reason));
    161 }
    162 
    163 void DeleteAllShortcuts(const ShellIntegration::ShortcutInfo& shortcut_info) {
    164   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    165 
    166   BrowserThread::PostTask(
    167       BrowserThread::FILE,
    168       FROM_HERE,
    169       base::Bind(&DeleteShortcutsOnFileThread, shortcut_info));
    170 }
    171 
    172 void UpdateAllShortcuts(const string16& old_app_title,
    173                         const ShellIntegration::ShortcutInfo& shortcut_info) {
    174   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    175 
    176   BrowserThread::PostTask(
    177       BrowserThread::FILE,
    178       FROM_HERE,
    179       base::Bind(&UpdateShortcutsOnFileThread, old_app_title, shortcut_info));
    180 }
    181 
    182 bool CreateShortcutsOnFileThread(
    183     const ShellIntegration::ShortcutInfo& shortcut_info,
    184     const ShellIntegration::ShortcutLocations& creation_locations,
    185     ShortcutCreationReason creation_reason) {
    186   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    187 
    188   base::FilePath shortcut_data_dir = GetWebAppDataDirectory(
    189       shortcut_info.profile_path, shortcut_info.extension_id,
    190       shortcut_info.url);
    191   return internals::CreatePlatformShortcuts(shortcut_data_dir, shortcut_info,
    192                                             creation_locations,
    193                                             creation_reason);
    194 }
    195 
    196 bool IsValidUrl(const GURL& url) {
    197   static const char* const kValidUrlSchemes[] = {
    198       chrome::kFileScheme,
    199       chrome::kFileSystemScheme,
    200       chrome::kFtpScheme,
    201       chrome::kHttpScheme,
    202       chrome::kHttpsScheme,
    203       extensions::kExtensionScheme,
    204   };
    205 
    206   for (size_t i = 0; i < arraysize(kValidUrlSchemes); ++i) {
    207     if (url.SchemeIs(kValidUrlSchemes[i]))
    208       return true;
    209   }
    210 
    211   return false;
    212 }
    213 
    214 #if defined(TOOLKIT_VIEWS)
    215 void GetIconsInfo(const WebApplicationInfo& app_info,
    216                   IconInfoList* icons) {
    217   DCHECK(icons);
    218 
    219   icons->clear();
    220   for (size_t i = 0; i < app_info.icons.size(); ++i) {
    221     // We only take square shaped icons (i.e. width == height).
    222     if (app_info.icons[i].width == app_info.icons[i].height) {
    223       icons->push_back(app_info.icons[i]);
    224     }
    225   }
    226 
    227   std::sort(icons->begin(), icons->end(), &IconPrecedes);
    228 }
    229 #endif
    230 
    231 #if defined(TOOLKIT_GTK)
    232 std::string GetWMClassFromAppName(std::string app_name) {
    233   file_util::ReplaceIllegalCharactersInPath(&app_name, '_');
    234   TrimString(app_name, "_", &app_name);
    235   return app_name;
    236 }
    237 #endif
    238 
    239 string16 GetAppShortcutsSubdirName() {
    240   chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
    241   if (channel == chrome::VersionInfo::CHANNEL_CANARY)
    242     return l10n_util::GetStringUTF16(IDS_APP_SHORTCUTS_SUBDIR_NAME_CANARY);
    243   return l10n_util::GetStringUTF16(IDS_APP_SHORTCUTS_SUBDIR_NAME);
    244 }
    245 
    246 }  // namespace web_app
    247