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/chromeos/drive/drive_app_registry.h" 6 7 #include <algorithm> 8 #include <string> 9 #include <utility> 10 #include <vector> 11 12 #include "base/files/file_path.h" 13 #include "base/strings/string_util.h" 14 #include "base/strings/utf_string_conversions.h" 15 #include "chrome/browser/chromeos/drive/file_system_util.h" 16 #include "chrome/browser/chromeos/drive/job_scheduler.h" 17 #include "chrome/browser/google_apis/drive_api_parser.h" 18 #include "content/public/browser/browser_thread.h" 19 20 using content::BrowserThread; 21 22 namespace drive { 23 24 namespace { 25 26 // Webstore URL prefix. 27 const char kStoreProductUrl[] = "https://chrome.google.com/webstore/"; 28 29 // Extracts Web store id from its web store URL. 30 std::string GetWebStoreIdFromUrl(const GURL& url) { 31 if (!StartsWithASCII(url.spec(), kStoreProductUrl, false)) { 32 LOG(WARNING) << "Unrecognized product URL " << url.spec(); 33 return std::string(); 34 } 35 36 base::FilePath path(url.path()); 37 std::vector<base::FilePath::StringType> components; 38 path.GetComponents(&components); 39 DCHECK_LE(2U, components.size()); // Coming from kStoreProductUrl 40 41 // Return the last part of the path 42 return components[components.size() - 1]; 43 } 44 45 bool SortBySize(const google_apis::InstalledApp::IconList::value_type& a, 46 const google_apis::InstalledApp::IconList::value_type& b) { 47 return a.first < b.first; 48 } 49 50 } // namespace 51 52 // DriveAppInfo struct implementation. 53 54 DriveAppInfo::DriveAppInfo( 55 const std::string& app_id, 56 const google_apis::InstalledApp::IconList& app_icons, 57 const google_apis::InstalledApp::IconList& document_icons, 58 const std::string& web_store_id, 59 const string16& app_name, 60 const string16& object_type, 61 bool is_primary_selector) 62 : app_id(app_id), 63 app_icons(app_icons), 64 document_icons(document_icons), 65 web_store_id(web_store_id), 66 app_name(app_name), 67 object_type(object_type), 68 is_primary_selector(is_primary_selector) { 69 } 70 71 DriveAppInfo::~DriveAppInfo() { 72 } 73 74 // FileSystem::DriveAppFileSelector struct implementation. 75 76 DriveAppRegistry::DriveAppFileSelector::DriveAppFileSelector( 77 const GURL& product_link, 78 const google_apis::InstalledApp::IconList& app_icons, 79 const google_apis::InstalledApp::IconList& document_icons, 80 const string16& object_type, 81 const std::string& app_id, 82 bool is_primary_selector) 83 : product_link(product_link), 84 app_icons(app_icons), 85 document_icons(document_icons), 86 object_type(object_type), 87 app_id(app_id), 88 is_primary_selector(is_primary_selector) { 89 } 90 91 DriveAppRegistry::DriveAppFileSelector::~DriveAppFileSelector() { 92 } 93 94 // DriveAppRegistry implementation. 95 96 DriveAppRegistry::DriveAppRegistry(JobScheduler* scheduler) 97 : scheduler_(scheduler), 98 is_updating_(false), 99 weak_ptr_factory_(this) { 100 } 101 102 DriveAppRegistry::~DriveAppRegistry() { 103 STLDeleteValues(&app_extension_map_); 104 STLDeleteValues(&app_mimetypes_map_); 105 } 106 107 void DriveAppRegistry::GetAppsForFile( 108 const base::FilePath& file_path, 109 const std::string& mime_type, 110 ScopedVector<DriveAppInfo>* apps) { 111 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 112 113 SelectorAppList result_map; 114 if (!file_path.empty()) { 115 base::FilePath::StringType extension = file_path.Extension(); 116 if (extension.size() < 2) 117 return; 118 119 extension = extension.substr(1); 120 if (!extension.empty()) 121 FindAppsForSelector(extension, app_extension_map_, &result_map); 122 } 123 124 if (!mime_type.empty()) 125 FindAppsForSelector(mime_type, app_mimetypes_map_, &result_map); 126 127 // Insert found web apps into |apps|, but skip duplicate results. 128 std::set<std::string> inserted_app_ids; 129 for (SelectorAppList::const_iterator it = result_map.begin(); 130 it != result_map.end(); ++it) { 131 if (inserted_app_ids.find(it->second->app_id) == inserted_app_ids.end()) { 132 inserted_app_ids.insert(it->second->app_id); 133 apps->push_back(it->second); 134 } 135 } 136 } 137 138 void DriveAppRegistry::Update() { 139 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 140 141 if (is_updating_) // There is already an update in progress. 142 return; 143 144 is_updating_ = true; 145 146 scheduler_->GetAppList( 147 base::Bind(&DriveAppRegistry::UpdateAfterGetAppList, 148 weak_ptr_factory_.GetWeakPtr())); 149 } 150 151 void DriveAppRegistry::UpdateAfterGetAppList( 152 google_apis::GDataErrorCode gdata_error, 153 scoped_ptr<google_apis::AppList> app_list) { 154 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 155 156 DCHECK(is_updating_); 157 is_updating_ = false; 158 159 FileError error = GDataToFileError(gdata_error); 160 if (error != FILE_ERROR_OK) { 161 // Failed to fetch the data from the server. We can do nothing here. 162 return; 163 } 164 165 DCHECK(app_list); 166 167 url_to_name_map_.clear(); 168 STLDeleteValues(&app_extension_map_); 169 STLDeleteValues(&app_mimetypes_map_); 170 for (size_t i = 0; i < app_list->items().size(); ++i) { 171 const google_apis::AppResource& app = *app_list->items()[i]; 172 if (app.product_url().is_empty()) 173 continue; 174 175 google_apis::InstalledApp::IconList app_icons; 176 google_apis::InstalledApp::IconList document_icons; 177 for (size_t j = 0; j < app.icons().size(); ++j) { 178 const google_apis::DriveAppIcon& icon = *app.icons()[j]; 179 if (icon.icon_url().is_empty()) 180 continue; 181 if (icon.category() == google_apis::DriveAppIcon::APPLICATION) 182 app_icons.push_back(std::make_pair(icon.icon_side_length(), 183 icon.icon_url())); 184 if (icon.category() == google_apis::DriveAppIcon::DOCUMENT) 185 document_icons.push_back(std::make_pair(icon.icon_side_length(), 186 icon.icon_url())); 187 } 188 std::sort(app_icons.begin(), app_icons.end(), SortBySize); 189 std::sort(document_icons.begin(), document_icons.end(), SortBySize); 190 191 url_to_name_map_.insert( 192 std::make_pair(app.product_url(), app.name())); 193 AddAppSelectorList(app.product_url(), 194 app_icons, 195 document_icons, 196 app.object_type(), 197 app.application_id(), 198 true, // primary 199 app.primary_mimetypes(), 200 &app_mimetypes_map_); 201 AddAppSelectorList(app.product_url(), 202 app_icons, 203 document_icons, 204 app.object_type(), 205 app.application_id(), 206 false, // primary 207 app.secondary_mimetypes(), 208 &app_mimetypes_map_); 209 AddAppSelectorList(app.product_url(), 210 app_icons, 211 document_icons, 212 app.object_type(), 213 app.application_id(), 214 true, // primary 215 app.primary_file_extensions(), 216 &app_extension_map_); 217 AddAppSelectorList(app.product_url(), 218 app_icons, 219 document_icons, 220 app.object_type(), 221 app.application_id(), 222 false, // primary 223 app.secondary_file_extensions(), 224 &app_extension_map_); 225 } 226 } 227 228 // static. 229 void DriveAppRegistry::AddAppSelectorList( 230 const GURL& product_link, 231 const google_apis::InstalledApp::IconList& app_icons, 232 const google_apis::InstalledApp::IconList& document_icons, 233 const std::string& object_type, 234 const std::string& app_id, 235 bool is_primary_selector, 236 const ScopedVector<std::string>& selectors, 237 DriveAppFileSelectorMap* map) { 238 for (ScopedVector<std::string>::const_iterator it = selectors.begin(); 239 it != selectors.end(); ++it) { 240 std::string* value = *it; 241 map->insert(std::make_pair( 242 *value, new DriveAppFileSelector(product_link, 243 app_icons, 244 document_icons, 245 UTF8ToUTF16(object_type), 246 app_id, 247 is_primary_selector))); 248 } 249 } 250 251 void DriveAppRegistry::FindAppsForSelector( 252 const std::string& file_selector, 253 const DriveAppFileSelectorMap& map, 254 SelectorAppList* apps) { 255 for (DriveAppFileSelectorMap::const_iterator it = map.find(file_selector); 256 it != map.end() && it->first == file_selector; ++it) { 257 const DriveAppFileSelector* web_app = it->second; 258 std::map<GURL, std::string>::const_iterator product_iter = 259 url_to_name_map_.find(web_app->product_link); 260 if (product_iter == url_to_name_map_.end()) { 261 NOTREACHED(); 262 continue; 263 } 264 265 std::string web_store_id = GetWebStoreIdFromUrl(web_app->product_link); 266 if (web_store_id.empty()) 267 continue; 268 269 if (apps->find(web_app) != apps->end()) 270 continue; 271 272 apps->insert(std::make_pair( 273 web_app, 274 new DriveAppInfo(web_app->app_id, 275 web_app->app_icons, 276 web_app->document_icons, 277 web_store_id, 278 UTF8ToUTF16(product_iter->second), // app name. 279 web_app->object_type, 280 web_app->is_primary_selector))); 281 } 282 } 283 284 } // namespace drive 285