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