Switch global error menu icon to vectorized MD asset
[chromium-blink-merge.git] / chrome / browser / apps / drive / drive_app_provider.cc
blob06f31a19f747788ebf091ee09a7814d276663551
1 // Copyright 2014 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.
5 #include "chrome/browser/apps/drive/drive_app_provider.h"
7 #include <vector>
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/location.h"
12 #include "base/logging.h"
13 #include "base/single_thread_task_runner.h"
14 #include "base/stl_util.h"
15 #include "base/thread_task_runner_handle.h"
16 #include "chrome/browser/apps/drive/drive_app_converter.h"
17 #include "chrome/browser/apps/drive/drive_app_mapping.h"
18 #include "chrome/browser/apps/drive/drive_app_uninstall_sync_service.h"
19 #include "chrome/browser/apps/drive/drive_service_bridge.h"
20 #include "chrome/browser/extensions/extension_service.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
23 #include "components/drive/drive_app_registry.h"
24 #include "extensions/browser/extension_registry.h"
25 #include "extensions/browser/extension_registry_factory.h"
26 #include "extensions/browser/extension_system.h"
27 #include "extensions/browser/uninstall_reason.h"
28 #include "extensions/common/extension.h"
30 using extensions::Extension;
31 using extensions::ExtensionRegistry;
33 namespace {
35 void IgnoreUninstallResult(google_apis::DriveApiErrorCode) {
38 } // namespace
40 DriveAppProvider::DriveAppProvider(
41 Profile* profile,
42 DriveAppUninstallSyncService* uninstall_sync_service)
43 : profile_(profile),
44 uninstall_sync_service_(uninstall_sync_service),
45 service_bridge_(DriveServiceBridge::Create(profile).Pass()),
46 mapping_(new DriveAppMapping(profile->GetPrefs())),
47 drive_app_registry_updated_(false),
48 weak_ptr_factory_(this) {
49 service_bridge_->GetAppRegistry()->AddObserver(this);
50 ExtensionRegistry::Get(profile_)->AddObserver(this);
53 DriveAppProvider::~DriveAppProvider() {
54 ExtensionRegistry::Get(profile_)->RemoveObserver(this);
55 service_bridge_->GetAppRegistry()->RemoveObserver(this);
58 // static
59 void DriveAppProvider::AppendDependsOnFactories(
60 std::set<BrowserContextKeyedServiceFactory*>* factories) {
61 factories->insert(extensions::ExtensionRegistryFactory::GetInstance());
62 DriveServiceBridge::AppendDependsOnFactories(factories);
65 void DriveAppProvider::SetDriveServiceBridgeForTest(
66 scoped_ptr<DriveServiceBridge> test_bridge) {
67 service_bridge_->GetAppRegistry()->RemoveObserver(this);
68 service_bridge_ = test_bridge.Pass();
69 service_bridge_->GetAppRegistry()->AddObserver(this);
72 void DriveAppProvider::AddUninstalledDriveAppFromSync(
73 const std::string& drive_app_id) {
74 mapping_->AddUninstalledDriveApp(drive_app_id);
76 // Decouple the operation because this function could be called during
77 // sync processing and UpdateDriveApps could trigger another sync change.
78 // See http://crbug.com/429205
79 base::ThreadTaskRunnerHandle::Get()->PostTask(
80 FROM_HERE, base::Bind(&DriveAppProvider::UpdateDriveApps,
81 weak_ptr_factory_.GetWeakPtr()));
84 void DriveAppProvider::RemoveUninstalledDriveAppFromSync(
85 const std::string& drive_app_id) {
86 mapping_->RemoveUninstalledDriveApp(drive_app_id);
88 // Decouple the operation because this function could be called during
89 // sync processing and UpdateDriveApps could trigger another sync change.
90 // See http://crbug.com/429205
91 base::ThreadTaskRunnerHandle::Get()->PostTask(
92 FROM_HERE, base::Bind(&DriveAppProvider::UpdateDriveApps,
93 weak_ptr_factory_.GetWeakPtr()));
96 void DriveAppProvider::UpdateMappingAndExtensionSystem(
97 const std::string& drive_app_id,
98 const Extension* new_app,
99 bool is_new_app_generated) {
100 const std::string& new_chrome_app_id = new_app->id();
102 const std::string existing_chrome_app_id =
103 mapping_->GetChromeApp(drive_app_id);
104 if (existing_chrome_app_id == new_chrome_app_id)
105 return;
107 const bool is_existing_app_generated =
108 mapping_->IsChromeAppGenerated(existing_chrome_app_id);
109 mapping_->Add(drive_app_id, new_chrome_app_id, is_new_app_generated);
111 const Extension* existing_app =
112 ExtensionRegistry::Get(profile_)->GetExtensionById(
113 existing_chrome_app_id, ExtensionRegistry::EVERYTHING);
114 if (existing_app && is_existing_app_generated) {
115 extensions::ExtensionSystem::Get(profile_)
116 ->extension_service()
117 ->UninstallExtension(existing_chrome_app_id,
118 extensions::UNINSTALL_REASON_SYNC,
119 base::Bind(&base::DoNothing),
120 NULL);
124 void DriveAppProvider::ProcessDeferredOnExtensionInstalled(
125 const std::string drive_app_id,
126 const std::string chrome_app_id) {
127 const Extension* app = ExtensionRegistry::Get(profile_)->GetExtensionById(
128 chrome_app_id, ExtensionRegistry::EVERYTHING);
129 if (!app)
130 return;
132 // Remove uninstall tracking for user installed app.
133 mapping_->RemoveUninstalledDriveApp(drive_app_id);
134 uninstall_sync_service_->UntrackUninstalledDriveApp(drive_app_id);
136 UpdateMappingAndExtensionSystem(drive_app_id, app, false);
139 void DriveAppProvider::SchedulePendingConverters() {
140 if (pending_converters_.empty())
141 return;
143 if (!pending_converters_.front()->IsStarted())
144 pending_converters_.front()->Start();
147 void DriveAppProvider::OnLocalAppConverted(const DriveAppConverter* converter,
148 bool success) {
149 DCHECK_EQ(pending_converters_.front(), converter);
151 if (success) {
152 const bool was_generated =
153 mapping_->GetDriveApp(converter->extension()->id()) ==
154 converter->drive_app_info().app_id &&
155 mapping_->IsChromeAppGenerated(converter->extension()->id());
156 UpdateMappingAndExtensionSystem(
157 converter->drive_app_info().app_id,
158 converter->extension(),
159 converter->is_new_install() || was_generated);
160 } else {
161 LOG(WARNING) << "Failed to convert drive app to web app, "
162 << "drive app id= " << converter->drive_app_info().app_id
163 << ", name=" << converter->drive_app_info().app_name;
166 pending_converters_.erase(pending_converters_.begin());
167 SchedulePendingConverters();
170 bool DriveAppProvider::IsMappedUrlAppUpToDate(
171 const drive::DriveAppInfo& drive_app) const {
172 const std::string& url_app_id = mapping_->GetChromeApp(drive_app.app_id);
173 if (url_app_id.empty())
174 return false;
176 const Extension* url_app = ExtensionRegistry::Get(profile_)->GetExtensionById(
177 url_app_id, ExtensionRegistry::EVERYTHING);
178 if (!url_app)
179 return false;
180 DCHECK(url_app->is_hosted_app() && url_app->from_bookmark());
182 return drive_app.app_name == url_app->name() &&
183 drive_app.create_url ==
184 extensions::AppLaunchInfo::GetLaunchWebURL(url_app);
187 void DriveAppProvider::AddOrUpdateDriveApp(
188 const drive::DriveAppInfo& drive_app) {
189 const Extension* chrome_app =
190 ExtensionRegistry::Get(profile_)->GetExtensionById(
191 drive_app.product_id, ExtensionRegistry::EVERYTHING);
192 if (chrome_app) {
193 UpdateMappingAndExtensionSystem(drive_app.app_id, chrome_app, false);
194 return;
197 if (IsMappedUrlAppUpToDate(drive_app))
198 return;
200 ScopedVector<DriveAppConverter>::iterator it = pending_converters_.begin();
201 while (it != pending_converters_.end()) {
202 if (!(*it)->IsStarted() &&
203 (*it)->drive_app_info().app_id == drive_app.app_id) {
204 it = pending_converters_.erase(it);
205 } else {
206 ++it;
210 pending_converters_.push_back(
211 new DriveAppConverter(profile_,
212 drive_app,
213 base::Bind(&DriveAppProvider::OnLocalAppConverted,
214 base::Unretained(this))));
217 void DriveAppProvider::ProcessRemovedDriveApp(const std::string& drive_app_id) {
218 const std::string chrome_app_id = mapping_->GetChromeApp(drive_app_id);
219 const bool is_generated = mapping_->IsChromeAppGenerated(chrome_app_id);
220 mapping_->Remove(drive_app_id);
222 if (chrome_app_id.empty() || !is_generated)
223 return;
225 const Extension* existing_app =
226 ExtensionRegistry::Get(profile_)
227 ->GetExtensionById(chrome_app_id, ExtensionRegistry::EVERYTHING);
228 if (!existing_app)
229 return;
231 extensions::ExtensionSystem::Get(profile_)
232 ->extension_service()
233 ->UninstallExtension(chrome_app_id,
234 extensions::UNINSTALL_REASON_SYNC,
235 base::Bind(&base::DoNothing),
236 NULL);
239 void DriveAppProvider::UpdateDriveApps() {
240 if (!drive_app_registry_updated_)
241 return;
243 service_bridge_->GetAppRegistry()->GetAppList(&drive_apps_);
245 IdSet current_ids;
246 for (size_t i = 0; i < drive_apps_.size(); ++i) {
247 const std::string& drive_app_id = drive_apps_[i].app_id;
248 if (!mapping_->IsUninstalledDriveApp(drive_app_id) &&
249 drive_apps_[i].create_url.is_valid()) {
250 current_ids.insert(drive_app_id);
254 const IdSet existing_ids = mapping_->GetDriveAppIds();
255 const IdSet ids_to_remove =
256 base::STLSetDifference<IdSet>(existing_ids, current_ids);
257 for (IdSet::const_iterator it = ids_to_remove.begin();
258 it != ids_to_remove.end();
259 ++it) {
260 ProcessRemovedDriveApp(*it);
263 for (size_t i = 0; i < drive_apps_.size(); ++i) {
264 if (!mapping_->IsUninstalledDriveApp(drive_apps_[i].app_id) &&
265 drive_apps_[i].create_url.is_valid()) {
266 AddOrUpdateDriveApp(drive_apps_[i]);
269 SchedulePendingConverters();
272 void DriveAppProvider::OnDriveAppRegistryUpdated() {
273 drive_app_registry_updated_ = true;
274 UpdateDriveApps();
277 void DriveAppProvider::OnExtensionInstalled(
278 content::BrowserContext* browser_context,
279 const Extension* extension,
280 bool is_update) {
281 // Bail if the |extension| is installed from a converter. The post install
282 // processing will be handled in OnLocalAppConverted.
283 if (!pending_converters_.empty() &&
284 pending_converters_.front()->IsInstalling(extension->id())) {
285 return;
288 // Only user installed app reaches here. If it is mapped, make sure it is not
289 // tagged as generated.
290 const std::string drive_app_id = mapping_->GetDriveApp(extension->id());
291 if (!drive_app_id.empty() &&
292 mapping_->IsChromeAppGenerated(extension->id())) {
293 mapping_->Add(drive_app_id, extension->id(), false);
294 return;
297 for (size_t i = 0; i < drive_apps_.size(); ++i) {
298 if (drive_apps_[i].product_id == extension->id()) {
299 // Defer the processing because it touches the extensions system and
300 // it is better to let the current task finish to avoid unexpected
301 // incomplete status.
302 base::ThreadTaskRunnerHandle::Get()->PostTask(
303 FROM_HERE,
304 base::Bind(&DriveAppProvider::ProcessDeferredOnExtensionInstalled,
305 weak_ptr_factory_.GetWeakPtr(), drive_apps_[i].app_id,
306 extension->id()));
307 return;
312 void DriveAppProvider::OnExtensionUninstalled(
313 content::BrowserContext* browser_context,
314 const Extension* extension,
315 extensions::UninstallReason reason) {
316 std::string drive_app_id = mapping_->GetDriveApp(extension->id());
317 if (drive_app_id.empty())
318 return;
320 for (size_t i = 0; i < drive_apps_.size(); ++i) {
321 if (drive_apps_[i].app_id != drive_app_id)
322 continue;
324 if (drive_apps_[i].is_removable) {
325 service_bridge_->GetAppRegistry()->UninstallApp(
326 drive_app_id, base::Bind(&IgnoreUninstallResult));
327 } else {
328 mapping_->AddUninstalledDriveApp(drive_app_id);
329 uninstall_sync_service_->TrackUninstalledDriveApp(drive_app_id);
332 return;