Add ENABLE_MEDIA_ROUTER define to builds other than Android and iOS.
[chromium-blink-merge.git] / chrome / browser / extensions / api / developer_private / developer_private_api.cc
blobd89f9f0cf65fb8fabf9c9263855aa85d7fcdfab6
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 "base/base64.h"
8 #include "base/bind.h"
9 #include "base/files/file_util.h"
10 #include "base/lazy_instance.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/browser/devtools/devtools_window.h"
15 #include "chrome/browser/extensions/api/developer_private/developer_private_mangle.h"
16 #include "chrome/browser/extensions/api/developer_private/entry_picker.h"
17 #include "chrome/browser/extensions/api/developer_private/extension_info_generator.h"
18 #include "chrome/browser/extensions/api/developer_private/show_permissions_dialog_helper.h"
19 #include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
20 #include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h"
21 #include "chrome/browser/extensions/devtools_util.h"
22 #include "chrome/browser/extensions/extension_service.h"
23 #include "chrome/browser/extensions/extension_ui_util.h"
24 #include "chrome/browser/extensions/extension_util.h"
25 #include "chrome/browser/extensions/shared_module_service.h"
26 #include "chrome/browser/extensions/unpacked_installer.h"
27 #include "chrome/browser/extensions/updater/extension_updater.h"
28 #include "chrome/browser/profiles/profile.h"
29 #include "chrome/browser/ui/browser_finder.h"
30 #include "chrome/browser/ui/tabs/tab_strip_model.h"
31 #include "chrome/common/extensions/api/developer_private.h"
32 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
33 #include "chrome/common/url_constants.h"
34 #include "chrome/grit/generated_resources.h"
35 #include "content/public/browser/browser_thread.h"
36 #include "content/public/browser/notification_service.h"
37 #include "content/public/browser/render_process_host.h"
38 #include "content/public/browser/render_view_host.h"
39 #include "content/public/browser/site_instance.h"
40 #include "content/public/browser/storage_partition.h"
41 #include "content/public/browser/web_contents.h"
42 #include "extensions/browser/app_window/app_window.h"
43 #include "extensions/browser/app_window/app_window_registry.h"
44 #include "extensions/browser/error_map.h"
45 #include "extensions/browser/extension_error.h"
46 #include "extensions/browser/extension_prefs.h"
47 #include "extensions/browser/extension_registry.h"
48 #include "extensions/browser/extension_system.h"
49 #include "extensions/browser/file_highlighter.h"
50 #include "extensions/browser/management_policy.h"
51 #include "extensions/browser/notification_types.h"
52 #include "extensions/browser/warning_service.h"
53 #include "extensions/common/constants.h"
54 #include "extensions/common/extension_resource.h"
55 #include "extensions/common/extension_set.h"
56 #include "extensions/common/feature_switch.h"
57 #include "extensions/common/install_warning.h"
58 #include "extensions/common/manifest.h"
59 #include "extensions/common/manifest_handlers/icons_handler.h"
60 #include "extensions/common/permissions/permissions_data.h"
61 #include "extensions/grit/extensions_browser_resources.h"
62 #include "storage/browser/fileapi/external_mount_points.h"
63 #include "storage/browser/fileapi/file_system_context.h"
64 #include "storage/browser/fileapi/file_system_operation.h"
65 #include "storage/browser/fileapi/file_system_operation_runner.h"
66 #include "storage/browser/fileapi/isolated_context.h"
67 #include "ui/base/l10n/l10n_util.h"
68 #include "ui/base/resource/resource_bundle.h"
70 namespace extensions {
72 namespace developer_private = api::developer_private;
74 namespace {
76 const char kNoSuchExtensionError[] = "No such extension.";
77 const char kCannotModifyPolicyExtensionError[] =
78 "Cannot modify the extension by policy.";
79 const char kRequiresUserGestureError[] =
80 "This action requires a user gesture.";
81 const char kCouldNotShowSelectFileDialogError[] =
82 "Could not show a file chooser.";
83 const char kFileSelectionCanceled[] =
84 "File selection was canceled.";
85 const char kNoSuchRendererError[] = "No such renderer.";
86 const char kInvalidPathError[] = "Invalid path.";
87 const char kManifestKeyIsRequiredError[] =
88 "The 'manifestKey' argument is required for manifest files.";
89 const char kCouldNotFindWebContentsError[] =
90 "Could not find a valid web contents.";
92 const char kUnpackedAppsFolder[] = "apps_target";
93 const char kManifestFile[] = "manifest.json";
95 ExtensionService* GetExtensionService(content::BrowserContext* context) {
96 return ExtensionSystem::Get(context)->extension_service();
99 ExtensionUpdater* GetExtensionUpdater(Profile* profile) {
100 return GetExtensionService(profile)->updater();
103 GURL GetImageURLFromData(const std::string& contents) {
104 std::string contents_base64;
105 base::Base64Encode(contents, &contents_base64);
107 // TODO(dvh): make use of content::kDataScheme. Filed as crbug/297301.
108 const char kDataURLPrefix[] = "data:;base64,";
109 return GURL(kDataURLPrefix + contents_base64);
112 GURL GetDefaultImageURL(developer_private::ItemType type) {
113 int icon_resource_id;
114 switch (type) {
115 case developer::ITEM_TYPE_LEGACY_PACKAGED_APP:
116 case developer::ITEM_TYPE_HOSTED_APP:
117 case developer::ITEM_TYPE_PACKAGED_APP:
118 icon_resource_id = IDR_APP_DEFAULT_ICON;
119 break;
120 default:
121 icon_resource_id = IDR_EXTENSION_DEFAULT_ICON;
122 break;
125 return GetImageURLFromData(
126 ResourceBundle::GetSharedInstance().GetRawDataResourceForScale(
127 icon_resource_id, ui::SCALE_FACTOR_100P).as_string());
130 // TODO(dvh): This code should be refactored and moved to
131 // extensions::ImageLoader. Also a resize should be performed to avoid
132 // potential huge URLs: crbug/297298.
133 GURL ToDataURL(const base::FilePath& path, developer_private::ItemType type) {
134 std::string contents;
135 if (path.empty() || !base::ReadFileToString(path, &contents))
136 return GetDefaultImageURL(type);
138 return GetImageURLFromData(contents);
141 void BroadcastItemStateChanged(content::BrowserContext* browser_context,
142 developer::EventType event_type,
143 const std::string& item_id) {
144 developer::EventData event_data;
145 event_data.event_type = event_type;
146 event_data.item_id = item_id;
147 event_data.extension_info =
148 ExtensionInfoGenerator(browser_context).CreateExtensionInfo(item_id);
150 scoped_ptr<base::ListValue> args(new base::ListValue());
151 args->Append(event_data.ToValue().release());
152 scoped_ptr<Event> event(new Event(
153 developer_private::OnItemStateChanged::kEventName, args.Pass()));
154 EventRouter::Get(browser_context)->BroadcastEvent(event.Pass());
157 std::string ReadFileToString(const base::FilePath& path) {
158 std::string data;
159 ignore_result(base::ReadFileToString(path, &data));
160 return data;
163 bool UserCanModifyExtensionConfiguration(
164 const Extension* extension,
165 content::BrowserContext* browser_context,
166 std::string* error) {
167 ManagementPolicy* management_policy =
168 ExtensionSystem::Get(browser_context)->management_policy();
169 if (!management_policy->UserMayModifySettings(extension, nullptr)) {
170 LOG(ERROR) << "Attempt to change settings of an extension that is "
171 << "non-usermanagable was made. Extension id : "
172 << extension->id();
173 *error = kCannotModifyPolicyExtensionError;
174 return false;
177 return true;
180 } // namespace
182 namespace ChoosePath = api::developer_private::ChoosePath;
183 namespace GetItemsInfo = api::developer_private::GetItemsInfo;
184 namespace PackDirectory = api::developer_private::PackDirectory;
185 namespace Reload = api::developer_private::Reload;
187 static base::LazyInstance<BrowserContextKeyedAPIFactory<DeveloperPrivateAPI> >
188 g_factory = LAZY_INSTANCE_INITIALIZER;
190 // static
191 BrowserContextKeyedAPIFactory<DeveloperPrivateAPI>*
192 DeveloperPrivateAPI::GetFactoryInstance() {
193 return g_factory.Pointer();
196 // static
197 DeveloperPrivateAPI* DeveloperPrivateAPI::Get(
198 content::BrowserContext* context) {
199 return GetFactoryInstance()->Get(context);
202 DeveloperPrivateAPI::DeveloperPrivateAPI(content::BrowserContext* context)
203 : profile_(Profile::FromBrowserContext(context)) {
204 RegisterNotifications();
207 DeveloperPrivateEventRouter::DeveloperPrivateEventRouter(Profile* profile)
208 : extension_registry_observer_(this),
209 error_console_observer_(this),
210 process_manager_observer_(this),
211 profile_(profile) {
212 extension_registry_observer_.Add(ExtensionRegistry::Get(profile_));
213 error_console_observer_.Add(ErrorConsole::Get(profile));
214 process_manager_observer_.Add(ProcessManager::Get(profile));
217 DeveloperPrivateEventRouter::~DeveloperPrivateEventRouter() {
220 void DeveloperPrivateEventRouter::AddExtensionId(
221 const std::string& extension_id) {
222 extension_ids_.insert(extension_id);
225 void DeveloperPrivateEventRouter::RemoveExtensionId(
226 const std::string& extension_id) {
227 extension_ids_.erase(extension_id);
230 void DeveloperPrivateEventRouter::OnExtensionLoaded(
231 content::BrowserContext* browser_context,
232 const Extension* extension) {
233 DCHECK(profile_->IsSameProfile(Profile::FromBrowserContext(browser_context)));
234 BroadcastItemStateChanged(
235 browser_context, developer::EVENT_TYPE_LOADED, extension->id());
238 void DeveloperPrivateEventRouter::OnExtensionUnloaded(
239 content::BrowserContext* browser_context,
240 const Extension* extension,
241 UnloadedExtensionInfo::Reason reason) {
242 DCHECK(profile_->IsSameProfile(Profile::FromBrowserContext(browser_context)));
243 BroadcastItemStateChanged(
244 browser_context, developer::EVENT_TYPE_UNLOADED, extension->id());
247 void DeveloperPrivateEventRouter::OnExtensionWillBeInstalled(
248 content::BrowserContext* browser_context,
249 const Extension* extension,
250 bool is_update,
251 bool from_ephemeral,
252 const std::string& old_name) {
253 DCHECK(profile_->IsSameProfile(Profile::FromBrowserContext(browser_context)));
254 BroadcastItemStateChanged(
255 browser_context, developer::EVENT_TYPE_INSTALLED, extension->id());
258 void DeveloperPrivateEventRouter::OnExtensionUninstalled(
259 content::BrowserContext* browser_context,
260 const Extension* extension,
261 extensions::UninstallReason reason) {
262 DCHECK(profile_->IsSameProfile(Profile::FromBrowserContext(browser_context)));
263 BroadcastItemStateChanged(
264 browser_context, developer::EVENT_TYPE_UNINSTALLED, extension->id());
267 void DeveloperPrivateEventRouter::OnErrorAdded(const ExtensionError* error) {
268 // We don't want to handle errors thrown by extensions subscribed to these
269 // events (currently only the Apps Developer Tool), because doing so risks
270 // entering a loop.
271 if (extension_ids_.find(error->extension_id()) != extension_ids_.end())
272 return;
274 BroadcastItemStateChanged(
275 profile_, developer::EVENT_TYPE_ERROR_ADDED, error->extension_id());
278 void DeveloperPrivateEventRouter::OnExtensionFrameRegistered(
279 const std::string& extension_id,
280 content::RenderFrameHost* render_frame_host) {
281 BroadcastItemStateChanged(
282 profile_, developer::EVENT_TYPE_VIEW_REGISTERED, extension_id);
285 void DeveloperPrivateEventRouter::OnExtensionFrameUnregistered(
286 const std::string& extension_id,
287 content::RenderFrameHost* render_frame_host) {
288 BroadcastItemStateChanged(
289 profile_, developer::EVENT_TYPE_VIEW_UNREGISTERED, extension_id);
292 void DeveloperPrivateAPI::SetLastUnpackedDirectory(const base::FilePath& path) {
293 last_unpacked_directory_ = path;
296 void DeveloperPrivateAPI::RegisterNotifications() {
297 EventRouter::Get(profile_)->RegisterObserver(
298 this, developer_private::OnItemStateChanged::kEventName);
301 DeveloperPrivateAPI::~DeveloperPrivateAPI() {}
303 void DeveloperPrivateAPI::Shutdown() {}
305 void DeveloperPrivateAPI::OnListenerAdded(
306 const EventListenerInfo& details) {
307 if (!developer_private_event_router_) {
308 developer_private_event_router_.reset(
309 new DeveloperPrivateEventRouter(profile_));
312 developer_private_event_router_->AddExtensionId(details.extension_id);
315 void DeveloperPrivateAPI::OnListenerRemoved(
316 const EventListenerInfo& details) {
317 if (!EventRouter::Get(profile_)->HasEventListener(
318 developer_private::OnItemStateChanged::kEventName)) {
319 developer_private_event_router_.reset(NULL);
320 } else {
321 developer_private_event_router_->RemoveExtensionId(details.extension_id);
325 namespace api {
327 DeveloperPrivateAPIFunction::~DeveloperPrivateAPIFunction() {
330 const Extension* DeveloperPrivateAPIFunction::GetExtensionById(
331 const std::string& id) {
332 return ExtensionRegistry::Get(browser_context())->GetExtensionById(
333 id, ExtensionRegistry::EVERYTHING);
336 bool DeveloperPrivateAutoUpdateFunction::RunSync() {
337 ExtensionUpdater* updater = GetExtensionUpdater(GetProfile());
338 if (updater)
339 updater->CheckNow(ExtensionUpdater::CheckParams());
340 SetResult(new base::FundamentalValue(true));
341 return true;
344 DeveloperPrivateAutoUpdateFunction::~DeveloperPrivateAutoUpdateFunction() {}
346 DeveloperPrivateGetExtensionsInfoFunction::
347 ~DeveloperPrivateGetExtensionsInfoFunction() {
350 ExtensionFunction::ResponseAction
351 DeveloperPrivateGetExtensionsInfoFunction::Run() {
352 scoped_ptr<developer::GetExtensionsInfo::Params> params(
353 developer::GetExtensionsInfo::Params::Create(*args_));
354 EXTENSION_FUNCTION_VALIDATE(params);
356 bool include_disabled = true;
357 bool include_terminated = true;
358 if (params->options) {
359 if (params->options->include_disabled)
360 include_disabled = *params->options->include_disabled;
361 if (params->options->include_terminated)
362 include_terminated = *params->options->include_terminated;
365 std::vector<linked_ptr<developer::ExtensionInfo>> list =
366 ExtensionInfoGenerator(browser_context()).
367 CreateExtensionsInfo(include_disabled, include_terminated);
369 return RespondNow(ArgumentList(
370 developer::GetExtensionsInfo::Results::Create(list)));
373 DeveloperPrivateGetExtensionInfoFunction::
374 ~DeveloperPrivateGetExtensionInfoFunction() {
377 ExtensionFunction::ResponseAction
378 DeveloperPrivateGetExtensionInfoFunction::Run() {
379 scoped_ptr<developer::GetExtensionInfo::Params> params(
380 developer::GetExtensionInfo::Params::Create(*args_));
381 EXTENSION_FUNCTION_VALIDATE(params);
383 scoped_ptr<developer::ExtensionInfo> info =
384 ExtensionInfoGenerator(browser_context()).CreateExtensionInfo(params->id);
386 if (!info)
387 return RespondNow(Error(kNoSuchExtensionError));
389 return RespondNow(OneArgument(info->ToValue().release()));
392 DeveloperPrivateGetItemsInfoFunction::DeveloperPrivateGetItemsInfoFunction() {}
393 DeveloperPrivateGetItemsInfoFunction::~DeveloperPrivateGetItemsInfoFunction() {}
395 ExtensionFunction::ResponseAction DeveloperPrivateGetItemsInfoFunction::Run() {
396 scoped_ptr<developer::GetItemsInfo::Params> params(
397 developer::GetItemsInfo::Params::Create(*args_));
398 EXTENSION_FUNCTION_VALIDATE(params);
400 ExtensionSet items;
401 ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context());
402 items.InsertAll(registry->enabled_extensions());
404 if (params->include_disabled)
405 items.InsertAll(registry->disabled_extensions());
406 if (params->include_terminated)
407 items.InsertAll(registry->terminated_extensions());
409 std::map<std::string, ExtensionResource> resource_map;
410 for (const scoped_refptr<const Extension>& item : items) {
411 // Don't show component extensions and invisible apps.
412 if (ui_util::ShouldDisplayInExtensionSettings(item.get(),
413 browser_context())) {
414 resource_map[item->id()] =
415 IconsInfo::GetIconResource(item.get(),
416 extension_misc::EXTENSION_ICON_MEDIUM,
417 ExtensionIconSet::MATCH_BIGGER);
421 std::vector<linked_ptr<developer::ExtensionInfo>> list =
422 ExtensionInfoGenerator(browser_context()).
423 CreateExtensionsInfo(params->include_disabled,
424 params->include_terminated);
426 for (const linked_ptr<developer::ExtensionInfo>& info : list)
427 item_list_.push_back(developer_private_mangle::MangleExtensionInfo(*info));
429 content::BrowserThread::PostTask(
430 content::BrowserThread::FILE,
431 FROM_HERE,
432 base::Bind(&DeveloperPrivateGetItemsInfoFunction::GetIconsOnFileThread,
433 this,
434 resource_map));
436 return RespondLater();
439 void DeveloperPrivateGetItemsInfoFunction::GetIconsOnFileThread(
440 const std::map<std::string, ExtensionResource> resource_map) {
441 for (const linked_ptr<developer::ItemInfo>& item : item_list_) {
442 auto resource = resource_map.find(item->id);
443 if (resource != resource_map.end()) {
444 item->icon_url = ToDataURL(resource->second.GetFilePath(),
445 item->type).spec();
449 content::BrowserThread::PostTask(
450 content::BrowserThread::UI,
451 FROM_HERE,
452 base::Bind(&DeveloperPrivateGetItemsInfoFunction::Finish, this));
455 void DeveloperPrivateGetItemsInfoFunction::Finish() {
456 Respond(ArgumentList(developer::GetItemsInfo::Results::Create(item_list_)));
459 DeveloperPrivateUpdateExtensionConfigurationFunction::
460 ~DeveloperPrivateUpdateExtensionConfigurationFunction() {}
462 ExtensionFunction::ResponseAction
463 DeveloperPrivateUpdateExtensionConfigurationFunction::Run() {
464 scoped_ptr<developer::UpdateExtensionConfiguration::Params> params(
465 developer::UpdateExtensionConfiguration::Params::Create(*args_));
466 EXTENSION_FUNCTION_VALIDATE(params);
468 const developer::ExtensionConfigurationUpdate& update = params->update;
470 const Extension* extension = GetExtensionById(update.extension_id);
471 if (!extension)
472 return RespondNow(Error(kNoSuchExtensionError));
473 if (!user_gesture())
474 return RespondNow(Error(kRequiresUserGestureError));
476 if (update.file_access) {
477 std::string error;
478 if (!UserCanModifyExtensionConfiguration(extension,
479 browser_context(),
480 &error)) {
481 return RespondNow(Error(error));
483 util::SetAllowFileAccess(
484 extension->id(), browser_context(), *update.file_access);
486 if (update.incognito_access) {
487 util::SetIsIncognitoEnabled(
488 extension->id(), browser_context(), *update.incognito_access);
490 if (update.error_collection) {
491 ErrorConsole::Get(browser_context())->SetReportingAllForExtension(
492 extension->id(), *update.error_collection);
494 if (update.run_on_all_urls) {
495 util::SetAllowedScriptingOnAllUrls(
496 extension->id(), browser_context(), *update.run_on_all_urls);
498 if (update.show_action_button) {
499 ExtensionActionAPI::SetBrowserActionVisibility(
500 ExtensionPrefs::Get(browser_context()),
501 extension->id(),
502 *update.show_action_button);
505 return RespondNow(NoArguments());
508 DeveloperPrivateReloadFunction::~DeveloperPrivateReloadFunction() {}
510 ExtensionFunction::ResponseAction DeveloperPrivateReloadFunction::Run() {
511 scoped_ptr<Reload::Params> params(Reload::Params::Create(*args_));
512 EXTENSION_FUNCTION_VALIDATE(params.get());
514 const Extension* extension = GetExtensionById(params->extension_id);
515 if (!extension)
516 return RespondNow(Error(kNoSuchExtensionError));
518 bool fail_quietly = params->options &&
519 params->options->fail_quietly &&
520 *params->options->fail_quietly;
522 ExtensionService* service = GetExtensionService(browser_context());
523 if (fail_quietly)
524 service->ReloadExtensionWithQuietFailure(params->extension_id);
525 else
526 service->ReloadExtension(params->extension_id);
528 // TODO(devlin): We shouldn't return until the extension has finished trying
529 // to reload (and then we could also return the error).
530 return RespondNow(NoArguments());
533 DeveloperPrivateShowPermissionsDialogFunction::
534 DeveloperPrivateShowPermissionsDialogFunction() {}
536 DeveloperPrivateShowPermissionsDialogFunction::
537 ~DeveloperPrivateShowPermissionsDialogFunction() {}
539 ExtensionFunction::ResponseAction
540 DeveloperPrivateShowPermissionsDialogFunction::Run() {
541 scoped_ptr<developer::ShowPermissionsDialog::Params> params(
542 developer::ShowPermissionsDialog::Params::Create(*args_));
543 EXTENSION_FUNCTION_VALIDATE(params);
545 const Extension* target_extension = GetExtensionById(params->extension_id);
546 if (!target_extension)
547 return RespondNow(Error(kNoSuchExtensionError));
549 content::WebContents* web_contents = GetSenderWebContents();
550 if (!web_contents)
551 return RespondNow(Error(kCouldNotFindWebContentsError));
553 ShowPermissionsDialogHelper::Show(
554 browser_context(),
555 web_contents,
556 target_extension,
557 source_context_type() == Feature::WEBUI_CONTEXT,
558 base::Bind(&DeveloperPrivateShowPermissionsDialogFunction::Finish, this));
559 return RespondLater();
562 void DeveloperPrivateShowPermissionsDialogFunction::Finish() {
563 Respond(NoArguments());
566 DeveloperPrivateLoadUnpackedFunction::DeveloperPrivateLoadUnpackedFunction()
567 : fail_quietly_(false) {
570 ExtensionFunction::ResponseAction DeveloperPrivateLoadUnpackedFunction::Run() {
571 scoped_ptr<developer_private::LoadUnpacked::Params> params(
572 developer_private::LoadUnpacked::Params::Create(*args_));
573 EXTENSION_FUNCTION_VALIDATE(params);
575 if (!ShowPicker(
576 ui::SelectFileDialog::SELECT_FOLDER,
577 l10n_util::GetStringUTF16(IDS_EXTENSION_LOAD_FROM_DIRECTORY),
578 ui::SelectFileDialog::FileTypeInfo(),
579 0 /* file_type_index */)) {
580 return RespondNow(Error(kCouldNotShowSelectFileDialogError));
583 fail_quietly_ = params->options &&
584 params->options->fail_quietly &&
585 *params->options->fail_quietly;
587 AddRef(); // Balanced in FileSelected / FileSelectionCanceled.
588 return RespondLater();
591 void DeveloperPrivateLoadUnpackedFunction::FileSelected(
592 const base::FilePath& path) {
593 scoped_refptr<UnpackedInstaller> installer(
594 UnpackedInstaller::Create(GetExtensionService(browser_context())));
595 installer->set_be_noisy_on_failure(!fail_quietly_);
596 installer->set_completion_callback(
597 base::Bind(&DeveloperPrivateLoadUnpackedFunction::OnLoadComplete, this));
598 installer->Load(path);
600 DeveloperPrivateAPI::Get(browser_context())->SetLastUnpackedDirectory(path);
602 Release(); // Balanced in Run().
605 void DeveloperPrivateLoadUnpackedFunction::FileSelectionCanceled() {
606 // This isn't really an error, but we should keep it like this for
607 // backward compatability.
608 Respond(Error(kFileSelectionCanceled));
609 Release(); // Balanced in Run().
612 void DeveloperPrivateLoadUnpackedFunction::OnLoadComplete(
613 const Extension* extension,
614 const base::FilePath& file_path,
615 const std::string& error) {
616 Respond(extension ? NoArguments() : Error(error));
619 bool DeveloperPrivateChooseEntryFunction::ShowPicker(
620 ui::SelectFileDialog::Type picker_type,
621 const base::string16& select_title,
622 const ui::SelectFileDialog::FileTypeInfo& info,
623 int file_type_index) {
624 content::WebContents* web_contents = GetSenderWebContents();
625 if (!web_contents)
626 return false;
628 // The entry picker will hold a reference to this function instance,
629 // and subsequent sending of the function response) until the user has
630 // selected a file or cancelled the picker. At that point, the picker will
631 // delete itself.
632 new EntryPicker(this,
633 web_contents,
634 picker_type,
635 DeveloperPrivateAPI::Get(browser_context())->
636 GetLastUnpackedDirectory(),
637 select_title,
638 info,
639 file_type_index);
640 return true;
643 DeveloperPrivateChooseEntryFunction::~DeveloperPrivateChooseEntryFunction() {}
645 void DeveloperPrivatePackDirectoryFunction::OnPackSuccess(
646 const base::FilePath& crx_file,
647 const base::FilePath& pem_file) {
648 developer::PackDirectoryResponse response;
649 response.message = base::UTF16ToUTF8(
650 PackExtensionJob::StandardSuccessMessage(crx_file, pem_file));
651 response.status = developer::PACK_STATUS_SUCCESS;
652 Respond(OneArgument(response.ToValue().release()));
653 Release(); // Balanced in Run().
656 void DeveloperPrivatePackDirectoryFunction::OnPackFailure(
657 const std::string& error,
658 ExtensionCreator::ErrorType error_type) {
659 developer::PackDirectoryResponse response;
660 response.message = error;
661 if (error_type == ExtensionCreator::kCRXExists) {
662 response.item_path = item_path_str_;
663 response.pem_path = key_path_str_;
664 response.override_flags = ExtensionCreator::kOverwriteCRX;
665 response.status = developer::PACK_STATUS_WARNING;
666 } else {
667 response.status = developer::PACK_STATUS_ERROR;
669 Respond(OneArgument(response.ToValue().release()));
670 Release(); // Balanced in Run().
673 ExtensionFunction::ResponseAction DeveloperPrivatePackDirectoryFunction::Run() {
674 scoped_ptr<PackDirectory::Params> params(
675 PackDirectory::Params::Create(*args_));
676 EXTENSION_FUNCTION_VALIDATE(params);
678 int flags = params->flags ? *params->flags : 0;
679 item_path_str_ = params->path;
680 if (params->private_key_path)
681 key_path_str_ = *params->private_key_path;
683 base::FilePath root_directory =
684 base::FilePath::FromUTF8Unsafe(item_path_str_);
685 base::FilePath key_file = base::FilePath::FromUTF8Unsafe(key_path_str_);
687 developer::PackDirectoryResponse response;
688 if (root_directory.empty()) {
689 if (item_path_str_.empty())
690 response.message = l10n_util::GetStringUTF8(
691 IDS_EXTENSION_PACK_DIALOG_ERROR_ROOT_REQUIRED);
692 else
693 response.message = l10n_util::GetStringUTF8(
694 IDS_EXTENSION_PACK_DIALOG_ERROR_ROOT_INVALID);
696 response.status = developer::PACK_STATUS_ERROR;
697 return RespondNow(OneArgument(response.ToValue().release()));
700 if (!key_path_str_.empty() && key_file.empty()) {
701 response.message = l10n_util::GetStringUTF8(
702 IDS_EXTENSION_PACK_DIALOG_ERROR_KEY_INVALID);
703 response.status = developer::PACK_STATUS_ERROR;
704 return RespondNow(OneArgument(response.ToValue().release()));
707 AddRef(); // Balanced in OnPackSuccess / OnPackFailure.
709 // TODO(devlin): Why is PackExtensionJob ref-counted?
710 pack_job_ = new PackExtensionJob(this, root_directory, key_file, flags);
711 pack_job_->Start();
712 return RespondLater();
715 DeveloperPrivatePackDirectoryFunction::DeveloperPrivatePackDirectoryFunction() {
718 DeveloperPrivatePackDirectoryFunction::
719 ~DeveloperPrivatePackDirectoryFunction() {}
721 DeveloperPrivateLoadUnpackedFunction::~DeveloperPrivateLoadUnpackedFunction() {}
723 bool DeveloperPrivateLoadDirectoryFunction::RunAsync() {
724 // TODO(grv) : add unittests.
725 std::string directory_url_str;
726 std::string filesystem_name;
727 std::string filesystem_path;
729 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &filesystem_name));
730 EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_path));
731 EXTENSION_FUNCTION_VALIDATE(args_->GetString(2, &directory_url_str));
733 context_ = content::BrowserContext::GetStoragePartition(
734 GetProfile(), render_view_host()->GetSiteInstance())
735 ->GetFileSystemContext();
737 // Directory url is non empty only for syncfilesystem.
738 if (!directory_url_str.empty()) {
739 storage::FileSystemURL directory_url =
740 context_->CrackURL(GURL(directory_url_str));
741 if (!directory_url.is_valid() ||
742 directory_url.type() != storage::kFileSystemTypeSyncable) {
743 SetError("DirectoryEntry of unsupported filesystem.");
744 return false;
746 return LoadByFileSystemAPI(directory_url);
747 } else {
748 // Check if the DirecotryEntry is the instance of chrome filesystem.
749 if (!app_file_handler_util::ValidateFileEntryAndGetPath(filesystem_name,
750 filesystem_path,
751 render_view_host_,
752 &project_base_path_,
753 &error_)) {
754 SetError("DirectoryEntry of unsupported filesystem.");
755 return false;
758 // Try to load using the FileSystem API backend, in case the filesystem
759 // points to a non-native local directory.
760 std::string filesystem_id;
761 bool cracked =
762 storage::CrackIsolatedFileSystemName(filesystem_name, &filesystem_id);
763 CHECK(cracked);
764 base::FilePath virtual_path =
765 storage::IsolatedContext::GetInstance()
766 ->CreateVirtualRootPath(filesystem_id)
767 .Append(base::FilePath::FromUTF8Unsafe(filesystem_path));
768 storage::FileSystemURL directory_url = context_->CreateCrackedFileSystemURL(
769 extensions::Extension::GetBaseURLFromExtensionId(extension_id()),
770 storage::kFileSystemTypeIsolated,
771 virtual_path);
773 if (directory_url.is_valid() &&
774 directory_url.type() != storage::kFileSystemTypeNativeLocal &&
775 directory_url.type() != storage::kFileSystemTypeRestrictedNativeLocal &&
776 directory_url.type() != storage::kFileSystemTypeDragged) {
777 return LoadByFileSystemAPI(directory_url);
780 Load();
783 return true;
786 bool DeveloperPrivateLoadDirectoryFunction::LoadByFileSystemAPI(
787 const storage::FileSystemURL& directory_url) {
788 std::string directory_url_str = directory_url.ToGURL().spec();
790 size_t pos = 0;
791 // Parse the project directory name from the project url. The project url is
792 // expected to have project name as the suffix.
793 if ((pos = directory_url_str.rfind("/")) == std::string::npos) {
794 SetError("Invalid Directory entry.");
795 return false;
798 std::string project_name;
799 project_name = directory_url_str.substr(pos + 1);
800 project_base_url_ = directory_url_str.substr(0, pos + 1);
802 base::FilePath project_path(GetProfile()->GetPath());
803 project_path = project_path.AppendASCII(kUnpackedAppsFolder);
804 project_path = project_path.Append(
805 base::FilePath::FromUTF8Unsafe(project_name));
807 project_base_path_ = project_path;
809 content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE,
810 base::Bind(&DeveloperPrivateLoadDirectoryFunction::
811 ClearExistingDirectoryContent,
812 this,
813 project_base_path_));
814 return true;
817 void DeveloperPrivateLoadDirectoryFunction::Load() {
818 ExtensionService* service = GetExtensionService(GetProfile());
819 UnpackedInstaller::Create(service)->Load(project_base_path_);
821 // TODO(grv) : The unpacked installer should fire an event when complete
822 // and return the extension_id.
823 SetResult(new base::StringValue("-1"));
824 SendResponse(true);
827 void DeveloperPrivateLoadDirectoryFunction::ClearExistingDirectoryContent(
828 const base::FilePath& project_path) {
830 // Clear the project directory before copying new files.
831 base::DeleteFile(project_path, true /*recursive*/);
833 pending_copy_operations_count_ = 1;
835 content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE,
836 base::Bind(&DeveloperPrivateLoadDirectoryFunction::
837 ReadDirectoryByFileSystemAPI,
838 this, project_path, project_path.BaseName()));
841 void DeveloperPrivateLoadDirectoryFunction::ReadDirectoryByFileSystemAPI(
842 const base::FilePath& project_path,
843 const base::FilePath& destination_path) {
844 GURL project_url = GURL(project_base_url_ + destination_path.AsUTF8Unsafe());
845 storage::FileSystemURL url = context_->CrackURL(project_url);
847 context_->operation_runner()->ReadDirectory(
848 url, base::Bind(&DeveloperPrivateLoadDirectoryFunction::
849 ReadDirectoryByFileSystemAPICb,
850 this, project_path, destination_path));
853 void DeveloperPrivateLoadDirectoryFunction::ReadDirectoryByFileSystemAPICb(
854 const base::FilePath& project_path,
855 const base::FilePath& destination_path,
856 base::File::Error status,
857 const storage::FileSystemOperation::FileEntryList& file_list,
858 bool has_more) {
859 if (status != base::File::FILE_OK) {
860 DLOG(ERROR) << "Error in copying files from sync filesystem.";
861 return;
864 // We add 1 to the pending copy operations for both files and directories. We
865 // release the directory copy operation once all the files under the directory
866 // are added for copying. We do that to ensure that pendingCopyOperationsCount
867 // does not become zero before all copy operations are finished.
868 // In case the directory happens to be executing the last copy operation it
869 // will call SendResponse to send the response to the API. The pending copy
870 // operations of files are released by the CopyFile function.
871 pending_copy_operations_count_ += file_list.size();
873 for (size_t i = 0; i < file_list.size(); ++i) {
874 if (file_list[i].is_directory) {
875 ReadDirectoryByFileSystemAPI(project_path.Append(file_list[i].name),
876 destination_path.Append(file_list[i].name));
877 continue;
880 GURL project_url = GURL(project_base_url_ +
881 destination_path.Append(file_list[i].name).AsUTF8Unsafe());
882 storage::FileSystemURL url = context_->CrackURL(project_url);
884 base::FilePath target_path = project_path;
885 target_path = target_path.Append(file_list[i].name);
887 context_->operation_runner()->CreateSnapshotFile(
888 url,
889 base::Bind(&DeveloperPrivateLoadDirectoryFunction::SnapshotFileCallback,
890 this,
891 target_path));
894 if (!has_more) {
895 // Directory copy operation released here.
896 pending_copy_operations_count_--;
898 if (!pending_copy_operations_count_) {
899 content::BrowserThread::PostTask(
900 content::BrowserThread::UI, FROM_HERE,
901 base::Bind(&DeveloperPrivateLoadDirectoryFunction::SendResponse,
902 this,
903 success_));
908 void DeveloperPrivateLoadDirectoryFunction::SnapshotFileCallback(
909 const base::FilePath& target_path,
910 base::File::Error result,
911 const base::File::Info& file_info,
912 const base::FilePath& src_path,
913 const scoped_refptr<storage::ShareableFileReference>& file_ref) {
914 if (result != base::File::FILE_OK) {
915 SetError("Error in copying files from sync filesystem.");
916 success_ = false;
917 return;
920 content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE,
921 base::Bind(&DeveloperPrivateLoadDirectoryFunction::CopyFile,
922 this,
923 src_path,
924 target_path));
927 void DeveloperPrivateLoadDirectoryFunction::CopyFile(
928 const base::FilePath& src_path,
929 const base::FilePath& target_path) {
930 if (!base::CreateDirectory(target_path.DirName())) {
931 SetError("Error in copying files from sync filesystem.");
932 success_ = false;
935 if (success_)
936 base::CopyFile(src_path, target_path);
938 CHECK(pending_copy_operations_count_ > 0);
939 pending_copy_operations_count_--;
941 if (!pending_copy_operations_count_) {
942 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
943 base::Bind(&DeveloperPrivateLoadDirectoryFunction::Load,
944 this));
948 DeveloperPrivateLoadDirectoryFunction::DeveloperPrivateLoadDirectoryFunction()
949 : pending_copy_operations_count_(0), success_(true) {}
951 DeveloperPrivateLoadDirectoryFunction::~DeveloperPrivateLoadDirectoryFunction()
954 ExtensionFunction::ResponseAction DeveloperPrivateChoosePathFunction::Run() {
955 scoped_ptr<developer::ChoosePath::Params> params(
956 developer::ChoosePath::Params::Create(*args_));
957 EXTENSION_FUNCTION_VALIDATE(params);
959 ui::SelectFileDialog::Type type = ui::SelectFileDialog::SELECT_FOLDER;
960 ui::SelectFileDialog::FileTypeInfo info;
962 if (params->select_type == developer::SELECT_TYPE_FILE)
963 type = ui::SelectFileDialog::SELECT_OPEN_FILE;
964 base::string16 select_title;
966 int file_type_index = 0;
967 if (params->file_type == developer::FILE_TYPE_LOAD) {
968 select_title = l10n_util::GetStringUTF16(IDS_EXTENSION_LOAD_FROM_DIRECTORY);
969 } else if (params->file_type == developer::FILE_TYPE_PEM) {
970 select_title = l10n_util::GetStringUTF16(
971 IDS_EXTENSION_PACK_DIALOG_SELECT_KEY);
972 info.extensions.push_back(std::vector<base::FilePath::StringType>(
973 1, FILE_PATH_LITERAL("pem")));
974 info.extension_description_overrides.push_back(
975 l10n_util::GetStringUTF16(
976 IDS_EXTENSION_PACK_DIALOG_KEY_FILE_TYPE_DESCRIPTION));
977 info.include_all_files = true;
978 file_type_index = 1;
979 } else {
980 NOTREACHED();
983 if (!ShowPicker(
984 type,
985 select_title,
986 info,
987 file_type_index)) {
988 return RespondNow(Error(kCouldNotShowSelectFileDialogError));
991 AddRef(); // Balanced by FileSelected / FileSelectionCanceled.
992 return RespondLater();
995 void DeveloperPrivateChoosePathFunction::FileSelected(
996 const base::FilePath& path) {
997 Respond(OneArgument(new base::StringValue(path.LossyDisplayName())));
998 Release();
1001 void DeveloperPrivateChoosePathFunction::FileSelectionCanceled() {
1002 // This isn't really an error, but we should keep it like this for
1003 // backward compatability.
1004 Respond(Error(kFileSelectionCanceled));
1005 Release();
1008 DeveloperPrivateChoosePathFunction::~DeveloperPrivateChoosePathFunction() {}
1010 bool DeveloperPrivateIsProfileManagedFunction::RunSync() {
1011 SetResult(new base::FundamentalValue(GetProfile()->IsSupervised()));
1012 return true;
1015 DeveloperPrivateIsProfileManagedFunction::
1016 ~DeveloperPrivateIsProfileManagedFunction() {
1019 DeveloperPrivateRequestFileSourceFunction::
1020 DeveloperPrivateRequestFileSourceFunction() {}
1022 DeveloperPrivateRequestFileSourceFunction::
1023 ~DeveloperPrivateRequestFileSourceFunction() {}
1025 ExtensionFunction::ResponseAction
1026 DeveloperPrivateRequestFileSourceFunction::Run() {
1027 params_ = developer::RequestFileSource::Params::Create(*args_);
1028 EXTENSION_FUNCTION_VALIDATE(params_);
1030 const developer::RequestFileSourceProperties& properties =
1031 params_->properties;
1032 const Extension* extension = GetExtensionById(properties.extension_id);
1033 if (!extension)
1034 return RespondNow(Error(kNoSuchExtensionError));
1036 // Under no circumstances should we ever need to reference a file outside of
1037 // the extension's directory. If it tries to, abort.
1038 base::FilePath path_suffix =
1039 base::FilePath::FromUTF8Unsafe(properties.path_suffix);
1040 if (path_suffix.empty() || path_suffix.ReferencesParent())
1041 return RespondNow(Error(kInvalidPathError));
1043 if (properties.path_suffix == kManifestFile && !properties.manifest_key)
1044 return RespondNow(Error(kManifestKeyIsRequiredError));
1046 base::PostTaskAndReplyWithResult(
1047 content::BrowserThread::GetBlockingPool(),
1048 FROM_HERE,
1049 base::Bind(&ReadFileToString, extension->path().Append(path_suffix)),
1050 base::Bind(&DeveloperPrivateRequestFileSourceFunction::Finish, this));
1052 return RespondLater();
1055 void DeveloperPrivateRequestFileSourceFunction::Finish(
1056 const std::string& file_contents) {
1057 const developer::RequestFileSourceProperties& properties =
1058 params_->properties;
1059 const Extension* extension = GetExtensionById(properties.extension_id);
1060 if (!extension) {
1061 Respond(Error(kNoSuchExtensionError));
1062 return;
1065 developer::RequestFileSourceResponse response;
1066 base::FilePath path_suffix =
1067 base::FilePath::FromUTF8Unsafe(properties.path_suffix);
1068 base::FilePath path = extension->path().Append(path_suffix);
1069 response.title = base::StringPrintf("%s: %s",
1070 extension->name().c_str(),
1071 path.BaseName().AsUTF8Unsafe().c_str());
1072 response.message = properties.message;
1074 scoped_ptr<FileHighlighter> highlighter;
1075 if (properties.path_suffix == kManifestFile) {
1076 highlighter.reset(new ManifestHighlighter(
1077 file_contents,
1078 *properties.manifest_key,
1079 properties.manifest_specific ?
1080 *properties.manifest_specific : std::string()));
1081 } else {
1082 highlighter.reset(new SourceHighlighter(
1083 file_contents,
1084 properties.line_number ? *properties.line_number : 0));
1087 response.before_highlight = highlighter->GetBeforeFeature();
1088 response.highlight = highlighter->GetFeature();
1089 response.after_highlight = highlighter->GetAfterFeature();
1091 Respond(OneArgument(response.ToValue().release()));
1094 DeveloperPrivateOpenDevToolsFunction::DeveloperPrivateOpenDevToolsFunction() {}
1095 DeveloperPrivateOpenDevToolsFunction::~DeveloperPrivateOpenDevToolsFunction() {}
1097 ExtensionFunction::ResponseAction
1098 DeveloperPrivateOpenDevToolsFunction::Run() {
1099 scoped_ptr<developer::OpenDevTools::Params> params(
1100 developer::OpenDevTools::Params::Create(*args_));
1101 EXTENSION_FUNCTION_VALIDATE(params);
1102 const developer::OpenDevToolsProperties& properties = params->properties;
1104 if (properties.render_process_id == -1) {
1105 // This is a lazy background page.
1106 const Extension* extension = properties.extension_id ?
1107 ExtensionRegistry::Get(browser_context())->enabled_extensions().GetByID(
1108 *properties.extension_id) : nullptr;
1109 if (!extension)
1110 return RespondNow(Error(kNoSuchExtensionError));
1112 Profile* profile = Profile::FromBrowserContext(browser_context());
1113 if (properties.incognito && *properties.incognito)
1114 profile = profile->GetOffTheRecordProfile();
1116 // Wakes up the background page and opens the inspect window.
1117 devtools_util::InspectBackgroundPage(extension, profile);
1118 return RespondNow(NoArguments());
1121 content::RenderViewHost* rvh =
1122 content::RenderViewHost::FromID(properties.render_process_id,
1123 properties.render_view_id);
1125 content::WebContents* web_contents =
1126 rvh ? content::WebContents::FromRenderViewHost(rvh) : nullptr;
1127 // It's possible that the render view was closed since we last updated the
1128 // links. Handle this gracefully.
1129 if (!web_contents)
1130 return RespondNow(Error(kNoSuchRendererError));
1132 // If we include a url, we should inspect it specifically (and not just the
1133 // render view).
1134 if (properties.url) {
1135 // Line/column numbers are reported in display-friendly 1-based numbers,
1136 // but are inspected in zero-based numbers.
1137 // Default to the first line/column.
1138 DevToolsWindow::OpenDevToolsWindow(
1139 web_contents,
1140 DevToolsToggleAction::Reveal(
1141 base::UTF8ToUTF16(*properties.url),
1142 properties.line_number ? *properties.line_number - 1 : 0,
1143 properties.column_number ? *properties.column_number - 1 : 0));
1144 } else {
1145 DevToolsWindow::OpenDevToolsWindow(web_contents);
1148 // Once we open the inspector, we focus on the appropriate tab...
1149 Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
1151 // ... but some pages (popups and apps) don't have tabs, and some (background
1152 // pages) don't have an associated browser. For these, the inspector opens in
1153 // a new window, and our work is done.
1154 if (!browser || !browser->is_type_tabbed())
1155 return RespondNow(NoArguments());
1157 TabStripModel* tab_strip = browser->tab_strip_model();
1158 tab_strip->ActivateTabAt(tab_strip->GetIndexOfWebContents(web_contents),
1159 false); // Not through direct user gesture.
1160 return RespondNow(NoArguments());
1163 DeveloperPrivateDeleteExtensionErrorsFunction::
1164 ~DeveloperPrivateDeleteExtensionErrorsFunction() {}
1166 ExtensionFunction::ResponseAction
1167 DeveloperPrivateDeleteExtensionErrorsFunction::Run() {
1168 scoped_ptr<developer::DeleteExtensionErrors::Params> params(
1169 developer::DeleteExtensionErrors::Params::Create(*args_));
1170 EXTENSION_FUNCTION_VALIDATE(params);
1171 const developer::DeleteExtensionErrorsProperties& properties =
1172 params->properties;
1174 ErrorConsole* error_console =
1175 ErrorConsole::Get(Profile::FromBrowserContext(browser_context()));
1176 int type = -1;
1177 if (properties.type != developer::ERROR_TYPE_NONE) {
1178 type = properties.type == developer::ERROR_TYPE_MANIFEST ?
1179 ExtensionError::MANIFEST_ERROR : ExtensionError::RUNTIME_ERROR;
1181 std::set<int> error_ids;
1182 if (properties.error_ids) {
1183 error_ids.insert(properties.error_ids->begin(),
1184 properties.error_ids->end());
1186 error_console->RemoveErrors(ErrorMap::Filter(
1187 properties.extension_id, type, error_ids, false));
1189 return RespondNow(NoArguments());
1192 } // namespace api
1194 } // namespace extensions