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.
5 #include "chrome/browser/extensions/api/developer_private/developer_private_api.h"
7 #include "apps/app_load_service.h"
8 #include "apps/app_restore_service.h"
9 #include "apps/app_window.h"
10 #include "apps/app_window_registry.h"
11 #include "apps/saved_files_service.h"
12 #include "base/base64.h"
13 #include "base/bind.h"
14 #include "base/command_line.h"
15 #include "base/file_util.h"
16 #include "base/files/file_enumerator.h"
17 #include "base/i18n/file_util_icu.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/values.h"
21 #include "chrome/browser/chrome_notification_types.h"
22 #include "chrome/browser/devtools/devtools_window.h"
23 #include "chrome/browser/extensions/api/developer_private/developer_private_api_factory.h"
24 #include "chrome/browser/extensions/api/developer_private/entry_picker.h"
25 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
26 #include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h"
27 #include "chrome/browser/extensions/devtools_util.h"
28 #include "chrome/browser/extensions/extension_disabled_ui.h"
29 #include "chrome/browser/extensions/extension_error_reporter.h"
30 #include "chrome/browser/extensions/extension_service.h"
31 #include "chrome/browser/extensions/extension_util.h"
32 #include "chrome/browser/extensions/unpacked_installer.h"
33 #include "chrome/browser/extensions/updater/extension_updater.h"
34 #include "chrome/browser/platform_util.h"
35 #include "chrome/browser/profiles/profile.h"
36 #include "chrome/browser/sync_file_system/drive_backend_v1/drive_file_sync_service.h"
37 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
38 #include "chrome/browser/ui/chrome_select_file_policy.h"
39 #include "chrome/browser/ui/webui/extensions/extension_error_ui_util.h"
40 #include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
41 #include "chrome/common/extensions/api/developer_private.h"
42 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
43 #include "chrome/common/extensions/manifest_handlers/icons_handler.h"
44 #include "chrome/common/extensions/manifest_url_handler.h"
45 #include "chrome/common/url_constants.h"
46 #include "content/public/browser/browser_thread.h"
47 #include "content/public/browser/notification_service.h"
48 #include "content/public/browser/render_process_host.h"
49 #include "content/public/browser/render_view_host.h"
50 #include "content/public/browser/site_instance.h"
51 #include "content/public/browser/storage_partition.h"
52 #include "content/public/browser/web_contents.h"
53 #include "extensions/browser/extension_error.h"
54 #include "extensions/browser/extension_registry.h"
55 #include "extensions/browser/extension_system.h"
56 #include "extensions/browser/management_policy.h"
57 #include "extensions/browser/view_type_utils.h"
58 #include "extensions/common/constants.h"
59 #include "extensions/common/extension_resource.h"
60 #include "extensions/common/extension_set.h"
61 #include "extensions/common/install_warning.h"
62 #include "extensions/common/manifest_handlers/background_info.h"
63 #include "extensions/common/manifest_handlers/incognito_info.h"
64 #include "extensions/common/manifest_handlers/offline_enabled_info.h"
65 #include "extensions/common/switches.h"
66 #include "grit/chromium_strings.h"
67 #include "grit/generated_resources.h"
68 #include "grit/theme_resources.h"
69 #include "net/base/net_util.h"
70 #include "ui/base/l10n/l10n_util.h"
71 #include "ui/base/resource/resource_bundle.h"
72 #include "ui/base/webui/web_ui_util.h"
73 #include "webkit/browser/fileapi/external_mount_points.h"
74 #include "webkit/browser/fileapi/file_system_context.h"
75 #include "webkit/browser/fileapi/file_system_operation.h"
76 #include "webkit/browser/fileapi/file_system_operation_runner.h"
77 #include "webkit/common/blob/shareable_file_reference.h"
79 using apps::AppWindow;
80 using apps::AppWindowRegistry;
81 using content::RenderViewHost;
83 namespace extensions {
85 namespace developer_private = api::developer_private;
89 const base::FilePath::CharType kUnpackedAppsFolder[]
90 = FILE_PATH_LITERAL("apps_target");
92 ExtensionUpdater* GetExtensionUpdater(Profile* profile) {
93 return profile->GetExtensionService()->updater();
96 GURL GetImageURLFromData(std::string contents) {
97 std::string contents_base64;
98 base::Base64Encode(contents, &contents_base64);
100 // TODO(dvh): make use of content::kDataScheme. Filed as crbug/297301.
101 const char kDataURLPrefix[] = "data:image;base64,";
102 return GURL(kDataURLPrefix + contents_base64);
105 GURL GetDefaultImageURL(developer_private::ItemType type) {
106 int icon_resource_id;
108 case developer::ITEM_TYPE_LEGACY_PACKAGED_APP:
109 case developer::ITEM_TYPE_HOSTED_APP:
110 case developer::ITEM_TYPE_PACKAGED_APP:
111 icon_resource_id = IDR_APP_DEFAULT_ICON;
114 icon_resource_id = IDR_EXTENSION_DEFAULT_ICON;
118 return GetImageURLFromData(
119 ResourceBundle::GetSharedInstance().GetRawDataResourceForScale(
120 icon_resource_id, ui::SCALE_FACTOR_100P).as_string());
123 // TODO(dvh): This code should be refactored and moved to
124 // extensions::ImageLoader. Also a resize should be performed to avoid
125 // potential huge URLs: crbug/297298.
126 GURL ToDataURL(const base::FilePath& path, developer_private::ItemType type) {
127 std::string contents;
128 if (path.empty() || !base::ReadFileToString(path, &contents))
129 return GetDefaultImageURL(type);
131 return GetImageURLFromData(contents);
134 std::string GetExtensionID(const RenderViewHost* render_view_host) {
135 if (!render_view_host->GetSiteInstance())
136 return std::string();
138 return render_view_host->GetSiteInstance()->GetSiteURL().host();
143 namespace AllowFileAccess = api::developer_private::AllowFileAccess;
144 namespace AllowIncognito = api::developer_private::AllowIncognito;
145 namespace ChoosePath = api::developer_private::ChoosePath;
146 namespace Enable = api::developer_private::Enable;
147 namespace GetItemsInfo = api::developer_private::GetItemsInfo;
148 namespace Inspect = api::developer_private::Inspect;
149 namespace PackDirectory = api::developer_private::PackDirectory;
150 namespace Reload = api::developer_private::Reload;
152 DeveloperPrivateAPI* DeveloperPrivateAPI::Get(Profile* profile) {
153 return DeveloperPrivateAPIFactory::GetForProfile(profile);
156 DeveloperPrivateAPI::DeveloperPrivateAPI(Profile* profile) : profile_(profile) {
157 RegisterNotifications();
160 DeveloperPrivateEventRouter::DeveloperPrivateEventRouter(Profile* profile)
161 : profile_(profile) {
163 chrome::NOTIFICATION_EXTENSION_INSTALLED,
164 chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
165 chrome::NOTIFICATION_EXTENSION_LOADED,
166 chrome::NOTIFICATION_EXTENSION_UNLOADED,
167 chrome::NOTIFICATION_EXTENSION_VIEW_REGISTERED,
168 chrome::NOTIFICATION_EXTENSION_VIEW_UNREGISTERED
171 CHECK(registrar_.IsEmpty());
172 for (size_t i = 0; i < arraysize(types); ++i) {
175 content::Source<Profile>(profile_));
178 ErrorConsole::Get(profile)->AddObserver(this);
181 DeveloperPrivateEventRouter::~DeveloperPrivateEventRouter() {
182 ErrorConsole::Get(profile_)->RemoveObserver(this);
185 void DeveloperPrivateEventRouter::AddExtensionId(
186 const std::string& extension_id) {
187 extension_ids_.insert(extension_id);
190 void DeveloperPrivateEventRouter::RemoveExtensionId(
191 const std::string& extension_id) {
192 extension_ids_.erase(extension_id);
195 void DeveloperPrivateEventRouter::Observe(
197 const content::NotificationSource& source,
198 const content::NotificationDetails& details) {
199 const char* event_name = NULL;
200 Profile* profile = content::Source<Profile>(source).ptr();
202 CHECK(profile_->IsSameProfile(profile));
203 developer::EventData event_data;
204 const Extension* extension = NULL;
207 case chrome::NOTIFICATION_EXTENSION_INSTALLED:
208 event_data.event_type = developer::EVENT_TYPE_INSTALLED;
210 content::Details<const InstalledExtensionInfo>(details)->extension;
212 case chrome::NOTIFICATION_EXTENSION_UNINSTALLED:
213 event_data.event_type = developer::EVENT_TYPE_UNINSTALLED;
214 extension = content::Details<const Extension>(details).ptr();
216 case chrome::NOTIFICATION_EXTENSION_LOADED:
217 event_data.event_type = developer::EVENT_TYPE_LOADED;
218 extension = content::Details<const Extension>(details).ptr();
220 case chrome::NOTIFICATION_EXTENSION_UNLOADED:
221 event_data.event_type = developer::EVENT_TYPE_UNLOADED;
223 content::Details<const UnloadedExtensionInfo>(details)->extension;
225 case chrome::NOTIFICATION_EXTENSION_VIEW_UNREGISTERED:
226 event_data.event_type = developer::EVENT_TYPE_VIEW_UNREGISTERED;
227 event_data.item_id = GetExtensionID(
228 content::Details<const RenderViewHost>(details).ptr());
230 case chrome::NOTIFICATION_EXTENSION_VIEW_REGISTERED:
231 event_data.event_type = developer::EVENT_TYPE_VIEW_REGISTERED;
232 event_data.item_id = GetExtensionID(
233 content::Details<const RenderViewHost>(details).ptr());
241 event_data.item_id = extension->id();
243 scoped_ptr<base::ListValue> args(new base::ListValue());
244 args->Append(event_data.ToValue().release());
246 event_name = developer_private::OnItemStateChanged::kEventName;
247 scoped_ptr<Event> event(new Event(event_name, args.Pass()));
248 ExtensionSystem::Get(profile)->event_router()->BroadcastEvent(event.Pass());
251 void DeveloperPrivateEventRouter::OnErrorAdded(const ExtensionError* error) {
252 // We don't want to handle errors thrown by extensions subscribed to these
253 // events (currently only the Apps Developer Tool), because doing so risks
255 if (extension_ids_.find(error->extension_id()) != extension_ids_.end())
258 developer::EventData event_data;
259 event_data.event_type = developer::EVENT_TYPE_ERROR_ADDED;
260 event_data.item_id = error->extension_id();
262 scoped_ptr<base::ListValue> args(new base::ListValue);
263 args->Append(event_data.ToValue().release());
265 ExtensionSystem::Get(profile_)->event_router()->BroadcastEvent(
266 scoped_ptr<Event>(new Event(
267 developer_private::OnItemStateChanged::kEventName, args.Pass())));
270 void DeveloperPrivateAPI::SetLastUnpackedDirectory(const base::FilePath& path) {
271 last_unpacked_directory_ = path;
274 void DeveloperPrivateAPI::RegisterNotifications() {
275 ExtensionSystem::Get(profile_)->event_router()->RegisterObserver(
276 this, developer_private::OnItemStateChanged::kEventName);
279 DeveloperPrivateAPI::~DeveloperPrivateAPI() {}
281 void DeveloperPrivateAPI::Shutdown() {}
283 void DeveloperPrivateAPI::OnListenerAdded(
284 const EventListenerInfo& details) {
285 if (!developer_private_event_router_) {
286 developer_private_event_router_.reset(
287 new DeveloperPrivateEventRouter(profile_));
290 developer_private_event_router_->AddExtensionId(details.extension_id);
293 void DeveloperPrivateAPI::OnListenerRemoved(
294 const EventListenerInfo& details) {
295 if (!ExtensionSystem::Get(profile_)->event_router()->HasEventListener(
296 developer_private::OnItemStateChanged::kEventName)) {
297 developer_private_event_router_.reset(NULL);
299 developer_private_event_router_->RemoveExtensionId(details.extension_id);
305 bool DeveloperPrivateAutoUpdateFunction::RunImpl() {
306 ExtensionUpdater* updater = GetExtensionUpdater(GetProfile());
308 updater->CheckNow(ExtensionUpdater::CheckParams());
309 SetResult(new base::FundamentalValue(true));
313 DeveloperPrivateAutoUpdateFunction::~DeveloperPrivateAutoUpdateFunction() {}
315 scoped_ptr<developer::ItemInfo>
316 DeveloperPrivateGetItemsInfoFunction::CreateItemInfo(const Extension& item,
317 bool item_is_enabled) {
318 scoped_ptr<developer::ItemInfo> info(new developer::ItemInfo());
320 ExtensionSystem* system = ExtensionSystem::Get(GetProfile());
321 ExtensionService* service = system->extension_service();
322 ExtensionRegistry* registry = ExtensionRegistry::Get(GetProfile());
324 info->id = item.id();
325 info->name = item.name();
326 info->enabled = service->IsExtensionEnabled(info->id);
327 info->offline_enabled = OfflineEnabledInfo::IsOfflineEnabled(&item);
328 info->version = item.VersionString();
329 info->description = item.description();
332 if (item.is_legacy_packaged_app())
333 info->type = developer::ITEM_TYPE_LEGACY_PACKAGED_APP;
334 else if (item.is_hosted_app())
335 info->type = developer::ITEM_TYPE_HOSTED_APP;
336 else if (item.is_platform_app())
337 info->type = developer::ITEM_TYPE_PACKAGED_APP;
340 } else if (item.is_theme()) {
341 info->type = developer::ITEM_TYPE_THEME;
342 } else if (item.is_extension()) {
343 info->type = developer::ITEM_TYPE_EXTENSION;
348 if (Manifest::IsUnpackedLocation(item.location())) {
350 new std::string(base::UTF16ToUTF8(item.path().LossyDisplayName())));
351 // If the ErrorConsole is enabled, get the errors for the extension and add
352 // them to the list. Otherwise, use the install warnings (using both is
354 ErrorConsole* error_console = ErrorConsole::Get(GetProfile());
355 if (error_console->enabled()) {
356 const ErrorList& errors = error_console->GetErrorsForExtension(item.id());
357 if (!errors.empty()) {
358 for (ErrorList::const_iterator iter = errors.begin();
359 iter != errors.end();
361 switch ((*iter)->type()) {
362 case ExtensionError::MANIFEST_ERROR:
363 info->manifest_errors.push_back(
364 make_linked_ptr((*iter)->ToValue().release()));
366 case ExtensionError::RUNTIME_ERROR: {
367 const RuntimeError* error =
368 static_cast<const RuntimeError*>(*iter);
369 scoped_ptr<base::DictionaryValue> value = error->ToValue();
370 bool can_inspect = content::RenderViewHost::FromID(
371 error->render_process_id(),
372 error->render_view_id()) != NULL;
373 value->SetBoolean("canInspect", can_inspect);
374 info->runtime_errors.push_back(make_linked_ptr(value.release()));
381 for (std::vector<extensions::InstallWarning>::const_iterator it =
382 item.install_warnings().begin();
383 it != item.install_warnings().end();
385 scoped_ptr<developer::InstallWarning> warning(
386 new developer::InstallWarning);
387 warning->message = it->message;
388 info->install_warnings.push_back(make_linked_ptr(warning.release()));
393 info->incognito_enabled = util::IsIncognitoEnabled(item.id(), GetProfile());
394 info->wants_file_access = item.wants_file_access();
395 info->allow_file_access = util::AllowFileAccess(item.id(), GetProfile());
396 info->allow_reload = Manifest::IsUnpackedLocation(item.location());
397 info->is_unpacked = Manifest::IsUnpackedLocation(item.location());
398 info->terminated = registry->terminated_extensions().Contains(item.id());
399 info->allow_incognito = item.can_be_incognito_enabled();
401 info->homepage_url.reset(new std::string(
402 ManifestURL::GetHomepageURL(&item).spec()));
403 if (!ManifestURL::GetOptionsPage(&item).is_empty()) {
404 info->options_url.reset(
405 new std::string(ManifestURL::GetOptionsPage(&item).spec()));
408 if (!ManifestURL::GetUpdateURL(&item).is_empty()) {
409 info->update_url.reset(
410 new std::string(ManifestURL::GetUpdateURL(&item).spec()));
414 info->app_launch_url.reset(new std::string(
415 extensions::AppLaunchInfo::GetFullLaunchURL(&item).spec()));
418 info->may_disable = system->management_policy()->
419 UserMayModifySettings(&item, NULL);
420 info->is_app = item.is_app();
421 info->views = GetInspectablePagesForExtension(&item, item_is_enabled);
426 void DeveloperPrivateGetItemsInfoFunction::GetIconsOnFileThread(
427 ItemInfoList item_list,
428 const std::map<std::string, ExtensionResource> idToIcon) {
429 for (ItemInfoList::iterator iter = item_list.begin();
430 iter != item_list.end(); ++iter) {
431 developer_private::ItemInfo* info = iter->get();
432 std::map<std::string, ExtensionResource>::const_iterator resource_ptr
433 = idToIcon.find(info->id);
434 if (resource_ptr != idToIcon.end()) {
436 ToDataURL(resource_ptr->second.GetFilePath(), info->type).spec();
440 results_ = developer::GetItemsInfo::Results::Create(item_list);
441 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
442 base::Bind(&DeveloperPrivateGetItemsInfoFunction::SendResponse,
447 void DeveloperPrivateGetItemsInfoFunction::
448 GetInspectablePagesForExtensionProcess(
449 const Extension* extension,
450 const std::set<content::RenderViewHost*>& views,
451 ItemInspectViewList* result) {
452 bool has_generated_background_page =
453 BackgroundInfo::HasGeneratedBackgroundPage(extension);
454 for (std::set<content::RenderViewHost*>::const_iterator iter = views.begin();
455 iter != views.end(); ++iter) {
456 content::RenderViewHost* host = *iter;
457 content::WebContents* web_contents =
458 content::WebContents::FromRenderViewHost(host);
459 ViewType host_type = GetViewType(web_contents);
460 if (VIEW_TYPE_EXTENSION_POPUP == host_type ||
461 VIEW_TYPE_EXTENSION_DIALOG == host_type)
464 content::RenderProcessHost* process = host->GetProcess();
465 bool is_background_page =
466 (web_contents->GetURL() == BackgroundInfo::GetBackgroundURL(extension));
467 result->push_back(constructInspectView(
468 web_contents->GetURL(),
470 host->GetRoutingID(),
471 process->GetBrowserContext()->IsOffTheRecord(),
472 is_background_page && has_generated_background_page));
476 void DeveloperPrivateGetItemsInfoFunction::GetAppWindowPagesForExtensionProfile(
477 const Extension* extension,
478 ItemInspectViewList* result) {
479 AppWindowRegistry* registry = AppWindowRegistry::Get(GetProfile());
480 if (!registry) return;
482 const AppWindowRegistry::AppWindowList windows =
483 registry->GetAppWindowsForApp(extension->id());
485 bool has_generated_background_page =
486 BackgroundInfo::HasGeneratedBackgroundPage(extension);
487 for (AppWindowRegistry::const_iterator it = windows.begin();
490 content::WebContents* web_contents = (*it)->web_contents();
491 RenderViewHost* host = web_contents->GetRenderViewHost();
492 content::RenderProcessHost* process = host->GetProcess();
493 bool is_background_page =
494 (web_contents->GetURL() == BackgroundInfo::GetBackgroundURL(extension));
495 result->push_back(constructInspectView(
496 web_contents->GetURL(),
498 host->GetRoutingID(),
499 process->GetBrowserContext()->IsOffTheRecord(),
500 is_background_page && has_generated_background_page));
504 linked_ptr<developer::ItemInspectView> DeveloperPrivateGetItemsInfoFunction::
505 constructInspectView(
507 int render_process_id,
510 bool generated_background_page) {
511 linked_ptr<developer::ItemInspectView> view(new developer::ItemInspectView());
513 if (url.scheme() == kExtensionScheme) {
515 view->path = url.path().substr(1);
517 // For live pages, use the full URL.
518 view->path = url.spec();
521 view->render_process_id = render_process_id;
522 view->render_view_id = render_view_id;
523 view->incognito = incognito;
524 view->generated_background_page = generated_background_page;
528 ItemInspectViewList DeveloperPrivateGetItemsInfoFunction::
529 GetInspectablePagesForExtension(
530 const Extension* extension,
531 bool extension_is_enabled) {
533 ItemInspectViewList result;
534 // Get the extension process's active views.
535 extensions::ProcessManager* process_manager =
536 ExtensionSystem::Get(GetProfile())->process_manager();
537 GetInspectablePagesForExtensionProcess(
539 process_manager->GetRenderViewHostsForExtension(extension->id()),
542 // Get app window views
543 GetAppWindowPagesForExtensionProfile(extension, &result);
545 // Include a link to start the lazy background page, if applicable.
546 if (BackgroundInfo::HasLazyBackgroundPage(extension) &&
547 extension_is_enabled &&
548 !process_manager->GetBackgroundHostForExtension(extension->id())) {
549 result.push_back(constructInspectView(
550 BackgroundInfo::GetBackgroundURL(extension),
554 BackgroundInfo::HasGeneratedBackgroundPage(extension)));
557 ExtensionService* service = GetProfile()->GetExtensionService();
558 // Repeat for the incognito process, if applicable. Don't try to get
559 // app windows for incognito process.
560 if (service->profile()->HasOffTheRecordProfile() &&
561 IncognitoInfo::IsSplitMode(extension)) {
562 process_manager = ExtensionSystem::Get(
563 service->profile()->GetOffTheRecordProfile())->process_manager();
564 GetInspectablePagesForExtensionProcess(
566 process_manager->GetRenderViewHostsForExtension(extension->id()),
569 if (BackgroundInfo::HasLazyBackgroundPage(extension) &&
570 extension_is_enabled &&
571 !process_manager->GetBackgroundHostForExtension(extension->id())) {
572 result.push_back(constructInspectView(
573 BackgroundInfo::GetBackgroundURL(extension),
577 BackgroundInfo::HasGeneratedBackgroundPage(extension)));
584 bool DeveloperPrivateGetItemsInfoFunction::RunImpl() {
585 scoped_ptr<developer::GetItemsInfo::Params> params(
586 developer::GetItemsInfo::Params::Create(*args_));
587 EXTENSION_FUNCTION_VALIDATE(params.get() != NULL);
589 bool include_disabled = params->include_disabled;
590 bool include_terminated = params->include_terminated;
592 extensions::ExtensionSet items;
594 ExtensionRegistry* registry = ExtensionRegistry::Get(GetProfile());
596 items.InsertAll(registry->enabled_extensions());
598 if (include_disabled) {
599 items.InsertAll(registry->disabled_extensions());
602 if (include_terminated) {
603 items.InsertAll(registry->terminated_extensions());
606 ExtensionService* service =
607 ExtensionSystem::Get(GetProfile())->extension_service();
608 std::map<std::string, ExtensionResource> id_to_icon;
609 ItemInfoList item_list;
611 for (extensions::ExtensionSet::const_iterator iter = items.begin();
612 iter != items.end(); ++iter) {
613 const Extension& item = *iter->get();
615 ExtensionResource item_resource =
616 IconsInfo::GetIconResource(&item,
617 extension_misc::EXTENSION_ICON_MEDIUM,
618 ExtensionIconSet::MATCH_BIGGER);
619 id_to_icon[item.id()] = item_resource;
621 // Don't show component extensions and invisible apps.
622 if (item.ShouldNotBeVisible())
625 item_list.push_back(make_linked_ptr<developer::ItemInfo>(
627 item, service->IsExtensionEnabled(item.id())).release()));
630 content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE,
631 base::Bind(&DeveloperPrivateGetItemsInfoFunction::GetIconsOnFileThread,
639 DeveloperPrivateGetItemsInfoFunction::~DeveloperPrivateGetItemsInfoFunction() {}
641 bool DeveloperPrivateAllowFileAccessFunction::RunImpl() {
642 scoped_ptr<AllowFileAccess::Params> params(
643 AllowFileAccess::Params::Create(*args_));
644 EXTENSION_FUNCTION_VALIDATE(params.get());
646 EXTENSION_FUNCTION_VALIDATE(user_gesture_);
648 ExtensionSystem* system = ExtensionSystem::Get(GetProfile());
649 ManagementPolicy* management_policy = system->management_policy();
650 ExtensionService* service = GetProfile()->GetExtensionService();
651 const Extension* extension = service->GetInstalledExtension(params->item_id);
656 } else if (!management_policy->UserMayModifySettings(extension, NULL)) {
657 LOG(ERROR) << "Attempt to change allow file access of an extension that "
658 << "non-usermanagable was made. Extension id : "
662 util::SetAllowFileAccess(extension->id(), GetProfile(), params->allow);
669 DeveloperPrivateAllowFileAccessFunction::
670 ~DeveloperPrivateAllowFileAccessFunction() {}
672 bool DeveloperPrivateAllowIncognitoFunction::RunImpl() {
673 scoped_ptr<AllowIncognito::Params> params(
674 AllowIncognito::Params::Create(*args_));
675 EXTENSION_FUNCTION_VALIDATE(params.get());
677 ExtensionService* service = GetProfile()->GetExtensionService();
678 const Extension* extension = service->GetInstalledExtension(params->item_id);
684 util::SetIsIncognitoEnabled(extension->id(), GetProfile(), params->allow);
689 DeveloperPrivateAllowIncognitoFunction::
690 ~DeveloperPrivateAllowIncognitoFunction() {}
693 bool DeveloperPrivateReloadFunction::RunImpl() {
694 scoped_ptr<Reload::Params> params(Reload::Params::Create(*args_));
695 EXTENSION_FUNCTION_VALIDATE(params.get());
697 ExtensionService* service = GetProfile()->GetExtensionService();
698 CHECK(!params->item_id.empty());
699 service->ReloadExtension(params->item_id);
703 bool DeveloperPrivateShowPermissionsDialogFunction::RunImpl() {
704 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &extension_id_));
705 ExtensionService* service = GetProfile()->GetExtensionService();
706 CHECK(!extension_id_.empty());
707 AppWindowRegistry* registry = AppWindowRegistry::Get(GetProfile());
709 AppWindow* app_window =
710 registry->GetAppWindowForRenderViewHost(render_view_host());
711 prompt_.reset(new ExtensionInstallPrompt(app_window->web_contents()));
712 const Extension* extension = service->GetInstalledExtension(extension_id_);
717 // Released by InstallUIAbort or InstallUIProceed.
719 std::vector<base::FilePath> retained_file_paths;
720 if (extension->HasAPIPermission(extensions::APIPermission::kFileSystem)) {
721 std::vector<apps::SavedFileEntry> retained_file_entries =
722 apps::SavedFilesService::Get(GetProfile())
723 ->GetAllFileEntries(extension_id_);
724 for (size_t i = 0; i < retained_file_entries.size(); i++) {
725 retained_file_paths.push_back(retained_file_entries[i].path);
728 prompt_->ReviewPermissions(this, extension, retained_file_paths);
732 DeveloperPrivateReloadFunction::~DeveloperPrivateReloadFunction() {}
734 // This is called when the user clicks "Revoke File Access."
735 void DeveloperPrivateShowPermissionsDialogFunction::InstallUIProceed() {
736 apps::SavedFilesService::Get(GetProfile())
737 ->ClearQueue(GetProfile()->GetExtensionService()->GetExtensionById(
738 extension_id_, true));
739 if (apps::AppRestoreService::Get(GetProfile())
740 ->IsAppRestorable(extension_id_))
741 apps::AppLoadService::Get(GetProfile())->RestartApplication(extension_id_);
746 void DeveloperPrivateShowPermissionsDialogFunction::InstallUIAbort(
747 bool user_initiated) {
752 DeveloperPrivateShowPermissionsDialogFunction::
753 DeveloperPrivateShowPermissionsDialogFunction() {}
755 DeveloperPrivateShowPermissionsDialogFunction::
756 ~DeveloperPrivateShowPermissionsDialogFunction() {}
758 DeveloperPrivateEnableFunction::DeveloperPrivateEnableFunction() {}
760 bool DeveloperPrivateEnableFunction::RunImpl() {
761 scoped_ptr<Enable::Params> params(Enable::Params::Create(*args_));
762 EXTENSION_FUNCTION_VALIDATE(params.get());
764 std::string extension_id = params->item_id;
766 ExtensionSystem* system = ExtensionSystem::Get(GetProfile());
767 ManagementPolicy* policy = system->management_policy();
768 ExtensionService* service = GetProfile()->GetExtensionService();
770 const Extension* extension = service->GetInstalledExtension(extension_id);
772 LOG(ERROR) << "Did not find extension with id " << extension_id;
775 bool enable = params->enable;
776 if (!policy->UserMayModifySettings(extension, NULL) ||
777 (!enable && policy->MustRemainEnabled(extension, NULL)) ||
778 (enable && policy->MustRemainDisabled(extension, NULL, NULL))) {
779 LOG(ERROR) << "Attempt to change enable state denied by management policy. "
780 << "Extension id: " << extension_id.c_str();
785 ExtensionPrefs* prefs = service->extension_prefs();
786 if (prefs->DidExtensionEscalatePermissions(extension_id)) {
787 AppWindowRegistry* registry = AppWindowRegistry::Get(GetProfile());
789 AppWindow* app_window =
790 registry->GetAppWindowForRenderViewHost(render_view_host());
795 ShowExtensionDisabledDialog(
796 service, app_window->web_contents(), extension);
797 } else if ((prefs->GetDisableReasons(extension_id) &
798 Extension::DISABLE_UNSUPPORTED_REQUIREMENT) &&
799 !requirements_checker_.get()) {
800 // Recheck the requirements.
801 scoped_refptr<const Extension> extension =
802 service->GetExtensionById(extension_id,
803 true );// include_disabled
804 requirements_checker_.reset(new RequirementsChecker);
805 // Released by OnRequirementsChecked.
807 requirements_checker_->Check(
809 base::Bind(&DeveloperPrivateEnableFunction::OnRequirementsChecked,
810 this, extension_id));
812 service->EnableExtension(extension_id);
814 // Make sure any browser action contained within it is not hidden.
815 ExtensionActionAPI::SetBrowserActionVisibility(
816 prefs, extension->id(), true);
819 service->DisableExtension(extension_id, Extension::DISABLE_USER_ACTION);
824 void DeveloperPrivateEnableFunction::OnRequirementsChecked(
825 std::string extension_id,
826 std::vector<std::string> requirements_errors) {
827 if (requirements_errors.empty()) {
828 ExtensionService* service = GetProfile()->GetExtensionService();
829 service->EnableExtension(extension_id);
831 ExtensionErrorReporter::GetInstance()->ReportError(
832 base::UTF8ToUTF16(JoinString(requirements_errors, ' ')),
833 true /* be noisy */);
838 DeveloperPrivateEnableFunction::~DeveloperPrivateEnableFunction() {}
840 bool DeveloperPrivateInspectFunction::RunImpl() {
841 scoped_ptr<developer::Inspect::Params> params(
842 developer::Inspect::Params::Create(*args_));
843 EXTENSION_FUNCTION_VALIDATE(params.get() != NULL);
844 const developer::InspectOptions& options = params->options;
846 int render_process_id;
847 base::StringToInt(options.render_process_id, &render_process_id);
849 if (render_process_id == -1) {
850 // This is a lazy background page. Identify if it is a normal
851 // or incognito background page.
852 ExtensionService* service = GetProfile()->GetExtensionService();
853 if (options.incognito)
854 service = ExtensionSystem::Get(
855 service->profile()->GetOffTheRecordProfile())->extension_service();
856 const Extension* extension = service->extensions()->GetByID(
857 options.extension_id);
859 // Wakes up the background page and opens the inspect window.
860 devtools_util::InspectBackgroundPage(extension, GetProfile());
865 base::StringToInt(options.render_view_id, &render_view_id);
866 content::RenderViewHost* host = content::RenderViewHost::FromID(
867 render_process_id, render_view_id);
870 // This can happen if the host has gone away since the page was displayed.
874 DevToolsWindow::OpenDevToolsWindow(host);
878 DeveloperPrivateInspectFunction::~DeveloperPrivateInspectFunction() {}
880 bool DeveloperPrivateLoadUnpackedFunction::RunImpl() {
881 base::string16 select_title =
882 l10n_util::GetStringUTF16(IDS_EXTENSION_LOAD_FROM_DIRECTORY);
884 // Balanced in FileSelected / FileSelectionCanceled.
886 bool result = ShowPicker(
887 ui::SelectFileDialog::SELECT_FOLDER,
888 DeveloperPrivateAPI::Get(GetProfile())->GetLastUnpackedDirectory(),
890 ui::SelectFileDialog::FileTypeInfo(),
895 void DeveloperPrivateLoadUnpackedFunction::FileSelected(
896 const base::FilePath& path) {
897 ExtensionService* service = GetProfile()->GetExtensionService();
898 UnpackedInstaller::Create(service)->Load(path);
899 DeveloperPrivateAPI::Get(GetProfile())->SetLastUnpackedDirectory(path);
904 void DeveloperPrivateLoadUnpackedFunction::FileSelectionCanceled() {
909 bool DeveloperPrivateChooseEntryFunction::ShowPicker(
910 ui::SelectFileDialog::Type picker_type,
911 const base::FilePath& last_directory,
912 const base::string16& select_title,
913 const ui::SelectFileDialog::FileTypeInfo& info,
914 int file_type_index) {
915 AppWindowRegistry* registry = AppWindowRegistry::Get(GetProfile());
917 AppWindow* app_window =
918 registry->GetAppWindowForRenderViewHost(render_view_host());
923 // The entry picker will hold a reference to this function instance,
924 // and subsequent sending of the function response) until the user has
925 // selected a file or cancelled the picker. At that point, the picker will
927 new EntryPicker(this,
928 app_window->web_contents(),
937 bool DeveloperPrivateChooseEntryFunction::RunImpl() { return false; }
939 DeveloperPrivateChooseEntryFunction::~DeveloperPrivateChooseEntryFunction() {}
941 void DeveloperPrivatePackDirectoryFunction::OnPackSuccess(
942 const base::FilePath& crx_file,
943 const base::FilePath& pem_file) {
944 developer::PackDirectoryResponse response;
945 response.message = base::UTF16ToUTF8(
946 PackExtensionJob::StandardSuccessMessage(crx_file, pem_file));
947 response.status = developer::PACK_STATUS_SUCCESS;
948 results_ = developer::PackDirectory::Results::Create(response);
953 void DeveloperPrivatePackDirectoryFunction::OnPackFailure(
954 const std::string& error,
955 ExtensionCreator::ErrorType error_type) {
956 developer::PackDirectoryResponse response;
957 response.message = error;
958 if (error_type == ExtensionCreator::kCRXExists) {
959 response.item_path = item_path_str_;
960 response.pem_path = key_path_str_;
961 response.override_flags = ExtensionCreator::kOverwriteCRX;
962 response.status = developer::PACK_STATUS_WARNING;
964 response.status = developer::PACK_STATUS_ERROR;
966 results_ = developer::PackDirectory::Results::Create(response);
971 bool DeveloperPrivatePackDirectoryFunction::RunImpl() {
972 scoped_ptr<PackDirectory::Params> params(
973 PackDirectory::Params::Create(*args_));
974 EXTENSION_FUNCTION_VALIDATE(params.get());
976 int flags = params->flags;
977 item_path_str_ = params->path;
978 key_path_str_ = params->private_key_path;
980 base::FilePath root_directory =
981 base::FilePath::FromUTF8Unsafe(item_path_str_);
983 base::FilePath key_file = base::FilePath::FromUTF8Unsafe(key_path_str_);
985 developer::PackDirectoryResponse response;
986 if (root_directory.empty()) {
987 if (item_path_str_.empty())
988 response.message = l10n_util::GetStringUTF8(
989 IDS_EXTENSION_PACK_DIALOG_ERROR_ROOT_REQUIRED);
991 response.message = l10n_util::GetStringUTF8(
992 IDS_EXTENSION_PACK_DIALOG_ERROR_ROOT_INVALID);
994 response.status = developer::PACK_STATUS_ERROR;
995 results_ = developer::PackDirectory::Results::Create(response);
1000 if (!key_path_str_.empty() && key_file.empty()) {
1001 response.message = l10n_util::GetStringUTF8(
1002 IDS_EXTENSION_PACK_DIALOG_ERROR_KEY_INVALID);
1003 response.status = developer::PACK_STATUS_ERROR;
1004 results_ = developer::PackDirectory::Results::Create(response);
1009 // Balanced in OnPackSuccess / OnPackFailure.
1012 pack_job_ = new PackExtensionJob(this, root_directory, key_file, flags);
1017 DeveloperPrivatePackDirectoryFunction::DeveloperPrivatePackDirectoryFunction()
1020 DeveloperPrivatePackDirectoryFunction::~DeveloperPrivatePackDirectoryFunction()
1023 DeveloperPrivateLoadUnpackedFunction::~DeveloperPrivateLoadUnpackedFunction() {}
1025 bool DeveloperPrivateLoadDirectoryFunction::RunImpl() {
1026 // TODO(grv) : add unittests.
1027 std::string directory_url_str;
1028 std::string filesystem_name;
1029 std::string filesystem_path;
1031 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &filesystem_name));
1032 EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_path));
1033 EXTENSION_FUNCTION_VALIDATE(args_->GetString(2, &directory_url_str));
1035 // Directory url is non empty only for syncfilesystem.
1036 if (directory_url_str != "") {
1038 context_ = content::BrowserContext::GetStoragePartition(
1039 GetProfile(), render_view_host()->GetSiteInstance())
1040 ->GetFileSystemContext();
1042 fileapi::FileSystemURL directory_url =
1043 context_->CrackURL(GURL(directory_url_str));
1045 if (!directory_url.is_valid() && directory_url.type() ==
1046 fileapi::kFileSystemTypeSyncable) {
1047 SetError("DirectoryEntry of unsupported filesystem.");
1052 // Parse the project directory name from the project url. The project url is
1053 // expected to have project name as the suffix.
1054 if ((pos = directory_url_str.rfind("/")) == std::string::npos) {
1055 SetError("Invalid Directory entry.");
1059 std::string project_name;
1060 project_name = directory_url_str.substr(pos + 1);
1061 project_base_url_ = directory_url_str.substr(0, pos + 1);
1063 base::FilePath project_path(GetProfile()->GetPath());
1064 project_path = project_path.Append(kUnpackedAppsFolder);
1065 project_path = project_path.Append(
1066 base::FilePath::FromUTF8Unsafe(project_name));
1068 project_base_path_ = project_path;
1070 content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE,
1071 base::Bind(&DeveloperPrivateLoadDirectoryFunction::
1072 ClearExistingDirectoryContent,
1074 project_base_path_));
1077 // Check if the DirecotryEntry is the instace of chrome filesystem..
1078 if (!app_file_handler_util::ValidateFileEntryAndGetPath(filesystem_name,
1081 &project_base_path_,
1091 void DeveloperPrivateLoadDirectoryFunction::Load() {
1093 ExtensionService* service = GetProfile()->GetExtensionService();
1094 UnpackedInstaller::Create(service)->Load(project_base_path_);
1096 // TODO(grv) : The unpacked installer should fire an event when complete
1097 // and return the extension_id.
1098 SetResult(new base::StringValue("-1"));
1102 void DeveloperPrivateLoadDirectoryFunction::ClearExistingDirectoryContent(
1103 const base::FilePath& project_path) {
1105 // Clear the project directory before copying new files.
1106 base::DeleteFile(project_path, true/*recursive*/);
1108 pending_copy_operations_count_ = 1;
1110 content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE,
1111 base::Bind(&DeveloperPrivateLoadDirectoryFunction::
1112 ReadSyncFileSystemDirectory,
1113 this, project_path, project_path.BaseName()));
1116 void DeveloperPrivateLoadDirectoryFunction::ReadSyncFileSystemDirectory(
1117 const base::FilePath& project_path,
1118 const base::FilePath& destination_path) {
1120 current_path_ = context_->CrackURL(GURL(project_base_url_)).path();
1122 GURL project_url = GURL(project_base_url_ + destination_path.MaybeAsASCII());
1124 fileapi::FileSystemURL url = context_->CrackURL(project_url);
1126 context_->operation_runner()->ReadDirectory(
1127 url, base::Bind(&DeveloperPrivateLoadDirectoryFunction::
1128 ReadSyncFileSystemDirectoryCb,
1129 this, project_path, destination_path));
1132 void DeveloperPrivateLoadDirectoryFunction::ReadSyncFileSystemDirectoryCb(
1133 const base::FilePath& project_path,
1134 const base::FilePath& destination_path,
1135 base::File::Error status,
1136 const fileapi::FileSystemOperation::FileEntryList& file_list,
1139 if (status != base::File::FILE_OK) {
1140 DLOG(ERROR) << "Error in copying files from sync filesystem.";
1144 // We add 1 to the pending copy operations for both files and directories. We
1145 // release the directory copy operation once all the files under the directory
1146 // are added for copying. We do that to ensure that pendingCopyOperationsCount
1147 // does not become zero before all copy operations are finished.
1148 // In case the directory happens to be executing the last copy operation it
1149 // will call SendResponse to send the response to the API. The pending copy
1150 // operations of files are released by the CopyFile function.
1151 pending_copy_operations_count_ += file_list.size();
1153 for (size_t i = 0; i < file_list.size(); ++i) {
1154 if (file_list[i].is_directory) {
1155 ReadSyncFileSystemDirectory(project_path.Append(file_list[i].name),
1156 destination_path.Append(file_list[i].name));
1160 std::string origin_url(
1161 Extension::GetBaseURLFromExtensionId(extension_id()).spec());
1162 fileapi::FileSystemURL url(sync_file_system::CreateSyncableFileSystemURL(
1164 current_path_.Append(destination_path.Append(file_list[i].name))));
1165 base::FilePath target_path = project_path;
1166 target_path = target_path.Append(file_list[i].name);
1168 context_->operation_runner()->CreateSnapshotFile(
1170 base::Bind(&DeveloperPrivateLoadDirectoryFunction::SnapshotFileCallback,
1176 // Directory copy operation released here.
1177 pending_copy_operations_count_--;
1179 if (!pending_copy_operations_count_) {
1180 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
1181 base::Bind(&DeveloperPrivateLoadDirectoryFunction::SendResponse,
1187 void DeveloperPrivateLoadDirectoryFunction::SnapshotFileCallback(
1188 const base::FilePath& target_path,
1189 base::File::Error result,
1190 const base::File::Info& file_info,
1191 const base::FilePath& src_path,
1192 const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref) {
1193 if (result != base::File::FILE_OK) {
1194 SetError("Error in copying files from sync filesystem.");
1199 content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE,
1200 base::Bind(&DeveloperPrivateLoadDirectoryFunction::CopyFile,
1206 void DeveloperPrivateLoadDirectoryFunction::CopyFile(
1207 const base::FilePath& src_path,
1208 const base::FilePath& target_path) {
1209 if (!base::CreateDirectory(target_path.DirName())) {
1210 SetError("Error in copying files from sync filesystem.");
1215 base::CopyFile(src_path, target_path);
1217 CHECK(pending_copy_operations_count_ > 0);
1218 pending_copy_operations_count_--;
1220 if (!pending_copy_operations_count_) {
1221 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
1222 base::Bind(&DeveloperPrivateLoadDirectoryFunction::Load,
1227 DeveloperPrivateLoadDirectoryFunction::DeveloperPrivateLoadDirectoryFunction()
1228 : pending_copy_operations_count_(0), success_(true) {}
1230 DeveloperPrivateLoadDirectoryFunction::~DeveloperPrivateLoadDirectoryFunction()
1233 bool DeveloperPrivateChoosePathFunction::RunImpl() {
1235 scoped_ptr<developer::ChoosePath::Params> params(
1236 developer::ChoosePath::Params::Create(*args_));
1237 EXTENSION_FUNCTION_VALIDATE(params.get() != NULL);
1239 ui::SelectFileDialog::Type type = ui::SelectFileDialog::SELECT_FOLDER;
1240 ui::SelectFileDialog::FileTypeInfo info;
1241 if (params->select_type == developer::SELECT_TYPE_FILE) {
1242 type = ui::SelectFileDialog::SELECT_OPEN_FILE;
1244 base::string16 select_title;
1246 int file_type_index = 0;
1247 if (params->file_type == developer::FILE_TYPE_LOAD)
1248 select_title = l10n_util::GetStringUTF16(IDS_EXTENSION_LOAD_FROM_DIRECTORY);
1249 else if (params->file_type== developer::FILE_TYPE_PEM) {
1250 select_title = l10n_util::GetStringUTF16(
1251 IDS_EXTENSION_PACK_DIALOG_SELECT_KEY);
1252 info.extensions.push_back(std::vector<base::FilePath::StringType>());
1253 info.extensions.front().push_back(FILE_PATH_LITERAL("pem"));
1254 info.extension_description_overrides.push_back(
1255 l10n_util::GetStringUTF16(
1256 IDS_EXTENSION_PACK_DIALOG_KEY_FILE_TYPE_DESCRIPTION));
1257 info.include_all_files = true;
1258 file_type_index = 1;
1263 // Balanced by FileSelected / FileSelectionCanceled.
1265 bool result = ShowPicker(
1267 DeveloperPrivateAPI::Get(GetProfile())->GetLastUnpackedDirectory(),
1274 void DeveloperPrivateChoosePathFunction::FileSelected(
1275 const base::FilePath& path) {
1276 SetResult(new base::StringValue(base::UTF16ToUTF8(path.LossyDisplayName())));
1281 void DeveloperPrivateChoosePathFunction::FileSelectionCanceled() {
1282 SendResponse(false);
1286 DeveloperPrivateChoosePathFunction::~DeveloperPrivateChoosePathFunction() {}
1288 bool DeveloperPrivateIsProfileManagedFunction::RunImpl() {
1289 SetResult(new base::FundamentalValue(GetProfile()->IsManaged()));
1293 DeveloperPrivateIsProfileManagedFunction::
1294 ~DeveloperPrivateIsProfileManagedFunction() {
1297 DeveloperPrivateRequestFileSourceFunction::
1298 DeveloperPrivateRequestFileSourceFunction() {}
1300 DeveloperPrivateRequestFileSourceFunction::
1301 ~DeveloperPrivateRequestFileSourceFunction() {}
1303 bool DeveloperPrivateRequestFileSourceFunction::RunImpl() {
1304 scoped_ptr<developer::RequestFileSource::Params> params(
1305 developer::RequestFileSource::Params::Create(*args_));
1306 EXTENSION_FUNCTION_VALIDATE(params.get() != NULL);
1308 base::DictionaryValue* dict = NULL;
1309 if (!params->dict->GetAsDictionary(&dict)) {
1314 AddRef(); // Balanced in LaunchCallback().
1315 error_ui_util::HandleRequestFileSource(
1318 base::Bind(&DeveloperPrivateRequestFileSourceFunction::LaunchCallback,
1319 base::Unretained(this)));
1323 void DeveloperPrivateRequestFileSourceFunction::LaunchCallback(
1324 const base::DictionaryValue& results) {
1325 SetResult(results.DeepCopy());
1327 Release(); // Balanced in RunImpl().
1330 DeveloperPrivateOpenDevToolsFunction::DeveloperPrivateOpenDevToolsFunction() {}
1331 DeveloperPrivateOpenDevToolsFunction::~DeveloperPrivateOpenDevToolsFunction() {}
1333 bool DeveloperPrivateOpenDevToolsFunction::RunImpl() {
1334 scoped_ptr<developer::OpenDevTools::Params> params(
1335 developer::OpenDevTools::Params::Create(*args_));
1336 EXTENSION_FUNCTION_VALIDATE(params.get() != NULL);
1338 base::DictionaryValue* dict = NULL;
1339 if (!params->dict->GetAsDictionary(&dict)) {
1344 error_ui_util::HandleOpenDevTools(dict);
1351 } // namespace extensions