OLD | NEW |
| (Empty) |
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/extensions/api/management/management_api.h" | |
6 | |
7 #include <string> | |
8 #include <vector> | |
9 | |
10 #include "base/basictypes.h" | |
11 #include "base/bind.h" | |
12 #include "base/json/json_writer.h" | |
13 #include "base/lazy_instance.h" | |
14 #include "base/logging.h" | |
15 #include "base/memory/linked_ptr.h" | |
16 #include "base/memory/scoped_ptr.h" | |
17 #include "base/metrics/histogram.h" | |
18 #include "base/strings/string_number_conversions.h" | |
19 #include "base/strings/string_util.h" | |
20 #include "base/strings/utf_string_conversions.h" | |
21 #include "chrome/browser/extensions/api/management/management_api_constants.h" | |
22 #include "chrome/browser/extensions/extension_service.h" | |
23 #include "chrome/browser/extensions/extension_ui_util.h" | |
24 #include "chrome/browser/extensions/extension_uninstall_dialog.h" | |
25 #include "chrome/browser/extensions/extension_util.h" | |
26 #include "chrome/browser/extensions/launch_util.h" | |
27 #include "chrome/browser/favicon/favicon_service_factory.h" | |
28 #include "chrome/browser/profiles/profile.h" | |
29 #include "chrome/browser/ui/browser_dialogs.h" | |
30 #include "chrome/browser/ui/browser_finder.h" | |
31 #include "chrome/browser/ui/browser_window.h" | |
32 #include "chrome/browser/ui/extensions/application_launch.h" | |
33 #include "chrome/browser/ui/webui/extensions/extension_icon_source.h" | |
34 #include "chrome/browser/ui/webui/ntp/core_app_launcher_handler.h" | |
35 #include "chrome/common/extensions/api/management.h" | |
36 #include "chrome/common/extensions/chrome_utility_extensions_messages.h" | |
37 #include "chrome/common/extensions/extension_constants.h" | |
38 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h" | |
39 #include "content/public/browser/utility_process_host.h" | |
40 #include "content/public/browser/utility_process_host_client.h" | |
41 #include "extensions/browser/event_router.h" | |
42 #include "extensions/browser/extension_prefs.h" | |
43 #include "extensions/browser/extension_registry.h" | |
44 #include "extensions/browser/extension_system.h" | |
45 #include "extensions/browser/management_policy.h" | |
46 #include "extensions/browser/uninstall_reason.h" | |
47 #include "extensions/common/constants.h" | |
48 #include "extensions/common/error_utils.h" | |
49 #include "extensions/common/extension.h" | |
50 #include "extensions/common/extension_icon_set.h" | |
51 #include "extensions/common/manifest_handlers/icons_handler.h" | |
52 #include "extensions/common/manifest_handlers/offline_enabled_info.h" | |
53 #include "extensions/common/manifest_handlers/options_page_info.h" | |
54 #include "extensions/common/manifest_url_handlers.h" | |
55 #include "extensions/common/permissions/permission_set.h" | |
56 #include "extensions/common/permissions/permissions_data.h" | |
57 #include "extensions/common/url_pattern.h" | |
58 | |
59 using base::IntToString; | |
60 using content::BrowserThread; | |
61 using content::UtilityProcessHost; | |
62 using content::UtilityProcessHostClient; | |
63 | |
64 namespace keys = extension_management_api_constants; | |
65 | |
66 namespace extensions { | |
67 | |
68 namespace management = api::management; | |
69 | |
70 namespace { | |
71 | |
72 typedef std::vector<linked_ptr<management::ExtensionInfo> > ExtensionInfoList; | |
73 typedef std::vector<linked_ptr<management::IconInfo> > IconInfoList; | |
74 | |
75 enum AutoConfirmForTest { | |
76 DO_NOT_SKIP = 0, | |
77 PROCEED, | |
78 ABORT | |
79 }; | |
80 | |
81 AutoConfirmForTest auto_confirm_for_test = DO_NOT_SKIP; | |
82 | |
83 std::vector<std::string> CreateWarningsList(const Extension* extension) { | |
84 std::vector<std::string> warnings_list; | |
85 PermissionMessages warnings = | |
86 extension->permissions_data()->GetPermissionMessages(); | |
87 for (PermissionMessages::const_iterator iter = warnings.begin(); | |
88 iter != warnings.end(); ++iter) { | |
89 warnings_list.push_back(base::UTF16ToUTF8(iter->message())); | |
90 } | |
91 | |
92 return warnings_list; | |
93 } | |
94 | |
95 std::vector<management::LaunchType> GetAvailableLaunchTypes( | |
96 const Extension& extension) { | |
97 std::vector<management::LaunchType> launch_type_list; | |
98 if (extension.is_platform_app()) { | |
99 launch_type_list.push_back(management::LAUNCH_TYPE_OPEN_AS_WINDOW); | |
100 return launch_type_list; | |
101 } | |
102 | |
103 launch_type_list.push_back(management::LAUNCH_TYPE_OPEN_AS_REGULAR_TAB); | |
104 | |
105 #if !defined(OS_MACOSX) | |
106 launch_type_list.push_back(management::LAUNCH_TYPE_OPEN_AS_WINDOW); | |
107 #endif | |
108 | |
109 if (!util::IsStreamlinedHostedAppsEnabled()) { | |
110 launch_type_list.push_back(management::LAUNCH_TYPE_OPEN_AS_PINNED_TAB); | |
111 launch_type_list.push_back(management::LAUNCH_TYPE_OPEN_FULL_SCREEN); | |
112 } | |
113 return launch_type_list; | |
114 } | |
115 | |
116 scoped_ptr<management::ExtensionInfo> CreateExtensionInfo( | |
117 const Extension& extension, | |
118 ExtensionSystem* system) { | |
119 scoped_ptr<management::ExtensionInfo> info(new management::ExtensionInfo()); | |
120 ExtensionService* service = system->extension_service(); | |
121 | |
122 info->id = extension.id(); | |
123 info->name = extension.name(); | |
124 info->short_name = extension.short_name(); | |
125 info->enabled = service->IsExtensionEnabled(info->id); | |
126 info->offline_enabled = OfflineEnabledInfo::IsOfflineEnabled(&extension); | |
127 info->version = extension.VersionString(); | |
128 info->description = extension.description(); | |
129 info->options_url = OptionsPageInfo::GetOptionsPage(&extension).spec(); | |
130 info->homepage_url.reset(new std::string( | |
131 ManifestURL::GetHomepageURL(&extension).spec())); | |
132 info->may_disable = system->management_policy()-> | |
133 UserMayModifySettings(&extension, NULL); | |
134 info->is_app = extension.is_app(); | |
135 if (info->is_app) { | |
136 if (extension.is_legacy_packaged_app()) | |
137 info->type = management::ExtensionInfo::TYPE_LEGACY_PACKAGED_APP; | |
138 else if (extension.is_hosted_app()) | |
139 info->type = management::ExtensionInfo::TYPE_HOSTED_APP; | |
140 else | |
141 info->type = management::ExtensionInfo::TYPE_PACKAGED_APP; | |
142 } else if (extension.is_theme()) { | |
143 info->type = management::ExtensionInfo::TYPE_THEME; | |
144 } else { | |
145 info->type = management::ExtensionInfo::TYPE_EXTENSION; | |
146 } | |
147 | |
148 if (info->enabled) { | |
149 info->disabled_reason = management::ExtensionInfo::DISABLED_REASON_NONE; | |
150 } else { | |
151 ExtensionPrefs* prefs = ExtensionPrefs::Get(service->profile()); | |
152 if (prefs->DidExtensionEscalatePermissions(extension.id())) { | |
153 info->disabled_reason = | |
154 management::ExtensionInfo::DISABLED_REASON_PERMISSIONS_INCREASE; | |
155 } else { | |
156 info->disabled_reason = | |
157 management::ExtensionInfo::DISABLED_REASON_UNKNOWN; | |
158 } | |
159 } | |
160 | |
161 if (!ManifestURL::GetUpdateURL(&extension).is_empty()) { | |
162 info->update_url.reset(new std::string( | |
163 ManifestURL::GetUpdateURL(&extension).spec())); | |
164 } | |
165 | |
166 if (extension.is_app()) { | |
167 info->app_launch_url.reset(new std::string( | |
168 AppLaunchInfo::GetFullLaunchURL(&extension).spec())); | |
169 } | |
170 | |
171 const ExtensionIconSet::IconMap& icons = | |
172 IconsInfo::GetIcons(&extension).map(); | |
173 if (!icons.empty()) { | |
174 info->icons.reset(new IconInfoList()); | |
175 ExtensionIconSet::IconMap::const_iterator icon_iter; | |
176 for (icon_iter = icons.begin(); icon_iter != icons.end(); ++icon_iter) { | |
177 management::IconInfo* icon_info = new management::IconInfo(); | |
178 icon_info->size = icon_iter->first; | |
179 GURL url = ExtensionIconSource::GetIconURL( | |
180 &extension, icon_info->size, ExtensionIconSet::MATCH_EXACTLY, false, | |
181 NULL); | |
182 icon_info->url = url.spec(); | |
183 info->icons->push_back(make_linked_ptr<management::IconInfo>(icon_info)); | |
184 } | |
185 } | |
186 | |
187 const std::set<std::string> perms = | |
188 extension.permissions_data()->active_permissions()->GetAPIsAsStrings(); | |
189 if (!perms.empty()) { | |
190 std::set<std::string>::const_iterator perms_iter; | |
191 for (perms_iter = perms.begin(); perms_iter != perms.end(); ++perms_iter) | |
192 info->permissions.push_back(*perms_iter); | |
193 } | |
194 | |
195 if (!extension.is_hosted_app()) { | |
196 // Skip host permissions for hosted apps. | |
197 const URLPatternSet host_perms = | |
198 extension.permissions_data()->active_permissions()->explicit_hosts(); | |
199 if (!host_perms.is_empty()) { | |
200 for (URLPatternSet::const_iterator iter = host_perms.begin(); | |
201 iter != host_perms.end(); ++iter) { | |
202 info->host_permissions.push_back(iter->GetAsString()); | |
203 } | |
204 } | |
205 } | |
206 | |
207 switch (extension.location()) { | |
208 case Manifest::INTERNAL: | |
209 info->install_type = management::ExtensionInfo::INSTALL_TYPE_NORMAL; | |
210 break; | |
211 case Manifest::UNPACKED: | |
212 case Manifest::COMMAND_LINE: | |
213 info->install_type = management::ExtensionInfo::INSTALL_TYPE_DEVELOPMENT; | |
214 break; | |
215 case Manifest::EXTERNAL_PREF: | |
216 case Manifest::EXTERNAL_REGISTRY: | |
217 case Manifest::EXTERNAL_PREF_DOWNLOAD: | |
218 info->install_type = management::ExtensionInfo::INSTALL_TYPE_SIDELOAD; | |
219 break; | |
220 case Manifest::EXTERNAL_POLICY: | |
221 case Manifest::EXTERNAL_POLICY_DOWNLOAD: | |
222 info->install_type = management::ExtensionInfo::INSTALL_TYPE_ADMIN; | |
223 break; | |
224 case Manifest::NUM_LOCATIONS: | |
225 NOTREACHED(); | |
226 case Manifest::INVALID_LOCATION: | |
227 case Manifest::COMPONENT: | |
228 case Manifest::EXTERNAL_COMPONENT: | |
229 info->install_type = management::ExtensionInfo::INSTALL_TYPE_OTHER; | |
230 break; | |
231 } | |
232 | |
233 info->launch_type = management::LAUNCH_TYPE_NONE; | |
234 if (extension.is_app()) { | |
235 LaunchType launch_type; | |
236 if (extension.is_platform_app()) { | |
237 launch_type = LAUNCH_TYPE_WINDOW; | |
238 } else { | |
239 launch_type = | |
240 GetLaunchType(ExtensionPrefs::Get(service->profile()), &extension); | |
241 } | |
242 | |
243 switch (launch_type) { | |
244 case LAUNCH_TYPE_PINNED: | |
245 info->launch_type = management::LAUNCH_TYPE_OPEN_AS_PINNED_TAB; | |
246 break; | |
247 case LAUNCH_TYPE_REGULAR: | |
248 info->launch_type = management::LAUNCH_TYPE_OPEN_AS_REGULAR_TAB; | |
249 break; | |
250 case LAUNCH_TYPE_FULLSCREEN: | |
251 info->launch_type = management::LAUNCH_TYPE_OPEN_FULL_SCREEN; | |
252 break; | |
253 case LAUNCH_TYPE_WINDOW: | |
254 info->launch_type = management::LAUNCH_TYPE_OPEN_AS_WINDOW; | |
255 break; | |
256 case LAUNCH_TYPE_INVALID: | |
257 case NUM_LAUNCH_TYPES: | |
258 NOTREACHED(); | |
259 } | |
260 | |
261 info->available_launch_types.reset(new std::vector<management::LaunchType>( | |
262 GetAvailableLaunchTypes(extension))); | |
263 } | |
264 | |
265 return info.Pass(); | |
266 } | |
267 | |
268 void AddExtensionInfo(const ExtensionSet& extensions, | |
269 ExtensionSystem* system, | |
270 ExtensionInfoList* extension_list, | |
271 content::BrowserContext* context) { | |
272 for (ExtensionSet::const_iterator iter = extensions.begin(); | |
273 iter != extensions.end(); ++iter) { | |
274 const Extension& extension = *iter->get(); | |
275 | |
276 if (ui_util::ShouldNotBeVisible(&extension, context)) | |
277 continue; // Skip built-in extensions/apps. | |
278 | |
279 extension_list->push_back(make_linked_ptr<management::ExtensionInfo>( | |
280 CreateExtensionInfo(extension, system).release())); | |
281 } | |
282 } | |
283 | |
284 } // namespace | |
285 | |
286 ExtensionService* ManagementFunction::service() { | |
287 return ExtensionSystem::Get(GetProfile())->extension_service(); | |
288 } | |
289 | |
290 ExtensionService* AsyncManagementFunction::service() { | |
291 return ExtensionSystem::Get(GetProfile())->extension_service(); | |
292 } | |
293 | |
294 bool ManagementGetAllFunction::RunSync() { | |
295 ExtensionInfoList extensions; | |
296 ExtensionRegistry* registry = ExtensionRegistry::Get(GetProfile()); | |
297 ExtensionSystem* system = ExtensionSystem::Get(GetProfile()); | |
298 | |
299 AddExtensionInfo(registry->enabled_extensions(), | |
300 system, &extensions, browser_context()); | |
301 AddExtensionInfo(registry->disabled_extensions(), | |
302 system, &extensions, browser_context()); | |
303 AddExtensionInfo(registry->terminated_extensions(), | |
304 system, &extensions, browser_context()); | |
305 | |
306 results_ = management::GetAll::Results::Create(extensions); | |
307 return true; | |
308 } | |
309 | |
310 bool ManagementGetFunction::RunSync() { | |
311 scoped_ptr<management::Get::Params> params( | |
312 management::Get::Params::Create(*args_)); | |
313 EXTENSION_FUNCTION_VALIDATE(params.get()); | |
314 | |
315 const Extension* extension = service()->GetExtensionById(params->id, true); | |
316 if (!extension) { | |
317 error_ = ErrorUtils::FormatErrorMessage(keys::kNoExtensionError, | |
318 params->id); | |
319 return false; | |
320 } | |
321 | |
322 scoped_ptr<management::ExtensionInfo> info = | |
323 CreateExtensionInfo(*extension, ExtensionSystem::Get(GetProfile())); | |
324 results_ = management::Get::Results::Create(*info); | |
325 | |
326 return true; | |
327 } | |
328 | |
329 bool ManagementGetSelfFunction::RunSync() { | |
330 scoped_ptr<management::ExtensionInfo> info = | |
331 CreateExtensionInfo(*extension_, ExtensionSystem::Get(GetProfile())); | |
332 results_ = management::Get::Results::Create(*info); | |
333 | |
334 return true; | |
335 } | |
336 | |
337 bool ManagementGetPermissionWarningsByIdFunction::RunSync() { | |
338 scoped_ptr<management::GetPermissionWarningsById::Params> params( | |
339 management::GetPermissionWarningsById::Params::Create(*args_)); | |
340 EXTENSION_FUNCTION_VALIDATE(params.get()); | |
341 | |
342 const Extension* extension = service()->GetExtensionById(params->id, true); | |
343 if (!extension) { | |
344 error_ = ErrorUtils::FormatErrorMessage(keys::kNoExtensionError, | |
345 params->id); | |
346 return false; | |
347 } | |
348 | |
349 std::vector<std::string> warnings = CreateWarningsList(extension); | |
350 results_ = management::GetPermissionWarningsById::Results::Create(warnings); | |
351 return true; | |
352 } | |
353 | |
354 namespace { | |
355 | |
356 // This class helps ManagementGetPermissionWarningsByManifestFunction manage | |
357 // sending manifest JSON strings to the utility process for parsing. | |
358 class SafeManifestJSONParser : public UtilityProcessHostClient { | |
359 public: | |
360 SafeManifestJSONParser( | |
361 ManagementGetPermissionWarningsByManifestFunction* client, | |
362 const std::string& manifest) | |
363 : client_(client), | |
364 manifest_(manifest) {} | |
365 | |
366 void Start() { | |
367 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
368 BrowserThread::PostTask( | |
369 BrowserThread::IO, | |
370 FROM_HERE, | |
371 base::Bind(&SafeManifestJSONParser::StartWorkOnIOThread, this)); | |
372 } | |
373 | |
374 void StartWorkOnIOThread() { | |
375 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
376 UtilityProcessHost* host = UtilityProcessHost::Create( | |
377 this, base::MessageLoopProxy::current().get()); | |
378 host->Send(new ChromeUtilityMsg_ParseJSON(manifest_)); | |
379 } | |
380 | |
381 bool OnMessageReceived(const IPC::Message& message) override { | |
382 bool handled = true; | |
383 IPC_BEGIN_MESSAGE_MAP(SafeManifestJSONParser, message) | |
384 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseJSON_Succeeded, | |
385 OnJSONParseSucceeded) | |
386 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ParseJSON_Failed, | |
387 OnJSONParseFailed) | |
388 IPC_MESSAGE_UNHANDLED(handled = false) | |
389 IPC_END_MESSAGE_MAP() | |
390 return handled; | |
391 } | |
392 | |
393 void OnJSONParseSucceeded(const base::ListValue& wrapper) { | |
394 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
395 const base::Value* value = NULL; | |
396 CHECK(wrapper.Get(0, &value)); | |
397 if (value->IsType(base::Value::TYPE_DICTIONARY)) | |
398 parsed_manifest_.reset( | |
399 static_cast<const base::DictionaryValue*>(value)->DeepCopy()); | |
400 else | |
401 error_ = keys::kManifestParseError; | |
402 | |
403 BrowserThread::PostTask( | |
404 BrowserThread::UI, | |
405 FROM_HERE, | |
406 base::Bind(&SafeManifestJSONParser::ReportResultFromUIThread, this)); | |
407 } | |
408 | |
409 void OnJSONParseFailed(const std::string& error) { | |
410 CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
411 error_ = error; | |
412 BrowserThread::PostTask( | |
413 BrowserThread::UI, | |
414 FROM_HERE, | |
415 base::Bind(&SafeManifestJSONParser::ReportResultFromUIThread, this)); | |
416 } | |
417 | |
418 void ReportResultFromUIThread() { | |
419 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
420 if (error_.empty() && parsed_manifest_.get()) | |
421 client_->OnParseSuccess(parsed_manifest_.Pass()); | |
422 else | |
423 client_->OnParseFailure(error_); | |
424 } | |
425 | |
426 private: | |
427 ~SafeManifestJSONParser() override {} | |
428 | |
429 // The client who we'll report results back to. | |
430 ManagementGetPermissionWarningsByManifestFunction* client_; | |
431 | |
432 // Data to parse. | |
433 std::string manifest_; | |
434 | |
435 // Results of parsing. | |
436 scoped_ptr<base::DictionaryValue> parsed_manifest_; | |
437 | |
438 std::string error_; | |
439 }; | |
440 | |
441 } // namespace | |
442 | |
443 bool ManagementGetPermissionWarningsByManifestFunction::RunAsync() { | |
444 scoped_ptr<management::GetPermissionWarningsByManifest::Params> params( | |
445 management::GetPermissionWarningsByManifest::Params::Create(*args_)); | |
446 EXTENSION_FUNCTION_VALIDATE(params.get()); | |
447 | |
448 scoped_refptr<SafeManifestJSONParser> parser = | |
449 new SafeManifestJSONParser(this, params->manifest_str); | |
450 parser->Start(); | |
451 | |
452 // Matched with a Release() in OnParseSuccess/Failure(). | |
453 AddRef(); | |
454 | |
455 // Response is sent async in OnParseSuccess/Failure(). | |
456 return true; | |
457 } | |
458 | |
459 void ManagementGetPermissionWarningsByManifestFunction::OnParseSuccess( | |
460 scoped_ptr<base::DictionaryValue> parsed_manifest) { | |
461 CHECK(parsed_manifest.get()); | |
462 | |
463 scoped_refptr<Extension> extension = Extension::Create( | |
464 base::FilePath(), Manifest::INVALID_LOCATION, *parsed_manifest, | |
465 Extension::NO_FLAGS, &error_); | |
466 if (!extension.get()) { | |
467 OnParseFailure(keys::kExtensionCreateError); | |
468 return; | |
469 } | |
470 | |
471 std::vector<std::string> warnings = CreateWarningsList(extension.get()); | |
472 results_ = | |
473 management::GetPermissionWarningsByManifest::Results::Create(warnings); | |
474 SendResponse(true); | |
475 | |
476 // Matched with AddRef() in RunAsync(). | |
477 Release(); | |
478 } | |
479 | |
480 void ManagementGetPermissionWarningsByManifestFunction::OnParseFailure( | |
481 const std::string& error) { | |
482 error_ = error; | |
483 SendResponse(false); | |
484 | |
485 // Matched with AddRef() in RunAsync(). | |
486 Release(); | |
487 } | |
488 | |
489 bool ManagementLaunchAppFunction::RunSync() { | |
490 scoped_ptr<management::LaunchApp::Params> params( | |
491 management::LaunchApp::Params::Create(*args_)); | |
492 EXTENSION_FUNCTION_VALIDATE(params.get()); | |
493 const Extension* extension = service()->GetExtensionById(params->id, true); | |
494 if (!extension) { | |
495 error_ = ErrorUtils::FormatErrorMessage(keys::kNoExtensionError, | |
496 params->id); | |
497 return false; | |
498 } | |
499 if (!extension->is_app()) { | |
500 error_ = ErrorUtils::FormatErrorMessage(keys::kNotAnAppError, | |
501 params->id); | |
502 return false; | |
503 } | |
504 | |
505 // Look at prefs to find the right launch container. | |
506 // If the user has not set a preference, the default launch value will be | |
507 // returned. | |
508 LaunchContainer launch_container = | |
509 GetLaunchContainer(ExtensionPrefs::Get(GetProfile()), extension); | |
510 OpenApplication(AppLaunchParams( | |
511 GetProfile(), extension, launch_container, NEW_FOREGROUND_TAB)); | |
512 CoreAppLauncherHandler::RecordAppLaunchType( | |
513 extension_misc::APP_LAUNCH_EXTENSION_API, | |
514 extension->GetType()); | |
515 | |
516 return true; | |
517 } | |
518 | |
519 ManagementSetEnabledFunction::ManagementSetEnabledFunction() { | |
520 } | |
521 | |
522 ManagementSetEnabledFunction::~ManagementSetEnabledFunction() { | |
523 } | |
524 | |
525 bool ManagementSetEnabledFunction::RunAsync() { | |
526 scoped_ptr<management::SetEnabled::Params> params( | |
527 management::SetEnabled::Params::Create(*args_)); | |
528 EXTENSION_FUNCTION_VALIDATE(params.get()); | |
529 | |
530 extension_id_ = params->id; | |
531 | |
532 const Extension* extension = | |
533 ExtensionRegistry::Get(GetProfile()) | |
534 ->GetExtensionById(extension_id_, ExtensionRegistry::EVERYTHING); | |
535 if (!extension || ui_util::ShouldNotBeVisible(extension, browser_context())) { | |
536 error_ = ErrorUtils::FormatErrorMessage( | |
537 keys::kNoExtensionError, extension_id_); | |
538 return false; | |
539 } | |
540 | |
541 const ManagementPolicy* policy = | |
542 ExtensionSystem::Get(GetProfile())->management_policy(); | |
543 if (!policy->UserMayModifySettings(extension, NULL) || | |
544 (!params->enabled && policy->MustRemainEnabled(extension, NULL)) || | |
545 (params->enabled && policy->MustRemainDisabled(extension, NULL, NULL))) { | |
546 error_ = ErrorUtils::FormatErrorMessage( | |
547 keys::kUserCantModifyError, extension_id_); | |
548 return false; | |
549 } | |
550 | |
551 bool currently_enabled = service()->IsExtensionEnabled(extension_id_); | |
552 | |
553 if (!currently_enabled && params->enabled) { | |
554 ExtensionPrefs* prefs = ExtensionPrefs::Get(GetProfile()); | |
555 if (prefs->DidExtensionEscalatePermissions(extension_id_)) { | |
556 if (!user_gesture()) { | |
557 error_ = keys::kGestureNeededForEscalationError; | |
558 return false; | |
559 } | |
560 AddRef(); // Matched in InstallUIProceed/InstallUIAbort | |
561 install_prompt_.reset( | |
562 new ExtensionInstallPrompt(GetAssociatedWebContents())); | |
563 install_prompt_->ConfirmReEnable(this, extension); | |
564 return true; | |
565 } | |
566 service()->EnableExtension(extension_id_); | |
567 } else if (currently_enabled && !params->enabled) { | |
568 service()->DisableExtension(extension_id_, Extension::DISABLE_USER_ACTION); | |
569 } | |
570 | |
571 BrowserThread::PostTask( | |
572 BrowserThread::UI, | |
573 FROM_HERE, | |
574 base::Bind(&ManagementSetEnabledFunction::SendResponse, this, true)); | |
575 | |
576 return true; | |
577 } | |
578 | |
579 void ManagementSetEnabledFunction::InstallUIProceed() { | |
580 service()->EnableExtension(extension_id_); | |
581 SendResponse(true); | |
582 Release(); | |
583 } | |
584 | |
585 void ManagementSetEnabledFunction::InstallUIAbort(bool user_initiated) { | |
586 error_ = keys::kUserDidNotReEnableError; | |
587 SendResponse(false); | |
588 Release(); | |
589 } | |
590 | |
591 ManagementUninstallFunctionBase::ManagementUninstallFunctionBase() { | |
592 } | |
593 | |
594 ManagementUninstallFunctionBase::~ManagementUninstallFunctionBase() { | |
595 } | |
596 | |
597 bool ManagementUninstallFunctionBase::Uninstall( | |
598 const std::string& target_extension_id, | |
599 bool show_confirm_dialog) { | |
600 extension_id_ = target_extension_id; | |
601 const Extension* target_extension = | |
602 extensions::ExtensionRegistry::Get(browser_context())-> | |
603 GetExtensionById(extension_id_, ExtensionRegistry::EVERYTHING); | |
604 if (!target_extension || | |
605 ui_util::ShouldNotBeVisible(target_extension, browser_context())) { | |
606 error_ = ErrorUtils::FormatErrorMessage( | |
607 keys::kNoExtensionError, extension_id_); | |
608 return false; | |
609 } | |
610 | |
611 if (!ExtensionSystem::Get(GetProfile()) | |
612 ->management_policy() | |
613 ->UserMayModifySettings(target_extension, NULL)) { | |
614 error_ = ErrorUtils::FormatErrorMessage( | |
615 keys::kUserCantModifyError, extension_id_); | |
616 return false; | |
617 } | |
618 | |
619 if (auto_confirm_for_test == DO_NOT_SKIP) { | |
620 if (show_confirm_dialog) { | |
621 AddRef(); // Balanced in ExtensionUninstallAccepted/Canceled | |
622 content::WebContents* web_contents = GetAssociatedWebContents(); | |
623 extension_uninstall_dialog_.reset(ExtensionUninstallDialog::Create( | |
624 GetProfile(), | |
625 web_contents ? web_contents->GetTopLevelNativeWindow() : NULL, | |
626 this)); | |
627 if (extension_id() != target_extension_id) { | |
628 extension_uninstall_dialog_->ConfirmProgrammaticUninstall( | |
629 target_extension, extension()); | |
630 } else { | |
631 // If this is a self uninstall, show the generic uninstall dialog. | |
632 extension_uninstall_dialog_->ConfirmUninstall(target_extension); | |
633 } | |
634 } else { | |
635 Finish(true); | |
636 } | |
637 } else { | |
638 Finish(auto_confirm_for_test == PROCEED); | |
639 } | |
640 | |
641 return true; | |
642 } | |
643 | |
644 // static | |
645 void ManagementUninstallFunctionBase::SetAutoConfirmForTest( | |
646 bool should_proceed) { | |
647 auto_confirm_for_test = should_proceed ? PROCEED : ABORT; | |
648 } | |
649 | |
650 void ManagementUninstallFunctionBase::Finish(bool should_uninstall) { | |
651 if (should_uninstall) { | |
652 // The extension can be uninstalled in another window while the UI was | |
653 // showing. Do nothing in that case. | |
654 ExtensionRegistry* registry = ExtensionRegistry::Get(GetProfile()); | |
655 const Extension* extension = registry->GetExtensionById( | |
656 extension_id_, ExtensionRegistry::EVERYTHING); | |
657 if (!extension) { | |
658 error_ = ErrorUtils::FormatErrorMessage(keys::kNoExtensionError, | |
659 extension_id_); | |
660 SendResponse(false); | |
661 } else { | |
662 bool success = service()->UninstallExtension( | |
663 extension_id_, | |
664 extensions::UNINSTALL_REASON_MANAGEMENT_API, | |
665 base::Bind(&base::DoNothing), | |
666 NULL); | |
667 | |
668 // TODO set error_ if !success | |
669 SendResponse(success); | |
670 } | |
671 } else { | |
672 error_ = ErrorUtils::FormatErrorMessage( | |
673 keys::kUninstallCanceledError, extension_id_); | |
674 SendResponse(false); | |
675 } | |
676 } | |
677 | |
678 void ManagementUninstallFunctionBase::ExtensionUninstallAccepted() { | |
679 Finish(true); | |
680 Release(); | |
681 } | |
682 | |
683 void ManagementUninstallFunctionBase::ExtensionUninstallCanceled() { | |
684 Finish(false); | |
685 Release(); | |
686 } | |
687 | |
688 ManagementUninstallFunction::ManagementUninstallFunction() { | |
689 } | |
690 | |
691 ManagementUninstallFunction::~ManagementUninstallFunction() { | |
692 } | |
693 | |
694 bool ManagementUninstallFunction::RunAsync() { | |
695 scoped_ptr<management::Uninstall::Params> params( | |
696 management::Uninstall::Params::Create(*args_)); | |
697 EXTENSION_FUNCTION_VALIDATE(extension_.get()); | |
698 EXTENSION_FUNCTION_VALIDATE(params.get()); | |
699 | |
700 bool show_confirm_dialog = true; | |
701 // By default confirmation dialog isn't shown when uninstalling self, but this | |
702 // can be overridden with showConfirmDialog. | |
703 if (params->id == extension_->id()) { | |
704 show_confirm_dialog = params->options.get() && | |
705 params->options->show_confirm_dialog.get() && | |
706 *params->options->show_confirm_dialog; | |
707 } | |
708 if (show_confirm_dialog && !user_gesture()) { | |
709 error_ = keys::kGestureNeededForUninstallError; | |
710 return false; | |
711 } | |
712 return Uninstall(params->id, show_confirm_dialog); | |
713 } | |
714 | |
715 ManagementUninstallSelfFunction::ManagementUninstallSelfFunction() { | |
716 } | |
717 | |
718 ManagementUninstallSelfFunction::~ManagementUninstallSelfFunction() { | |
719 } | |
720 | |
721 bool ManagementUninstallSelfFunction::RunAsync() { | |
722 scoped_ptr<management::UninstallSelf::Params> params( | |
723 management::UninstallSelf::Params::Create(*args_)); | |
724 EXTENSION_FUNCTION_VALIDATE(params.get()); | |
725 | |
726 bool show_confirm_dialog = false; | |
727 if (params->options.get() && params->options->show_confirm_dialog.get()) | |
728 show_confirm_dialog = *params->options->show_confirm_dialog; | |
729 return Uninstall(extension_->id(), show_confirm_dialog); | |
730 } | |
731 | |
732 ManagementCreateAppShortcutFunction::ManagementCreateAppShortcutFunction() { | |
733 } | |
734 | |
735 ManagementCreateAppShortcutFunction::~ManagementCreateAppShortcutFunction() { | |
736 } | |
737 | |
738 // static | |
739 void ManagementCreateAppShortcutFunction::SetAutoConfirmForTest( | |
740 bool should_proceed) { | |
741 auto_confirm_for_test = should_proceed ? PROCEED : ABORT; | |
742 } | |
743 | |
744 void ManagementCreateAppShortcutFunction::OnCloseShortcutPrompt(bool created) { | |
745 if (!created) | |
746 error_ = keys::kCreateShortcutCanceledError; | |
747 SendResponse(created); | |
748 Release(); | |
749 } | |
750 | |
751 bool ManagementCreateAppShortcutFunction::RunAsync() { | |
752 if (!user_gesture()) { | |
753 error_ = keys::kGestureNeededForCreateAppShortcutError; | |
754 return false; | |
755 } | |
756 | |
757 scoped_ptr<management::CreateAppShortcut::Params> params( | |
758 management::CreateAppShortcut::Params::Create(*args_)); | |
759 EXTENSION_FUNCTION_VALIDATE(params.get()); | |
760 const Extension* extension = service()->GetExtensionById(params->id, true); | |
761 if (!extension) { | |
762 error_ = ErrorUtils::FormatErrorMessage(keys::kNoExtensionError, | |
763 params->id); | |
764 return false; | |
765 } | |
766 | |
767 if (!extension->is_app()) { | |
768 error_ = ErrorUtils::FormatErrorMessage(keys::kNotAnAppError, params->id); | |
769 return false; | |
770 } | |
771 | |
772 #if defined(OS_MACOSX) | |
773 if (!extension->is_platform_app()) { | |
774 error_ = keys::kCreateOnlyPackagedAppShortcutMac; | |
775 return false; | |
776 } | |
777 #endif | |
778 | |
779 Browser* browser = chrome::FindBrowserWithProfile( | |
780 GetProfile(), chrome::HOST_DESKTOP_TYPE_NATIVE); | |
781 if (!browser) { | |
782 // Shouldn't happen if we have user gesture. | |
783 error_ = keys::kNoBrowserToCreateShortcut; | |
784 return false; | |
785 } | |
786 | |
787 // Matched with a Release() in OnCloseShortcutPrompt(). | |
788 AddRef(); | |
789 | |
790 if (auto_confirm_for_test == DO_NOT_SKIP) { | |
791 chrome::ShowCreateChromeAppShortcutsDialog( | |
792 browser->window()->GetNativeWindow(), browser->profile(), extension, | |
793 base::Bind(&ManagementCreateAppShortcutFunction::OnCloseShortcutPrompt, | |
794 this)); | |
795 } else { | |
796 OnCloseShortcutPrompt(auto_confirm_for_test == PROCEED); | |
797 } | |
798 | |
799 // Response is sent async in OnCloseShortcutPrompt(). | |
800 return true; | |
801 } | |
802 | |
803 bool ManagementSetLaunchTypeFunction::RunSync() { | |
804 if (!user_gesture()) { | |
805 error_ = keys::kGestureNeededForSetLaunchTypeError; | |
806 return false; | |
807 } | |
808 | |
809 scoped_ptr<management::SetLaunchType::Params> params( | |
810 management::SetLaunchType::Params::Create(*args_)); | |
811 EXTENSION_FUNCTION_VALIDATE(params.get()); | |
812 const Extension* extension = service()->GetExtensionById(params->id, true); | |
813 if (!extension) { | |
814 error_ = | |
815 ErrorUtils::FormatErrorMessage(keys::kNoExtensionError, params->id); | |
816 return false; | |
817 } | |
818 | |
819 if (!extension->is_app()) { | |
820 error_ = ErrorUtils::FormatErrorMessage(keys::kNotAnAppError, params->id); | |
821 return false; | |
822 } | |
823 | |
824 std::vector<management::LaunchType> available_launch_types = | |
825 GetAvailableLaunchTypes(*extension); | |
826 | |
827 management::LaunchType app_launch_type = params->launch_type; | |
828 if (std::find(available_launch_types.begin(), | |
829 available_launch_types.end(), | |
830 app_launch_type) == available_launch_types.end()) { | |
831 error_ = keys::kLaunchTypeNotAvailableError; | |
832 return false; | |
833 } | |
834 | |
835 LaunchType launch_type = LAUNCH_TYPE_DEFAULT; | |
836 switch (app_launch_type) { | |
837 case management::LAUNCH_TYPE_OPEN_AS_PINNED_TAB: | |
838 launch_type = LAUNCH_TYPE_PINNED; | |
839 break; | |
840 case management::LAUNCH_TYPE_OPEN_AS_REGULAR_TAB: | |
841 launch_type = LAUNCH_TYPE_REGULAR; | |
842 break; | |
843 case management::LAUNCH_TYPE_OPEN_FULL_SCREEN: | |
844 launch_type = LAUNCH_TYPE_FULLSCREEN; | |
845 break; | |
846 case management::LAUNCH_TYPE_OPEN_AS_WINDOW: | |
847 launch_type = LAUNCH_TYPE_WINDOW; | |
848 break; | |
849 case management::LAUNCH_TYPE_NONE: | |
850 NOTREACHED(); | |
851 } | |
852 | |
853 SetLaunchType(service(), params->id, launch_type); | |
854 | |
855 return true; | |
856 } | |
857 | |
858 ManagementGenerateAppForLinkFunction::ManagementGenerateAppForLinkFunction() { | |
859 } | |
860 | |
861 ManagementGenerateAppForLinkFunction::~ManagementGenerateAppForLinkFunction() { | |
862 } | |
863 | |
864 void ManagementGenerateAppForLinkFunction::FinishCreateBookmarkApp( | |
865 const Extension* extension, | |
866 const WebApplicationInfo& web_app_info) { | |
867 if (extension) { | |
868 scoped_ptr<management::ExtensionInfo> info = | |
869 CreateExtensionInfo(*extension, ExtensionSystem::Get(GetProfile())); | |
870 results_ = management::GenerateAppForLink::Results::Create(*info); | |
871 | |
872 SendResponse(true); | |
873 Release(); | |
874 } else { | |
875 error_ = keys::kGenerateAppForLinkInstallError; | |
876 SendResponse(false); | |
877 Release(); | |
878 } | |
879 } | |
880 | |
881 void ManagementGenerateAppForLinkFunction::OnFaviconForApp( | |
882 const favicon_base::FaviconImageResult& image_result) { | |
883 WebApplicationInfo web_app; | |
884 web_app.title = base::UTF8ToUTF16(title_); | |
885 web_app.app_url = launch_url_; | |
886 | |
887 if (!image_result.image.IsEmpty()) { | |
888 WebApplicationInfo::IconInfo icon; | |
889 icon.data = image_result.image.AsBitmap(); | |
890 icon.width = icon.data.width(); | |
891 icon.height = icon.data.height(); | |
892 web_app.icons.push_back(icon); | |
893 } | |
894 | |
895 bookmark_app_helper_.reset(new BookmarkAppHelper(service(), web_app, NULL)); | |
896 bookmark_app_helper_->Create(base::Bind( | |
897 &ManagementGenerateAppForLinkFunction::FinishCreateBookmarkApp, this)); | |
898 } | |
899 | |
900 bool ManagementGenerateAppForLinkFunction::RunAsync() { | |
901 if (!user_gesture()) { | |
902 error_ = keys::kGestureNeededForGenerateAppForLinkError; | |
903 return false; | |
904 } | |
905 | |
906 scoped_ptr<management::GenerateAppForLink::Params> params( | |
907 management::GenerateAppForLink::Params::Create(*args_)); | |
908 EXTENSION_FUNCTION_VALIDATE(params.get()); | |
909 | |
910 GURL launch_url(params->url); | |
911 if (!launch_url.is_valid() || !launch_url.SchemeIsHTTPOrHTTPS()) { | |
912 error_ = ErrorUtils::FormatErrorMessage(keys::kInvalidURLError, | |
913 params->url); | |
914 return false; | |
915 } | |
916 | |
917 if (params->title.empty()) { | |
918 error_ = keys::kEmptyTitleError; | |
919 return false; | |
920 } | |
921 | |
922 FaviconService* favicon_service = | |
923 FaviconServiceFactory::GetForProfile(GetProfile(), | |
924 Profile::EXPLICIT_ACCESS); | |
925 DCHECK(favicon_service); | |
926 | |
927 title_ = params->title; | |
928 launch_url_ = launch_url; | |
929 | |
930 favicon_service->GetFaviconImageForPageURL( | |
931 launch_url, | |
932 base::Bind(&ManagementGenerateAppForLinkFunction::OnFaviconForApp, this), | |
933 &cancelable_task_tracker_); | |
934 | |
935 // Matched with a Release() in OnExtensionLoaded(). | |
936 AddRef(); | |
937 | |
938 // Response is sent async in OnExtensionLoaded(). | |
939 return true; | |
940 } | |
941 | |
942 ManagementEventRouter::ManagementEventRouter(content::BrowserContext* context) | |
943 : browser_context_(context), extension_registry_observer_(this) { | |
944 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_)); | |
945 } | |
946 | |
947 ManagementEventRouter::~ManagementEventRouter() {} | |
948 | |
949 void ManagementEventRouter::OnExtensionLoaded( | |
950 content::BrowserContext* browser_context, | |
951 const Extension* extension) { | |
952 BroadcastEvent(extension, management::OnEnabled::kEventName); | |
953 } | |
954 | |
955 void ManagementEventRouter::OnExtensionUnloaded( | |
956 content::BrowserContext* browser_context, | |
957 const Extension* extension, | |
958 UnloadedExtensionInfo::Reason reason) { | |
959 BroadcastEvent(extension, management::OnDisabled::kEventName); | |
960 } | |
961 | |
962 void ManagementEventRouter::OnExtensionInstalled( | |
963 content::BrowserContext* browser_context, | |
964 const Extension* extension, | |
965 bool is_update) { | |
966 BroadcastEvent(extension, management::OnInstalled::kEventName); | |
967 } | |
968 | |
969 void ManagementEventRouter::OnExtensionUninstalled( | |
970 content::BrowserContext* browser_context, | |
971 const Extension* extension, | |
972 extensions::UninstallReason reason) { | |
973 BroadcastEvent(extension, management::OnUninstalled::kEventName); | |
974 } | |
975 | |
976 void ManagementEventRouter::BroadcastEvent(const Extension* extension, | |
977 const char* event_name) { | |
978 if (ui_util::ShouldNotBeVisible(extension, browser_context_)) | |
979 return; // Don't dispatch events for built-in extenions. | |
980 scoped_ptr<base::ListValue> args(new base::ListValue()); | |
981 if (event_name == management::OnUninstalled::kEventName) { | |
982 args->Append(new base::StringValue(extension->id())); | |
983 } else { | |
984 scoped_ptr<management::ExtensionInfo> info = | |
985 CreateExtensionInfo(*extension, ExtensionSystem::Get(browser_context_)); | |
986 args->Append(info->ToValue().release()); | |
987 } | |
988 | |
989 EventRouter::Get(browser_context_) | |
990 ->BroadcastEvent(scoped_ptr<Event>(new Event(event_name, args.Pass()))); | |
991 } | |
992 | |
993 ManagementAPI::ManagementAPI(content::BrowserContext* context) | |
994 : browser_context_(context) { | |
995 EventRouter* event_router = EventRouter::Get(browser_context_); | |
996 event_router->RegisterObserver(this, management::OnInstalled::kEventName); | |
997 event_router->RegisterObserver(this, management::OnUninstalled::kEventName); | |
998 event_router->RegisterObserver(this, management::OnEnabled::kEventName); | |
999 event_router->RegisterObserver(this, management::OnDisabled::kEventName); | |
1000 } | |
1001 | |
1002 ManagementAPI::~ManagementAPI() { | |
1003 } | |
1004 | |
1005 void ManagementAPI::Shutdown() { | |
1006 EventRouter::Get(browser_context_)->UnregisterObserver(this); | |
1007 } | |
1008 | |
1009 static base::LazyInstance<BrowserContextKeyedAPIFactory<ManagementAPI> > | |
1010 g_factory = LAZY_INSTANCE_INITIALIZER; | |
1011 | |
1012 // static | |
1013 BrowserContextKeyedAPIFactory<ManagementAPI>* | |
1014 ManagementAPI::GetFactoryInstance() { | |
1015 return g_factory.Pointer(); | |
1016 } | |
1017 | |
1018 void ManagementAPI::OnListenerAdded(const EventListenerInfo& details) { | |
1019 management_event_router_.reset(new ManagementEventRouter(browser_context_)); | |
1020 EventRouter::Get(browser_context_)->UnregisterObserver(this); | |
1021 } | |
1022 | |
1023 } // namespace extensions | |
OLD | NEW |