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"
10 #include "base/bind_helpers.h"
11 #include "base/logging.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/stl_util.h"
14 #include "chrome/browser/apps/drive/drive_app_converter.h"
15 #include "chrome/browser/apps/drive/drive_app_mapping.h"
16 #include "chrome/browser/apps/drive/drive_app_uninstall_sync_service.h"
17 #include "chrome/browser/apps/drive/drive_service_bridge.h"
18 #include "chrome/browser/drive/drive_app_registry.h"
19 #include "chrome/browser/extensions/extension_service.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
22 #include "extensions/browser/extension_registry.h"
23 #include "extensions/browser/extension_registry_factory.h"
24 #include "extensions/browser/extension_system.h"
25 #include "extensions/browser/uninstall_reason.h"
26 #include "extensions/common/extension.h"
28 using extensions::Extension
;
29 using extensions::ExtensionRegistry
;
33 void IgnoreUninstallResult(google_apis::GDataErrorCode
) {
38 DriveAppProvider::DriveAppProvider(
40 DriveAppUninstallSyncService
* uninstall_sync_service
)
42 uninstall_sync_service_(uninstall_sync_service
),
43 service_bridge_(DriveServiceBridge::Create(profile
).Pass()),
44 mapping_(new DriveAppMapping(profile
->GetPrefs())),
45 weak_ptr_factory_(this) {
46 service_bridge_
->GetAppRegistry()->AddObserver(this);
47 ExtensionRegistry::Get(profile_
)->AddObserver(this);
50 DriveAppProvider::~DriveAppProvider() {
51 ExtensionRegistry::Get(profile_
)->RemoveObserver(this);
52 service_bridge_
->GetAppRegistry()->RemoveObserver(this);
56 void DriveAppProvider::AppendDependsOnFactories(
57 std::set
<BrowserContextKeyedServiceFactory
*>* factories
) {
58 factories
->insert(extensions::ExtensionRegistryFactory::GetInstance());
59 DriveServiceBridge::AppendDependsOnFactories(factories
);
62 void DriveAppProvider::SetDriveServiceBridgeForTest(
63 scoped_ptr
<DriveServiceBridge
> test_bridge
) {
64 service_bridge_
->GetAppRegistry()->RemoveObserver(this);
65 service_bridge_
= test_bridge
.Pass();
66 service_bridge_
->GetAppRegistry()->AddObserver(this);
69 void DriveAppProvider::AddUninstalledDriveAppFromSync(
70 const std::string
& drive_app_id
) {
71 mapping_
->AddUninstalledDriveApp(drive_app_id
);
75 void DriveAppProvider::RemoveUninstalledDriveAppFromSync(
76 const std::string
& drive_app_id
) {
77 mapping_
->RemoveUninstalledDriveApp(drive_app_id
);
81 void DriveAppProvider::UpdateMappingAndExtensionSystem(
82 const std::string
& drive_app_id
,
83 const Extension
* new_app
,
84 bool is_new_app_generated
) {
85 const std::string
& new_chrome_app_id
= new_app
->id();
87 const std::string existing_chrome_app_id
=
88 mapping_
->GetChromeApp(drive_app_id
);
89 if (existing_chrome_app_id
== new_chrome_app_id
)
92 const bool is_existing_app_generated
=
93 mapping_
->IsChromeAppGenerated(existing_chrome_app_id
);
94 mapping_
->Add(drive_app_id
, new_chrome_app_id
, is_new_app_generated
);
96 const Extension
* existing_app
=
97 ExtensionRegistry::Get(profile_
)->GetExtensionById(
98 existing_chrome_app_id
, ExtensionRegistry::EVERYTHING
);
99 if (existing_app
&& is_existing_app_generated
) {
100 extensions::ExtensionSystem::Get(profile_
)
101 ->extension_service()
102 ->UninstallExtension(existing_chrome_app_id
,
103 extensions::UNINSTALL_REASON_SYNC
,
104 base::Bind(&base::DoNothing
),
109 void DriveAppProvider::ProcessDeferredOnExtensionInstalled(
110 const std::string drive_app_id
,
111 const std::string chrome_app_id
) {
112 const Extension
* app
= ExtensionRegistry::Get(profile_
)->GetExtensionById(
113 chrome_app_id
, ExtensionRegistry::EVERYTHING
);
117 // Remove uninstall tracking for user installed app.
118 mapping_
->RemoveUninstalledDriveApp(drive_app_id
);
119 uninstall_sync_service_
->UntrackUninstalledDriveApp(drive_app_id
);
121 UpdateMappingAndExtensionSystem(drive_app_id
, app
, false);
124 void DriveAppProvider::SchedulePendingConverters() {
125 if (pending_converters_
.empty())
128 if (!pending_converters_
.front()->IsStarted())
129 pending_converters_
.front()->Start();
132 void DriveAppProvider::OnLocalAppConverted(const DriveAppConverter
* converter
,
134 DCHECK_EQ(pending_converters_
.front(), converter
);
137 const bool was_generated
=
138 mapping_
->GetDriveApp(converter
->extension()->id()) ==
139 converter
->drive_app_info().app_id
&&
140 mapping_
->IsChromeAppGenerated(converter
->extension()->id());
141 UpdateMappingAndExtensionSystem(
142 converter
->drive_app_info().app_id
,
143 converter
->extension(),
144 converter
->is_new_install() || was_generated
);
146 LOG(WARNING
) << "Failed to convert drive app to web app, "
147 << "drive app id= " << converter
->drive_app_info().app_id
148 << ", name=" << converter
->drive_app_info().app_name
;
151 pending_converters_
.erase(pending_converters_
.begin());
152 SchedulePendingConverters();
155 bool DriveAppProvider::IsMappedUrlAppUpToDate(
156 const drive::DriveAppInfo
& drive_app
) const {
157 const std::string
& url_app_id
= mapping_
->GetChromeApp(drive_app
.app_id
);
158 if (url_app_id
.empty())
161 const Extension
* url_app
= ExtensionRegistry::Get(profile_
)->GetExtensionById(
162 url_app_id
, ExtensionRegistry::EVERYTHING
);
165 DCHECK(url_app
->is_hosted_app() && url_app
->from_bookmark());
167 return drive_app
.app_name
== url_app
->name() &&
168 drive_app
.create_url
==
169 extensions::AppLaunchInfo::GetLaunchWebURL(url_app
);
172 void DriveAppProvider::AddOrUpdateDriveApp(
173 const drive::DriveAppInfo
& drive_app
) {
174 const Extension
* chrome_app
=
175 ExtensionRegistry::Get(profile_
)->GetExtensionById(
176 drive_app
.product_id
, ExtensionRegistry::EVERYTHING
);
178 UpdateMappingAndExtensionSystem(drive_app
.app_id
, chrome_app
, false);
182 if (IsMappedUrlAppUpToDate(drive_app
))
185 ScopedVector
<DriveAppConverter
>::iterator it
= pending_converters_
.begin();
186 while (it
!= pending_converters_
.end()) {
187 if (!(*it
)->IsStarted() &&
188 (*it
)->drive_app_info().app_id
== drive_app
.app_id
) {
189 it
= pending_converters_
.erase(it
);
195 pending_converters_
.push_back(
196 new DriveAppConverter(profile_
,
198 base::Bind(&DriveAppProvider::OnLocalAppConverted
,
199 base::Unretained(this))));
202 void DriveAppProvider::ProcessRemovedDriveApp(const std::string
& drive_app_id
) {
203 const std::string chrome_app_id
= mapping_
->GetChromeApp(drive_app_id
);
204 const bool is_generated
= mapping_
->IsChromeAppGenerated(chrome_app_id
);
205 mapping_
->Remove(drive_app_id
);
207 if (chrome_app_id
.empty() || !is_generated
)
210 const Extension
* existing_app
=
211 ExtensionRegistry::Get(profile_
)
212 ->GetExtensionById(chrome_app_id
, ExtensionRegistry::EVERYTHING
);
216 extensions::ExtensionSystem::Get(profile_
)
217 ->extension_service()
218 ->UninstallExtension(chrome_app_id
,
219 extensions::UNINSTALL_REASON_SYNC
,
220 base::Bind(&base::DoNothing
),
224 void DriveAppProvider::UpdateDriveApps() {
225 service_bridge_
->GetAppRegistry()->GetAppList(&drive_apps_
);
228 for (size_t i
= 0; i
< drive_apps_
.size(); ++i
) {
229 const std::string
& drive_app_id
= drive_apps_
[i
].app_id
;
230 if (!mapping_
->IsUninstalledDriveApp(drive_app_id
))
231 current_ids
.insert(drive_app_id
);
234 const IdSet existing_ids
= mapping_
->GetDriveAppIds();
235 const IdSet ids_to_remove
=
236 base::STLSetDifference
<IdSet
>(existing_ids
, current_ids
);
237 for (IdSet::const_iterator it
= ids_to_remove
.begin();
238 it
!= ids_to_remove
.end();
240 ProcessRemovedDriveApp(*it
);
243 for (size_t i
= 0; i
< drive_apps_
.size(); ++i
) {
244 if (!mapping_
->IsUninstalledDriveApp(drive_apps_
[i
].app_id
))
245 AddOrUpdateDriveApp(drive_apps_
[i
]);
247 SchedulePendingConverters();
250 void DriveAppProvider::OnDriveAppRegistryUpdated() {
254 void DriveAppProvider::OnExtensionInstalled(
255 content::BrowserContext
* browser_context
,
256 const Extension
* extension
,
258 // Bail if the |extension| is installed from a converter. The post install
259 // processing will be handled in OnLocalAppConverted.
260 if (!pending_converters_
.empty() &&
261 pending_converters_
.front()->IsInstalling(extension
->id())) {
265 // Only user installed app reaches here. If it is mapped, make sure it is not
266 // tagged as generated.
267 const std::string drive_app_id
= mapping_
->GetDriveApp(extension
->id());
268 if (!drive_app_id
.empty() &&
269 mapping_
->IsChromeAppGenerated(extension
->id())) {
270 mapping_
->Add(drive_app_id
, extension
->id(), false);
274 for (size_t i
= 0; i
< drive_apps_
.size(); ++i
) {
275 if (drive_apps_
[i
].product_id
== extension
->id()) {
276 // Defer the processing because it touches the extensions system and
277 // it is better to let the current task finish to avoid unexpected
278 // incomplete status.
279 base::MessageLoop::current()->PostTask(
281 base::Bind(&DriveAppProvider::ProcessDeferredOnExtensionInstalled
,
282 weak_ptr_factory_
.GetWeakPtr(),
283 drive_apps_
[i
].app_id
,
290 void DriveAppProvider::OnExtensionUninstalled(
291 content::BrowserContext
* browser_context
,
292 const Extension
* extension
,
293 extensions::UninstallReason reason
) {
294 std::string drive_app_id
= mapping_
->GetDriveApp(extension
->id());
295 if (drive_app_id
.empty())
298 for (size_t i
= 0; i
< drive_apps_
.size(); ++i
) {
299 if (drive_apps_
[i
].app_id
!= drive_app_id
)
302 if (drive_apps_
[i
].is_removable
) {
303 service_bridge_
->GetAppRegistry()->UninstallApp(
304 drive_app_id
, base::Bind(&IgnoreUninstallResult
));
306 mapping_
->AddUninstalledDriveApp(drive_app_id
);
307 uninstall_sync_service_
->TrackUninstalledDriveApp(drive_app_id
);