Revert of [Extensions Page] Send a profile changed event when developer mode changes...
[chromium-blink-merge.git] / chrome / browser / extensions / api / developer_private / developer_private_api.cc
blob13b8e3a0bbefece2062a1971458c5d42c1529ae6
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/bind.h"
8 #include "base/files/file_util.h"
9 #include "base/lazy_instance.h"
10 #include "base/metrics/histogram_macros.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_commands_global_registry.h"
23 #include "chrome/browser/extensions/extension_service.h"
24 #include "chrome/browser/extensions/extension_tab_util.h"
25 #include "chrome/browser/extensions/extension_ui_util.h"
26 #include "chrome/browser/extensions/extension_util.h"
27 #include "chrome/browser/extensions/install_verifier.h"
28 #include "chrome/browser/extensions/shared_module_service.h"
29 #include "chrome/browser/extensions/unpacked_installer.h"
30 #include "chrome/browser/extensions/updater/extension_updater.h"
31 #include "chrome/browser/extensions/webstore_reinstaller.h"
32 #include "chrome/browser/platform_util.h"
33 #include "chrome/browser/prefs/incognito_mode_prefs.h"
34 #include "chrome/browser/profiles/profile.h"
35 #include "chrome/browser/ui/apps/app_info_dialog.h"
36 #include "chrome/browser/ui/browser_finder.h"
37 #include "chrome/browser/ui/extensions/app_launch_params.h"
38 #include "chrome/browser/ui/extensions/application_launch.h"
39 #include "chrome/browser/ui/tabs/tab_strip_model.h"
40 #include "chrome/common/extensions/api/developer_private.h"
41 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
42 #include "chrome/common/pref_names.h"
43 #include "chrome/common/url_constants.h"
44 #include "chrome/grit/generated_resources.h"
45 #include "content/public/browser/browser_thread.h"
46 #include "content/public/browser/notification_service.h"
47 #include "content/public/browser/render_frame_host.h"
48 #include "content/public/browser/render_process_host.h"
49 #include "content/public/browser/site_instance.h"
50 #include "content/public/browser/storage_partition.h"
51 #include "content/public/browser/web_contents.h"
52 #include "extensions/browser/app_window/app_window.h"
53 #include "extensions/browser/app_window/app_window_registry.h"
54 #include "extensions/browser/error_map.h"
55 #include "extensions/browser/extension_error.h"
56 #include "extensions/browser/extension_prefs.h"
57 #include "extensions/browser/extension_registry.h"
58 #include "extensions/browser/extension_system.h"
59 #include "extensions/browser/file_highlighter.h"
60 #include "extensions/browser/management_policy.h"
61 #include "extensions/browser/notification_types.h"
62 #include "extensions/browser/warning_service.h"
63 #include "extensions/common/constants.h"
64 #include "extensions/common/extension_set.h"
65 #include "extensions/common/feature_switch.h"
66 #include "extensions/common/install_warning.h"
67 #include "extensions/common/manifest.h"
68 #include "extensions/common/manifest_handlers/options_page_info.h"
69 #include "extensions/common/permissions/permissions_data.h"
70 #include "extensions/grit/extensions_browser_resources.h"
71 #include "storage/browser/fileapi/external_mount_points.h"
72 #include "storage/browser/fileapi/file_system_context.h"
73 #include "storage/browser/fileapi/file_system_operation.h"
74 #include "storage/browser/fileapi/file_system_operation_runner.h"
75 #include "storage/browser/fileapi/isolated_context.h"
76 #include "ui/base/l10n/l10n_util.h"
78 namespace extensions {
80 namespace developer = api::developer_private;
82 namespace {
84 const char kNoSuchExtensionError[] = "No such extension.";
85 const char kCannotModifyPolicyExtensionError[] =
86 "Cannot modify the extension by policy.";
87 const char kRequiresUserGestureError[] =
88 "This action requires a user gesture.";
89 const char kCouldNotShowSelectFileDialogError[] =
90 "Could not show a file chooser.";
91 const char kFileSelectionCanceled[] =
92 "File selection was canceled.";
93 const char kNoSuchRendererError[] = "No such renderer.";
94 const char kInvalidPathError[] = "Invalid path.";
95 const char kManifestKeyIsRequiredError[] =
96 "The 'manifestKey' argument is required for manifest files.";
97 const char kCouldNotFindWebContentsError[] =
98 "Could not find a valid web contents.";
99 const char kCannotUpdateSupervisedProfileSettingsError[] =
100 "Cannot change settings for a supervised profile.";
101 const char kNoOptionsPageForExtensionError[] =
102 "Extension does not have an options page.";
104 const char kUnpackedAppsFolder[] = "apps_target";
105 const char kManifestFile[] = "manifest.json";
107 ExtensionService* GetExtensionService(content::BrowserContext* context) {
108 return ExtensionSystem::Get(context)->extension_service();
111 std::string ReadFileToString(const base::FilePath& path) {
112 std::string data;
113 ignore_result(base::ReadFileToString(path, &data));
114 return data;
117 bool UserCanModifyExtensionConfiguration(
118 const Extension* extension,
119 content::BrowserContext* browser_context,
120 std::string* error) {
121 ManagementPolicy* management_policy =
122 ExtensionSystem::Get(browser_context)->management_policy();
123 if (!management_policy->UserMayModifySettings(extension, nullptr)) {
124 LOG(ERROR) << "Attempt to change settings of an extension that is "
125 << "non-usermanagable was made. Extension id : "
126 << extension->id();
127 *error = kCannotModifyPolicyExtensionError;
128 return false;
131 return true;
134 // Runs the install verifier for all extensions that are enabled, disabled, or
135 // terminated.
136 void PerformVerificationCheck(content::BrowserContext* context) {
137 scoped_ptr<ExtensionSet> extensions =
138 ExtensionRegistry::Get(context)->GenerateInstalledExtensionsSet(
139 ExtensionRegistry::ENABLED |
140 ExtensionRegistry::DISABLED |
141 ExtensionRegistry::TERMINATED);
142 ExtensionPrefs* prefs = ExtensionPrefs::Get(context);
143 bool should_do_verification_check = false;
144 for (const scoped_refptr<const Extension>& extension : *extensions) {
145 if (ui_util::ShouldDisplayInExtensionSettings(extension.get(), context) &&
146 ((prefs->GetDisableReasons(extension->id()) &
147 Extension::DISABLE_NOT_VERIFIED) != 0)) {
148 should_do_verification_check = true;
149 break;
153 UMA_HISTOGRAM_BOOLEAN("ExtensionSettings.ShouldDoVerificationCheck",
154 should_do_verification_check);
155 if (should_do_verification_check)
156 InstallVerifier::Get(context)->VerifyAllExtensions();
159 scoped_ptr<developer::ProfileInfo> CreateProfileInfo(Profile* profile) {
160 scoped_ptr<developer::ProfileInfo> info(new developer::ProfileInfo());
161 info->is_supervised = profile->IsSupervised();
162 PrefService* prefs = profile->GetPrefs();
163 info->is_incognito_available =
164 IncognitoModePrefs::GetAvailability(prefs) !=
165 IncognitoModePrefs::DISABLED;
166 info->in_developer_mode =
167 !info->is_supervised &&
168 prefs->GetBoolean(prefs::kExtensionsUIDeveloperMode);
169 info->app_info_dialog_enabled = CanShowAppInfoDialog();
170 info->can_load_unpacked =
171 !ExtensionManagementFactory::GetForBrowserContext(profile)
172 ->BlacklistedByDefault();
173 return info.Pass();
176 } // namespace
178 namespace ChoosePath = api::developer_private::ChoosePath;
179 namespace GetItemsInfo = api::developer_private::GetItemsInfo;
180 namespace PackDirectory = api::developer_private::PackDirectory;
181 namespace Reload = api::developer_private::Reload;
183 static base::LazyInstance<BrowserContextKeyedAPIFactory<DeveloperPrivateAPI> >
184 g_factory = LAZY_INSTANCE_INITIALIZER;
186 // static
187 BrowserContextKeyedAPIFactory<DeveloperPrivateAPI>*
188 DeveloperPrivateAPI::GetFactoryInstance() {
189 return g_factory.Pointer();
192 // static
193 DeveloperPrivateAPI* DeveloperPrivateAPI::Get(
194 content::BrowserContext* context) {
195 return GetFactoryInstance()->Get(context);
198 DeveloperPrivateAPI::DeveloperPrivateAPI(content::BrowserContext* context)
199 : profile_(Profile::FromBrowserContext(context)) {
200 RegisterNotifications();
203 DeveloperPrivateEventRouter::DeveloperPrivateEventRouter(Profile* profile)
204 : extension_registry_observer_(this),
205 error_console_observer_(this),
206 process_manager_observer_(this),
207 app_window_registry_observer_(this),
208 extension_action_api_observer_(this),
209 warning_service_observer_(this),
210 extension_prefs_observer_(this),
211 extension_management_observer_(this),
212 command_service_observer_(this),
213 profile_(profile),
214 event_router_(EventRouter::Get(profile_)),
215 weak_factory_(this) {
216 extension_registry_observer_.Add(ExtensionRegistry::Get(profile_));
217 error_console_observer_.Add(ErrorConsole::Get(profile));
218 process_manager_observer_.Add(ProcessManager::Get(profile));
219 app_window_registry_observer_.Add(AppWindowRegistry::Get(profile));
220 extension_action_api_observer_.Add(ExtensionActionAPI::Get(profile));
221 warning_service_observer_.Add(WarningService::Get(profile));
222 extension_prefs_observer_.Add(ExtensionPrefs::Get(profile));
223 extension_management_observer_.Add(
224 ExtensionManagementFactory::GetForBrowserContext(profile));
225 command_service_observer_.Add(CommandService::Get(profile));
228 DeveloperPrivateEventRouter::~DeveloperPrivateEventRouter() {
231 void DeveloperPrivateEventRouter::AddExtensionId(
232 const std::string& extension_id) {
233 extension_ids_.insert(extension_id);
236 void DeveloperPrivateEventRouter::RemoveExtensionId(
237 const std::string& extension_id) {
238 extension_ids_.erase(extension_id);
241 void DeveloperPrivateEventRouter::OnExtensionLoaded(
242 content::BrowserContext* browser_context,
243 const Extension* extension) {
244 DCHECK(profile_->IsSameProfile(Profile::FromBrowserContext(browser_context)));
245 BroadcastItemStateChanged(developer::EVENT_TYPE_LOADED, extension->id());
248 void DeveloperPrivateEventRouter::OnExtensionUnloaded(
249 content::BrowserContext* browser_context,
250 const Extension* extension,
251 UnloadedExtensionInfo::Reason reason) {
252 DCHECK(profile_->IsSameProfile(Profile::FromBrowserContext(browser_context)));
253 BroadcastItemStateChanged(developer::EVENT_TYPE_UNLOADED, extension->id());
256 void DeveloperPrivateEventRouter::OnExtensionInstalled(
257 content::BrowserContext* browser_context,
258 const Extension* extension,
259 bool is_update) {
260 DCHECK(profile_->IsSameProfile(Profile::FromBrowserContext(browser_context)));
261 BroadcastItemStateChanged(developer::EVENT_TYPE_INSTALLED, extension->id());
264 void DeveloperPrivateEventRouter::OnExtensionUninstalled(
265 content::BrowserContext* browser_context,
266 const Extension* extension,
267 extensions::UninstallReason reason) {
268 DCHECK(profile_->IsSameProfile(Profile::FromBrowserContext(browser_context)));
269 BroadcastItemStateChanged(developer::EVENT_TYPE_UNINSTALLED, extension->id());
272 void DeveloperPrivateEventRouter::OnErrorAdded(const ExtensionError* error) {
273 // We don't want to handle errors thrown by extensions subscribed to these
274 // events (currently only the Apps Developer Tool), because doing so risks
275 // entering a loop.
276 if (extension_ids_.count(error->extension_id()))
277 return;
279 BroadcastItemStateChanged(developer::EVENT_TYPE_ERROR_ADDED,
280 error->extension_id());
283 void DeveloperPrivateEventRouter::OnErrorsRemoved(
284 const std::set<std::string>& removed_ids) {
285 for (const std::string& id : removed_ids) {
286 if (!extension_ids_.count(id))
287 BroadcastItemStateChanged(developer::EVENT_TYPE_ERRORS_REMOVED, id);
291 void DeveloperPrivateEventRouter::OnExtensionFrameRegistered(
292 const std::string& extension_id,
293 content::RenderFrameHost* render_frame_host) {
294 BroadcastItemStateChanged(developer::EVENT_TYPE_VIEW_REGISTERED,
295 extension_id);
298 void DeveloperPrivateEventRouter::OnExtensionFrameUnregistered(
299 const std::string& extension_id,
300 content::RenderFrameHost* render_frame_host) {
301 BroadcastItemStateChanged(developer::EVENT_TYPE_VIEW_UNREGISTERED,
302 extension_id);
305 void DeveloperPrivateEventRouter::OnAppWindowAdded(AppWindow* window) {
306 BroadcastItemStateChanged(developer::EVENT_TYPE_VIEW_REGISTERED,
307 window->extension_id());
310 void DeveloperPrivateEventRouter::OnAppWindowRemoved(AppWindow* window) {
311 BroadcastItemStateChanged(developer::EVENT_TYPE_VIEW_UNREGISTERED,
312 window->extension_id());
315 void DeveloperPrivateEventRouter::OnExtensionCommandAdded(
316 const std::string& extension_id,
317 const Command& added_command) {
318 BroadcastItemStateChanged(developer::EVENT_TYPE_PREFS_CHANGED,
319 extension_id);
322 void DeveloperPrivateEventRouter::OnExtensionCommandRemoved(
323 const std::string& extension_id,
324 const Command& removed_command) {
325 BroadcastItemStateChanged(developer::EVENT_TYPE_PREFS_CHANGED,
326 extension_id);
329 void DeveloperPrivateEventRouter::OnExtensionActionVisibilityChanged(
330 const std::string& extension_id,
331 bool is_now_visible) {
332 BroadcastItemStateChanged(developer::EVENT_TYPE_PREFS_CHANGED, extension_id);
335 void DeveloperPrivateEventRouter::OnExtensionDisableReasonsChanged(
336 const std::string& extension_id, int disable_reasons) {
337 BroadcastItemStateChanged(developer::EVENT_TYPE_PREFS_CHANGED, extension_id);
340 void DeveloperPrivateEventRouter::OnExtensionManagementSettingsChanged() {
341 scoped_ptr<base::ListValue> args(new base::ListValue());
342 args->Append(CreateProfileInfo(profile_)->ToValue());
343 scoped_ptr<Event> event(
344 new Event(events::DEVELOPER_PRIVATE_ON_PROFILE_STATE_CHANGED,
345 developer::OnProfileStateChanged::kEventName, args.Pass()));
346 event_router_->BroadcastEvent(event.Pass());
349 void DeveloperPrivateEventRouter::ExtensionWarningsChanged(
350 const ExtensionIdSet& affected_extensions) {
351 for (const ExtensionId& id : affected_extensions)
352 BroadcastItemStateChanged(developer::EVENT_TYPE_WARNINGS_CHANGED, id);
355 void DeveloperPrivateEventRouter::BroadcastItemStateChanged(
356 developer::EventType event_type,
357 const std::string& extension_id) {
358 scoped_ptr<ExtensionInfoGenerator> info_generator(
359 new ExtensionInfoGenerator(profile_));
360 ExtensionInfoGenerator* info_generator_weak = info_generator.get();
361 info_generator_weak->CreateExtensionInfo(
362 extension_id,
363 base::Bind(&DeveloperPrivateEventRouter::BroadcastItemStateChangedHelper,
364 weak_factory_.GetWeakPtr(),
365 event_type,
366 extension_id,
367 base::Passed(info_generator.Pass())));
370 void DeveloperPrivateEventRouter::BroadcastItemStateChangedHelper(
371 developer::EventType event_type,
372 const std::string& extension_id,
373 scoped_ptr<ExtensionInfoGenerator> info_generator,
374 const ExtensionInfoGenerator::ExtensionInfoList& infos) {
375 DCHECK_LE(infos.size(), 1u);
377 developer::EventData event_data;
378 event_data.event_type = event_type;
379 event_data.item_id = extension_id;
380 scoped_ptr<base::DictionaryValue> dict = event_data.ToValue();
382 if (!infos.empty()) {
383 // Hack: Ideally, we would use event_data.extension_info to set the
384 // extension info, but since it's an optional field, it's implemented as a
385 // scoped ptr, and so ownership between that and the vector of linked ptrs
386 // here is, well, messy. Easier to just set it like this.
387 dict->SetWithoutPathExpansion("extensionInfo",
388 infos[0]->ToValue().release());
391 scoped_ptr<base::ListValue> args(new base::ListValue());
392 args->Append(dict.release());
393 scoped_ptr<Event> event(
394 new Event(events::DEVELOPER_PRIVATE_ON_ITEM_STATE_CHANGED,
395 developer::OnItemStateChanged::kEventName, args.Pass()));
396 event_router_->BroadcastEvent(event.Pass());
399 void DeveloperPrivateAPI::SetLastUnpackedDirectory(const base::FilePath& path) {
400 last_unpacked_directory_ = path;
403 void DeveloperPrivateAPI::RegisterNotifications() {
404 EventRouter::Get(profile_)->RegisterObserver(
405 this, developer::OnItemStateChanged::kEventName);
408 DeveloperPrivateAPI::~DeveloperPrivateAPI() {}
410 void DeveloperPrivateAPI::Shutdown() {}
412 void DeveloperPrivateAPI::OnListenerAdded(
413 const EventListenerInfo& details) {
414 if (!developer_private_event_router_) {
415 developer_private_event_router_.reset(
416 new DeveloperPrivateEventRouter(profile_));
419 developer_private_event_router_->AddExtensionId(details.extension_id);
422 void DeveloperPrivateAPI::OnListenerRemoved(
423 const EventListenerInfo& details) {
424 if (!EventRouter::Get(profile_)->HasEventListener(
425 developer::OnItemStateChanged::kEventName)) {
426 developer_private_event_router_.reset(NULL);
427 } else {
428 developer_private_event_router_->RemoveExtensionId(details.extension_id);
432 namespace api {
434 DeveloperPrivateAPIFunction::~DeveloperPrivateAPIFunction() {
437 const Extension* DeveloperPrivateAPIFunction::GetExtensionById(
438 const std::string& id) {
439 return ExtensionRegistry::Get(browser_context())->GetExtensionById(
440 id, ExtensionRegistry::EVERYTHING);
443 const Extension* DeveloperPrivateAPIFunction::GetEnabledExtensionById(
444 const std::string& id) {
445 return ExtensionRegistry::Get(browser_context())->enabled_extensions().
446 GetByID(id);
449 DeveloperPrivateAutoUpdateFunction::~DeveloperPrivateAutoUpdateFunction() {}
451 ExtensionFunction::ResponseAction DeveloperPrivateAutoUpdateFunction::Run() {
452 ExtensionUpdater* updater =
453 ExtensionSystem::Get(browser_context())->extension_service()->updater();
454 if (updater) {
455 ExtensionUpdater::CheckParams params;
456 params.install_immediately = true;
457 updater->CheckNow(params);
459 return RespondNow(NoArguments());
462 DeveloperPrivateGetExtensionsInfoFunction::
463 DeveloperPrivateGetExtensionsInfoFunction() {
466 DeveloperPrivateGetExtensionsInfoFunction::
467 ~DeveloperPrivateGetExtensionsInfoFunction() {
470 ExtensionFunction::ResponseAction
471 DeveloperPrivateGetExtensionsInfoFunction::Run() {
472 scoped_ptr<developer::GetExtensionsInfo::Params> params(
473 developer::GetExtensionsInfo::Params::Create(*args_));
474 EXTENSION_FUNCTION_VALIDATE(params);
476 bool include_disabled = true;
477 bool include_terminated = true;
478 if (params->options) {
479 if (params->options->include_disabled)
480 include_disabled = *params->options->include_disabled;
481 if (params->options->include_terminated)
482 include_terminated = *params->options->include_terminated;
485 info_generator_.reset(new ExtensionInfoGenerator(browser_context()));
486 info_generator_->CreateExtensionsInfo(
487 include_disabled,
488 include_terminated,
489 base::Bind(&DeveloperPrivateGetExtensionsInfoFunction::OnInfosGenerated,
490 this /* refcounted */));
492 return RespondLater();
495 void DeveloperPrivateGetExtensionsInfoFunction::OnInfosGenerated(
496 const ExtensionInfoGenerator::ExtensionInfoList& list) {
497 Respond(ArgumentList(developer::GetExtensionsInfo::Results::Create(list)));
500 DeveloperPrivateGetExtensionInfoFunction::
501 DeveloperPrivateGetExtensionInfoFunction() {
504 DeveloperPrivateGetExtensionInfoFunction::
505 ~DeveloperPrivateGetExtensionInfoFunction() {
508 ExtensionFunction::ResponseAction
509 DeveloperPrivateGetExtensionInfoFunction::Run() {
510 scoped_ptr<developer::GetExtensionInfo::Params> params(
511 developer::GetExtensionInfo::Params::Create(*args_));
512 EXTENSION_FUNCTION_VALIDATE(params);
514 info_generator_.reset(new ExtensionInfoGenerator(browser_context()));
515 info_generator_->CreateExtensionInfo(
516 params->id,
517 base::Bind(&DeveloperPrivateGetExtensionInfoFunction::OnInfosGenerated,
518 this /* refcounted */));
520 return RespondLater();
523 void DeveloperPrivateGetExtensionInfoFunction::OnInfosGenerated(
524 const ExtensionInfoGenerator::ExtensionInfoList& list) {
525 DCHECK_EQ(1u, list.size());
526 const linked_ptr<developer::ExtensionInfo>& info = list[0];
527 Respond(info.get() ? OneArgument(info->ToValue()) :
528 Error(kNoSuchExtensionError));
531 DeveloperPrivateGetItemsInfoFunction::DeveloperPrivateGetItemsInfoFunction() {}
532 DeveloperPrivateGetItemsInfoFunction::~DeveloperPrivateGetItemsInfoFunction() {}
534 ExtensionFunction::ResponseAction DeveloperPrivateGetItemsInfoFunction::Run() {
535 scoped_ptr<developer::GetItemsInfo::Params> params(
536 developer::GetItemsInfo::Params::Create(*args_));
537 EXTENSION_FUNCTION_VALIDATE(params);
539 info_generator_.reset(new ExtensionInfoGenerator(browser_context()));
540 info_generator_->CreateExtensionsInfo(
541 params->include_disabled,
542 params->include_terminated,
543 base::Bind(&DeveloperPrivateGetItemsInfoFunction::OnInfosGenerated,
544 this /* refcounted */));
546 return RespondLater();
549 void DeveloperPrivateGetItemsInfoFunction::OnInfosGenerated(
550 const ExtensionInfoGenerator::ExtensionInfoList& list) {
551 std::vector<linked_ptr<developer::ItemInfo>> item_list;
552 for (const linked_ptr<developer::ExtensionInfo>& info : list)
553 item_list.push_back(developer_private_mangle::MangleExtensionInfo(*info));
555 Respond(ArgumentList(developer::GetItemsInfo::Results::Create(item_list)));
558 DeveloperPrivateGetProfileConfigurationFunction::
559 ~DeveloperPrivateGetProfileConfigurationFunction() {
562 ExtensionFunction::ResponseAction
563 DeveloperPrivateGetProfileConfigurationFunction::Run() {
564 scoped_ptr<developer::ProfileInfo> info = CreateProfileInfo(GetProfile());
566 // If this is called from the chrome://extensions page, we use this as a
567 // heuristic that it's a good time to verify installs. We do this on startup,
568 // but there's a chance that it failed erroneously, so it's good to double-
569 // check.
570 if (source_context_type() == Feature::WEBUI_CONTEXT)
571 PerformVerificationCheck(browser_context());
573 return RespondNow(OneArgument(info->ToValue()));
576 DeveloperPrivateUpdateProfileConfigurationFunction::
577 ~DeveloperPrivateUpdateProfileConfigurationFunction() {
580 ExtensionFunction::ResponseAction
581 DeveloperPrivateUpdateProfileConfigurationFunction::Run() {
582 scoped_ptr<developer::UpdateProfileConfiguration::Params> params(
583 developer::UpdateProfileConfiguration::Params::Create(*args_));
584 EXTENSION_FUNCTION_VALIDATE(params);
586 const developer::ProfileConfigurationUpdate& update = params->update;
587 PrefService* prefs = GetProfile()->GetPrefs();
588 if (update.in_developer_mode) {
589 if (GetProfile()->IsSupervised())
590 return RespondNow(Error(kCannotUpdateSupervisedProfileSettingsError));
591 prefs->SetBoolean(prefs::kExtensionsUIDeveloperMode,
592 *update.in_developer_mode);
595 return RespondNow(NoArguments());
598 DeveloperPrivateUpdateExtensionConfigurationFunction::
599 ~DeveloperPrivateUpdateExtensionConfigurationFunction() {}
601 ExtensionFunction::ResponseAction
602 DeveloperPrivateUpdateExtensionConfigurationFunction::Run() {
603 scoped_ptr<developer::UpdateExtensionConfiguration::Params> params(
604 developer::UpdateExtensionConfiguration::Params::Create(*args_));
605 EXTENSION_FUNCTION_VALIDATE(params);
607 const developer::ExtensionConfigurationUpdate& update = params->update;
609 const Extension* extension = GetExtensionById(update.extension_id);
610 if (!extension)
611 return RespondNow(Error(kNoSuchExtensionError));
612 if (!user_gesture())
613 return RespondNow(Error(kRequiresUserGestureError));
615 if (update.file_access) {
616 std::string error;
617 if (!UserCanModifyExtensionConfiguration(extension,
618 browser_context(),
619 &error)) {
620 return RespondNow(Error(error));
622 util::SetAllowFileAccess(
623 extension->id(), browser_context(), *update.file_access);
625 if (update.incognito_access) {
626 util::SetIsIncognitoEnabled(
627 extension->id(), browser_context(), *update.incognito_access);
629 if (update.error_collection) {
630 ErrorConsole::Get(browser_context())->SetReportingAllForExtension(
631 extension->id(), *update.error_collection);
633 if (update.run_on_all_urls) {
634 util::SetAllowedScriptingOnAllUrls(
635 extension->id(), browser_context(), *update.run_on_all_urls);
637 if (update.show_action_button) {
638 ExtensionActionAPI::Get(browser_context())->SetBrowserActionVisibility(
639 extension->id(),
640 *update.show_action_button);
643 return RespondNow(NoArguments());
646 DeveloperPrivateReloadFunction::~DeveloperPrivateReloadFunction() {}
648 ExtensionFunction::ResponseAction DeveloperPrivateReloadFunction::Run() {
649 scoped_ptr<Reload::Params> params(Reload::Params::Create(*args_));
650 EXTENSION_FUNCTION_VALIDATE(params.get());
652 const Extension* extension = GetExtensionById(params->extension_id);
653 if (!extension)
654 return RespondNow(Error(kNoSuchExtensionError));
656 bool fail_quietly = params->options &&
657 params->options->fail_quietly &&
658 *params->options->fail_quietly;
660 ExtensionService* service = GetExtensionService(browser_context());
661 if (fail_quietly)
662 service->ReloadExtensionWithQuietFailure(params->extension_id);
663 else
664 service->ReloadExtension(params->extension_id);
666 // TODO(devlin): We shouldn't return until the extension has finished trying
667 // to reload (and then we could also return the error).
668 return RespondNow(NoArguments());
671 DeveloperPrivateShowPermissionsDialogFunction::
672 DeveloperPrivateShowPermissionsDialogFunction() {}
674 DeveloperPrivateShowPermissionsDialogFunction::
675 ~DeveloperPrivateShowPermissionsDialogFunction() {}
677 ExtensionFunction::ResponseAction
678 DeveloperPrivateShowPermissionsDialogFunction::Run() {
679 scoped_ptr<developer::ShowPermissionsDialog::Params> params(
680 developer::ShowPermissionsDialog::Params::Create(*args_));
681 EXTENSION_FUNCTION_VALIDATE(params);
683 const Extension* target_extension = GetExtensionById(params->extension_id);
684 if (!target_extension)
685 return RespondNow(Error(kNoSuchExtensionError));
687 content::WebContents* web_contents = GetSenderWebContents();
688 if (!web_contents)
689 return RespondNow(Error(kCouldNotFindWebContentsError));
691 ShowPermissionsDialogHelper::Show(
692 browser_context(),
693 web_contents,
694 target_extension,
695 source_context_type() == Feature::WEBUI_CONTEXT,
696 base::Bind(&DeveloperPrivateShowPermissionsDialogFunction::Finish, this));
697 return RespondLater();
700 void DeveloperPrivateShowPermissionsDialogFunction::Finish() {
701 Respond(NoArguments());
704 DeveloperPrivateLoadUnpackedFunction::DeveloperPrivateLoadUnpackedFunction()
705 : fail_quietly_(false) {
708 ExtensionFunction::ResponseAction DeveloperPrivateLoadUnpackedFunction::Run() {
709 scoped_ptr<developer::LoadUnpacked::Params> params(
710 developer::LoadUnpacked::Params::Create(*args_));
711 EXTENSION_FUNCTION_VALIDATE(params);
713 if (!ShowPicker(
714 ui::SelectFileDialog::SELECT_FOLDER,
715 l10n_util::GetStringUTF16(IDS_EXTENSION_LOAD_FROM_DIRECTORY),
716 ui::SelectFileDialog::FileTypeInfo(),
717 0 /* file_type_index */)) {
718 return RespondNow(Error(kCouldNotShowSelectFileDialogError));
721 fail_quietly_ = params->options &&
722 params->options->fail_quietly &&
723 *params->options->fail_quietly;
725 AddRef(); // Balanced in FileSelected / FileSelectionCanceled.
726 return RespondLater();
729 void DeveloperPrivateLoadUnpackedFunction::FileSelected(
730 const base::FilePath& path) {
731 scoped_refptr<UnpackedInstaller> installer(
732 UnpackedInstaller::Create(GetExtensionService(browser_context())));
733 installer->set_be_noisy_on_failure(!fail_quietly_);
734 installer->set_completion_callback(
735 base::Bind(&DeveloperPrivateLoadUnpackedFunction::OnLoadComplete, this));
736 installer->Load(path);
738 DeveloperPrivateAPI::Get(browser_context())->SetLastUnpackedDirectory(path);
740 Release(); // Balanced in Run().
743 void DeveloperPrivateLoadUnpackedFunction::FileSelectionCanceled() {
744 // This isn't really an error, but we should keep it like this for
745 // backward compatability.
746 Respond(Error(kFileSelectionCanceled));
747 Release(); // Balanced in Run().
750 void DeveloperPrivateLoadUnpackedFunction::OnLoadComplete(
751 const Extension* extension,
752 const base::FilePath& file_path,
753 const std::string& error) {
754 Respond(extension ? NoArguments() : Error(error));
757 bool DeveloperPrivateChooseEntryFunction::ShowPicker(
758 ui::SelectFileDialog::Type picker_type,
759 const base::string16& select_title,
760 const ui::SelectFileDialog::FileTypeInfo& info,
761 int file_type_index) {
762 content::WebContents* web_contents = GetSenderWebContents();
763 if (!web_contents)
764 return false;
766 // The entry picker will hold a reference to this function instance,
767 // and subsequent sending of the function response) until the user has
768 // selected a file or cancelled the picker. At that point, the picker will
769 // delete itself.
770 new EntryPicker(this,
771 web_contents,
772 picker_type,
773 DeveloperPrivateAPI::Get(browser_context())->
774 GetLastUnpackedDirectory(),
775 select_title,
776 info,
777 file_type_index);
778 return true;
781 DeveloperPrivateChooseEntryFunction::~DeveloperPrivateChooseEntryFunction() {}
783 void DeveloperPrivatePackDirectoryFunction::OnPackSuccess(
784 const base::FilePath& crx_file,
785 const base::FilePath& pem_file) {
786 developer::PackDirectoryResponse response;
787 response.message = base::UTF16ToUTF8(
788 PackExtensionJob::StandardSuccessMessage(crx_file, pem_file));
789 response.status = developer::PACK_STATUS_SUCCESS;
790 Respond(OneArgument(response.ToValue()));
791 Release(); // Balanced in Run().
794 void DeveloperPrivatePackDirectoryFunction::OnPackFailure(
795 const std::string& error,
796 ExtensionCreator::ErrorType error_type) {
797 developer::PackDirectoryResponse response;
798 response.message = error;
799 if (error_type == ExtensionCreator::kCRXExists) {
800 response.item_path = item_path_str_;
801 response.pem_path = key_path_str_;
802 response.override_flags = ExtensionCreator::kOverwriteCRX;
803 response.status = developer::PACK_STATUS_WARNING;
804 } else {
805 response.status = developer::PACK_STATUS_ERROR;
807 Respond(OneArgument(response.ToValue()));
808 Release(); // Balanced in Run().
811 ExtensionFunction::ResponseAction DeveloperPrivatePackDirectoryFunction::Run() {
812 scoped_ptr<PackDirectory::Params> params(
813 PackDirectory::Params::Create(*args_));
814 EXTENSION_FUNCTION_VALIDATE(params);
816 int flags = params->flags ? *params->flags : 0;
817 item_path_str_ = params->path;
818 if (params->private_key_path)
819 key_path_str_ = *params->private_key_path;
821 base::FilePath root_directory =
822 base::FilePath::FromUTF8Unsafe(item_path_str_);
823 base::FilePath key_file = base::FilePath::FromUTF8Unsafe(key_path_str_);
825 developer::PackDirectoryResponse response;
826 if (root_directory.empty()) {
827 if (item_path_str_.empty())
828 response.message = l10n_util::GetStringUTF8(
829 IDS_EXTENSION_PACK_DIALOG_ERROR_ROOT_REQUIRED);
830 else
831 response.message = l10n_util::GetStringUTF8(
832 IDS_EXTENSION_PACK_DIALOG_ERROR_ROOT_INVALID);
834 response.status = developer::PACK_STATUS_ERROR;
835 return RespondNow(OneArgument(response.ToValue()));
838 if (!key_path_str_.empty() && key_file.empty()) {
839 response.message = l10n_util::GetStringUTF8(
840 IDS_EXTENSION_PACK_DIALOG_ERROR_KEY_INVALID);
841 response.status = developer::PACK_STATUS_ERROR;
842 return RespondNow(OneArgument(response.ToValue()));
845 AddRef(); // Balanced in OnPackSuccess / OnPackFailure.
847 // TODO(devlin): Why is PackExtensionJob ref-counted?
848 pack_job_ = new PackExtensionJob(this, root_directory, key_file, flags);
849 pack_job_->Start();
850 return RespondLater();
853 DeveloperPrivatePackDirectoryFunction::DeveloperPrivatePackDirectoryFunction() {
856 DeveloperPrivatePackDirectoryFunction::
857 ~DeveloperPrivatePackDirectoryFunction() {}
859 DeveloperPrivateLoadUnpackedFunction::~DeveloperPrivateLoadUnpackedFunction() {}
861 bool DeveloperPrivateLoadDirectoryFunction::RunAsync() {
862 // TODO(grv) : add unittests.
863 std::string directory_url_str;
864 std::string filesystem_name;
865 std::string filesystem_path;
867 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &filesystem_name));
868 EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_path));
869 EXTENSION_FUNCTION_VALIDATE(args_->GetString(2, &directory_url_str));
871 context_ = content::BrowserContext::GetStoragePartition(
872 GetProfile(), render_frame_host()->GetSiteInstance())
873 ->GetFileSystemContext();
875 // Directory url is non empty only for syncfilesystem.
876 if (!directory_url_str.empty()) {
877 storage::FileSystemURL directory_url =
878 context_->CrackURL(GURL(directory_url_str));
879 if (!directory_url.is_valid() ||
880 directory_url.type() != storage::kFileSystemTypeSyncable) {
881 SetError("DirectoryEntry of unsupported filesystem.");
882 return false;
884 return LoadByFileSystemAPI(directory_url);
885 } else {
886 // Check if the DirecotryEntry is the instance of chrome filesystem.
887 if (!app_file_handler_util::ValidateFileEntryAndGetPath(
888 filesystem_name, filesystem_path,
889 render_frame_host()->GetProcess()->GetID(), &project_base_path_,
890 &error_)) {
891 SetError("DirectoryEntry of unsupported filesystem.");
892 return false;
895 // Try to load using the FileSystem API backend, in case the filesystem
896 // points to a non-native local directory.
897 std::string filesystem_id;
898 bool cracked =
899 storage::CrackIsolatedFileSystemName(filesystem_name, &filesystem_id);
900 CHECK(cracked);
901 base::FilePath virtual_path =
902 storage::IsolatedContext::GetInstance()
903 ->CreateVirtualRootPath(filesystem_id)
904 .Append(base::FilePath::FromUTF8Unsafe(filesystem_path));
905 storage::FileSystemURL directory_url = context_->CreateCrackedFileSystemURL(
906 extensions::Extension::GetBaseURLFromExtensionId(extension_id()),
907 storage::kFileSystemTypeIsolated,
908 virtual_path);
910 if (directory_url.is_valid() &&
911 directory_url.type() != storage::kFileSystemTypeNativeLocal &&
912 directory_url.type() != storage::kFileSystemTypeRestrictedNativeLocal &&
913 directory_url.type() != storage::kFileSystemTypeDragged) {
914 return LoadByFileSystemAPI(directory_url);
917 Load();
920 return true;
923 bool DeveloperPrivateLoadDirectoryFunction::LoadByFileSystemAPI(
924 const storage::FileSystemURL& directory_url) {
925 std::string directory_url_str = directory_url.ToGURL().spec();
927 size_t pos = 0;
928 // Parse the project directory name from the project url. The project url is
929 // expected to have project name as the suffix.
930 if ((pos = directory_url_str.rfind("/")) == std::string::npos) {
931 SetError("Invalid Directory entry.");
932 return false;
935 std::string project_name;
936 project_name = directory_url_str.substr(pos + 1);
937 project_base_url_ = directory_url_str.substr(0, pos + 1);
939 base::FilePath project_path(GetProfile()->GetPath());
940 project_path = project_path.AppendASCII(kUnpackedAppsFolder);
941 project_path = project_path.Append(
942 base::FilePath::FromUTF8Unsafe(project_name));
944 project_base_path_ = project_path;
946 content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE,
947 base::Bind(&DeveloperPrivateLoadDirectoryFunction::
948 ClearExistingDirectoryContent,
949 this,
950 project_base_path_));
951 return true;
954 void DeveloperPrivateLoadDirectoryFunction::Load() {
955 ExtensionService* service = GetExtensionService(GetProfile());
956 UnpackedInstaller::Create(service)->Load(project_base_path_);
958 // TODO(grv) : The unpacked installer should fire an event when complete
959 // and return the extension_id.
960 SetResult(new base::StringValue("-1"));
961 SendResponse(true);
964 void DeveloperPrivateLoadDirectoryFunction::ClearExistingDirectoryContent(
965 const base::FilePath& project_path) {
967 // Clear the project directory before copying new files.
968 base::DeleteFile(project_path, true /*recursive*/);
970 pending_copy_operations_count_ = 1;
972 content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE,
973 base::Bind(&DeveloperPrivateLoadDirectoryFunction::
974 ReadDirectoryByFileSystemAPI,
975 this, project_path, project_path.BaseName()));
978 void DeveloperPrivateLoadDirectoryFunction::ReadDirectoryByFileSystemAPI(
979 const base::FilePath& project_path,
980 const base::FilePath& destination_path) {
981 GURL project_url = GURL(project_base_url_ + destination_path.AsUTF8Unsafe());
982 storage::FileSystemURL url = context_->CrackURL(project_url);
984 context_->operation_runner()->ReadDirectory(
985 url, base::Bind(&DeveloperPrivateLoadDirectoryFunction::
986 ReadDirectoryByFileSystemAPICb,
987 this, project_path, destination_path));
990 void DeveloperPrivateLoadDirectoryFunction::ReadDirectoryByFileSystemAPICb(
991 const base::FilePath& project_path,
992 const base::FilePath& destination_path,
993 base::File::Error status,
994 const storage::FileSystemOperation::FileEntryList& file_list,
995 bool has_more) {
996 if (status != base::File::FILE_OK) {
997 DLOG(ERROR) << "Error in copying files from sync filesystem.";
998 return;
1001 // We add 1 to the pending copy operations for both files and directories. We
1002 // release the directory copy operation once all the files under the directory
1003 // are added for copying. We do that to ensure that pendingCopyOperationsCount
1004 // does not become zero before all copy operations are finished.
1005 // In case the directory happens to be executing the last copy operation it
1006 // will call SendResponse to send the response to the API. The pending copy
1007 // operations of files are released by the CopyFile function.
1008 pending_copy_operations_count_ += file_list.size();
1010 for (size_t i = 0; i < file_list.size(); ++i) {
1011 if (file_list[i].is_directory) {
1012 ReadDirectoryByFileSystemAPI(project_path.Append(file_list[i].name),
1013 destination_path.Append(file_list[i].name));
1014 continue;
1017 GURL project_url = GURL(project_base_url_ +
1018 destination_path.Append(file_list[i].name).AsUTF8Unsafe());
1019 storage::FileSystemURL url = context_->CrackURL(project_url);
1021 base::FilePath target_path = project_path;
1022 target_path = target_path.Append(file_list[i].name);
1024 context_->operation_runner()->CreateSnapshotFile(
1025 url,
1026 base::Bind(&DeveloperPrivateLoadDirectoryFunction::SnapshotFileCallback,
1027 this,
1028 target_path));
1031 if (!has_more) {
1032 // Directory copy operation released here.
1033 pending_copy_operations_count_--;
1035 if (!pending_copy_operations_count_) {
1036 content::BrowserThread::PostTask(
1037 content::BrowserThread::UI, FROM_HERE,
1038 base::Bind(&DeveloperPrivateLoadDirectoryFunction::SendResponse,
1039 this,
1040 success_));
1045 void DeveloperPrivateLoadDirectoryFunction::SnapshotFileCallback(
1046 const base::FilePath& target_path,
1047 base::File::Error result,
1048 const base::File::Info& file_info,
1049 const base::FilePath& src_path,
1050 const scoped_refptr<storage::ShareableFileReference>& file_ref) {
1051 if (result != base::File::FILE_OK) {
1052 SetError("Error in copying files from sync filesystem.");
1053 success_ = false;
1054 return;
1057 content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE,
1058 base::Bind(&DeveloperPrivateLoadDirectoryFunction::CopyFile,
1059 this,
1060 src_path,
1061 target_path));
1064 void DeveloperPrivateLoadDirectoryFunction::CopyFile(
1065 const base::FilePath& src_path,
1066 const base::FilePath& target_path) {
1067 if (!base::CreateDirectory(target_path.DirName())) {
1068 SetError("Error in copying files from sync filesystem.");
1069 success_ = false;
1072 if (success_)
1073 base::CopyFile(src_path, target_path);
1075 CHECK(pending_copy_operations_count_ > 0);
1076 pending_copy_operations_count_--;
1078 if (!pending_copy_operations_count_) {
1079 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
1080 base::Bind(&DeveloperPrivateLoadDirectoryFunction::Load,
1081 this));
1085 DeveloperPrivateLoadDirectoryFunction::DeveloperPrivateLoadDirectoryFunction()
1086 : pending_copy_operations_count_(0), success_(true) {}
1088 DeveloperPrivateLoadDirectoryFunction::~DeveloperPrivateLoadDirectoryFunction()
1091 ExtensionFunction::ResponseAction DeveloperPrivateChoosePathFunction::Run() {
1092 scoped_ptr<developer::ChoosePath::Params> params(
1093 developer::ChoosePath::Params::Create(*args_));
1094 EXTENSION_FUNCTION_VALIDATE(params);
1096 ui::SelectFileDialog::Type type = ui::SelectFileDialog::SELECT_FOLDER;
1097 ui::SelectFileDialog::FileTypeInfo info;
1099 if (params->select_type == developer::SELECT_TYPE_FILE)
1100 type = ui::SelectFileDialog::SELECT_OPEN_FILE;
1101 base::string16 select_title;
1103 int file_type_index = 0;
1104 if (params->file_type == developer::FILE_TYPE_LOAD) {
1105 select_title = l10n_util::GetStringUTF16(IDS_EXTENSION_LOAD_FROM_DIRECTORY);
1106 } else if (params->file_type == developer::FILE_TYPE_PEM) {
1107 select_title = l10n_util::GetStringUTF16(
1108 IDS_EXTENSION_PACK_DIALOG_SELECT_KEY);
1109 info.extensions.push_back(std::vector<base::FilePath::StringType>(
1110 1, FILE_PATH_LITERAL("pem")));
1111 info.extension_description_overrides.push_back(
1112 l10n_util::GetStringUTF16(
1113 IDS_EXTENSION_PACK_DIALOG_KEY_FILE_TYPE_DESCRIPTION));
1114 info.include_all_files = true;
1115 file_type_index = 1;
1116 } else {
1117 NOTREACHED();
1120 if (!ShowPicker(
1121 type,
1122 select_title,
1123 info,
1124 file_type_index)) {
1125 return RespondNow(Error(kCouldNotShowSelectFileDialogError));
1128 AddRef(); // Balanced by FileSelected / FileSelectionCanceled.
1129 return RespondLater();
1132 void DeveloperPrivateChoosePathFunction::FileSelected(
1133 const base::FilePath& path) {
1134 Respond(OneArgument(new base::StringValue(path.LossyDisplayName())));
1135 Release();
1138 void DeveloperPrivateChoosePathFunction::FileSelectionCanceled() {
1139 // This isn't really an error, but we should keep it like this for
1140 // backward compatability.
1141 Respond(Error(kFileSelectionCanceled));
1142 Release();
1145 DeveloperPrivateChoosePathFunction::~DeveloperPrivateChoosePathFunction() {}
1147 bool DeveloperPrivateIsProfileManagedFunction::RunSync() {
1148 SetResult(new base::FundamentalValue(GetProfile()->IsSupervised()));
1149 return true;
1152 DeveloperPrivateIsProfileManagedFunction::
1153 ~DeveloperPrivateIsProfileManagedFunction() {
1156 DeveloperPrivateRequestFileSourceFunction::
1157 DeveloperPrivateRequestFileSourceFunction() {}
1159 DeveloperPrivateRequestFileSourceFunction::
1160 ~DeveloperPrivateRequestFileSourceFunction() {}
1162 ExtensionFunction::ResponseAction
1163 DeveloperPrivateRequestFileSourceFunction::Run() {
1164 params_ = developer::RequestFileSource::Params::Create(*args_);
1165 EXTENSION_FUNCTION_VALIDATE(params_);
1167 const developer::RequestFileSourceProperties& properties =
1168 params_->properties;
1169 const Extension* extension = GetExtensionById(properties.extension_id);
1170 if (!extension)
1171 return RespondNow(Error(kNoSuchExtensionError));
1173 // Under no circumstances should we ever need to reference a file outside of
1174 // the extension's directory. If it tries to, abort.
1175 base::FilePath path_suffix =
1176 base::FilePath::FromUTF8Unsafe(properties.path_suffix);
1177 if (path_suffix.empty() || path_suffix.ReferencesParent())
1178 return RespondNow(Error(kInvalidPathError));
1180 if (properties.path_suffix == kManifestFile && !properties.manifest_key)
1181 return RespondNow(Error(kManifestKeyIsRequiredError));
1183 base::PostTaskAndReplyWithResult(
1184 content::BrowserThread::GetBlockingPool(),
1185 FROM_HERE,
1186 base::Bind(&ReadFileToString, extension->path().Append(path_suffix)),
1187 base::Bind(&DeveloperPrivateRequestFileSourceFunction::Finish, this));
1189 return RespondLater();
1192 void DeveloperPrivateRequestFileSourceFunction::Finish(
1193 const std::string& file_contents) {
1194 const developer::RequestFileSourceProperties& properties =
1195 params_->properties;
1196 const Extension* extension = GetExtensionById(properties.extension_id);
1197 if (!extension) {
1198 Respond(Error(kNoSuchExtensionError));
1199 return;
1202 developer::RequestFileSourceResponse response;
1203 base::FilePath path_suffix =
1204 base::FilePath::FromUTF8Unsafe(properties.path_suffix);
1205 base::FilePath path = extension->path().Append(path_suffix);
1206 response.title = base::StringPrintf("%s: %s",
1207 extension->name().c_str(),
1208 path.BaseName().AsUTF8Unsafe().c_str());
1209 response.message = properties.message;
1211 scoped_ptr<FileHighlighter> highlighter;
1212 if (properties.path_suffix == kManifestFile) {
1213 highlighter.reset(new ManifestHighlighter(
1214 file_contents,
1215 *properties.manifest_key,
1216 properties.manifest_specific ?
1217 *properties.manifest_specific : std::string()));
1218 } else {
1219 highlighter.reset(new SourceHighlighter(
1220 file_contents,
1221 properties.line_number ? *properties.line_number : 0));
1224 response.before_highlight = highlighter->GetBeforeFeature();
1225 response.highlight = highlighter->GetFeature();
1226 response.after_highlight = highlighter->GetAfterFeature();
1228 Respond(OneArgument(response.ToValue()));
1231 DeveloperPrivateOpenDevToolsFunction::DeveloperPrivateOpenDevToolsFunction() {}
1232 DeveloperPrivateOpenDevToolsFunction::~DeveloperPrivateOpenDevToolsFunction() {}
1234 ExtensionFunction::ResponseAction
1235 DeveloperPrivateOpenDevToolsFunction::Run() {
1236 scoped_ptr<developer::OpenDevTools::Params> params(
1237 developer::OpenDevTools::Params::Create(*args_));
1238 EXTENSION_FUNCTION_VALIDATE(params);
1239 const developer::OpenDevToolsProperties& properties = params->properties;
1241 if (properties.render_process_id == -1) {
1242 // This is a lazy background page.
1243 const Extension* extension = properties.extension_id ?
1244 GetEnabledExtensionById(*properties.extension_id) : nullptr;
1245 if (!extension)
1246 return RespondNow(Error(kNoSuchExtensionError));
1248 Profile* profile = GetProfile();
1249 if (properties.incognito && *properties.incognito)
1250 profile = profile->GetOffTheRecordProfile();
1252 // Wakes up the background page and opens the inspect window.
1253 devtools_util::InspectBackgroundPage(extension, profile);
1254 return RespondNow(NoArguments());
1257 // NOTE(devlin): Even though the properties use "render_view_id", this
1258 // actually refers to a render frame.
1259 content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
1260 properties.render_process_id, properties.render_view_id);
1262 content::WebContents* web_contents =
1263 rfh ? content::WebContents::FromRenderFrameHost(rfh) : nullptr;
1264 // It's possible that the render frame was closed since we last updated the
1265 // links. Handle this gracefully.
1266 if (!web_contents)
1267 return RespondNow(Error(kNoSuchRendererError));
1269 // If we include a url, we should inspect it specifically (and not just the
1270 // render frame).
1271 if (properties.url) {
1272 // Line/column numbers are reported in display-friendly 1-based numbers,
1273 // but are inspected in zero-based numbers.
1274 // Default to the first line/column.
1275 DevToolsWindow::OpenDevToolsWindow(
1276 web_contents,
1277 DevToolsToggleAction::Reveal(
1278 base::UTF8ToUTF16(*properties.url),
1279 properties.line_number ? *properties.line_number - 1 : 0,
1280 properties.column_number ? *properties.column_number - 1 : 0));
1281 } else {
1282 DevToolsWindow::OpenDevToolsWindow(web_contents);
1285 // Once we open the inspector, we focus on the appropriate tab...
1286 Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
1288 // ... but some pages (popups and apps) don't have tabs, and some (background
1289 // pages) don't have an associated browser. For these, the inspector opens in
1290 // a new window, and our work is done.
1291 if (!browser || !browser->is_type_tabbed())
1292 return RespondNow(NoArguments());
1294 TabStripModel* tab_strip = browser->tab_strip_model();
1295 tab_strip->ActivateTabAt(tab_strip->GetIndexOfWebContents(web_contents),
1296 false); // Not through direct user gesture.
1297 return RespondNow(NoArguments());
1300 DeveloperPrivateDeleteExtensionErrorsFunction::
1301 ~DeveloperPrivateDeleteExtensionErrorsFunction() {}
1303 ExtensionFunction::ResponseAction
1304 DeveloperPrivateDeleteExtensionErrorsFunction::Run() {
1305 scoped_ptr<developer::DeleteExtensionErrors::Params> params(
1306 developer::DeleteExtensionErrors::Params::Create(*args_));
1307 EXTENSION_FUNCTION_VALIDATE(params);
1308 const developer::DeleteExtensionErrorsProperties& properties =
1309 params->properties;
1311 ErrorConsole* error_console = ErrorConsole::Get(GetProfile());
1312 int type = -1;
1313 if (properties.type != developer::ERROR_TYPE_NONE) {
1314 type = properties.type == developer::ERROR_TYPE_MANIFEST ?
1315 ExtensionError::MANIFEST_ERROR : ExtensionError::RUNTIME_ERROR;
1317 std::set<int> error_ids;
1318 if (properties.error_ids) {
1319 error_ids.insert(properties.error_ids->begin(),
1320 properties.error_ids->end());
1322 error_console->RemoveErrors(ErrorMap::Filter(
1323 properties.extension_id, type, error_ids, false));
1325 return RespondNow(NoArguments());
1328 DeveloperPrivateRepairExtensionFunction::
1329 ~DeveloperPrivateRepairExtensionFunction() {}
1331 ExtensionFunction::ResponseAction
1332 DeveloperPrivateRepairExtensionFunction::Run() {
1333 scoped_ptr<developer::RepairExtension::Params> params(
1334 developer::RepairExtension::Params::Create(*args_));
1335 EXTENSION_FUNCTION_VALIDATE(params);
1336 const Extension* extension = GetExtensionById(params->extension_id);
1337 if (!extension)
1338 return RespondNow(Error(kNoSuchExtensionError));
1340 content::WebContents* web_contents = GetSenderWebContents();
1341 if (!web_contents)
1342 return RespondNow(Error(kCouldNotFindWebContentsError));
1344 scoped_refptr<WebstoreReinstaller> reinstaller(new WebstoreReinstaller(
1345 web_contents,
1346 params->extension_id,
1347 base::Bind(&DeveloperPrivateRepairExtensionFunction::OnReinstallComplete,
1348 this)));
1349 reinstaller->BeginReinstall();
1351 return RespondLater();
1354 void DeveloperPrivateRepairExtensionFunction::OnReinstallComplete(
1355 bool success,
1356 const std::string& error,
1357 webstore_install::Result result) {
1358 Respond(success ? NoArguments() : Error(error));
1361 DeveloperPrivateShowOptionsFunction::~DeveloperPrivateShowOptionsFunction() {}
1363 ExtensionFunction::ResponseAction DeveloperPrivateShowOptionsFunction::Run() {
1364 scoped_ptr<developer::ShowOptions::Params> params(
1365 developer::ShowOptions::Params::Create(*args_));
1366 EXTENSION_FUNCTION_VALIDATE(params);
1367 const Extension* extension = GetEnabledExtensionById(params->extension_id);
1368 if (!extension)
1369 return RespondNow(Error(kNoSuchExtensionError));
1371 if (OptionsPageInfo::GetOptionsPage(extension).is_empty())
1372 return RespondNow(Error(kNoOptionsPageForExtensionError));
1374 content::WebContents* web_contents = GetSenderWebContents();
1375 if (!web_contents)
1376 return RespondNow(Error(kCouldNotFindWebContentsError));
1378 ExtensionTabUtil::OpenOptionsPage(
1379 extension,
1380 chrome::FindBrowserWithWebContents(web_contents));
1381 return RespondNow(NoArguments());
1384 DeveloperPrivateShowPathFunction::~DeveloperPrivateShowPathFunction() {}
1386 ExtensionFunction::ResponseAction DeveloperPrivateShowPathFunction::Run() {
1387 scoped_ptr<developer::ShowPath::Params> params(
1388 developer::ShowPath::Params::Create(*args_));
1389 EXTENSION_FUNCTION_VALIDATE(params);
1390 const Extension* extension = GetExtensionById(params->extension_id);
1391 if (!extension)
1392 return RespondNow(Error(kNoSuchExtensionError));
1394 // We explicitly show manifest.json in order to work around an issue in OSX
1395 // where opening the directory doesn't focus the Finder.
1396 platform_util::ShowItemInFolder(GetProfile(),
1397 extension->path().Append(kManifestFilename));
1398 return RespondNow(NoArguments());
1401 DeveloperPrivateSetShortcutHandlingSuspendedFunction::
1402 ~DeveloperPrivateSetShortcutHandlingSuspendedFunction() {}
1404 ExtensionFunction::ResponseAction
1405 DeveloperPrivateSetShortcutHandlingSuspendedFunction::Run() {
1406 scoped_ptr<developer::SetShortcutHandlingSuspended::Params> params(
1407 developer::SetShortcutHandlingSuspended::Params::Create(*args_));
1408 EXTENSION_FUNCTION_VALIDATE(params);
1409 ExtensionCommandsGlobalRegistry::Get(GetProfile())->
1410 SetShortcutHandlingSuspended(params->is_suspended);
1411 return RespondNow(NoArguments());
1414 DeveloperPrivateUpdateExtensionCommandFunction::
1415 ~DeveloperPrivateUpdateExtensionCommandFunction() {}
1417 ExtensionFunction::ResponseAction
1418 DeveloperPrivateUpdateExtensionCommandFunction::Run() {
1419 scoped_ptr<developer::UpdateExtensionCommand::Params> params(
1420 developer::UpdateExtensionCommand::Params::Create(*args_));
1421 EXTENSION_FUNCTION_VALIDATE(params);
1422 const developer::ExtensionCommandUpdate& update = params->update;
1424 CommandService* command_service = CommandService::Get(GetProfile());
1426 if (update.scope != developer::COMMAND_SCOPE_NONE) {
1427 command_service->SetScope(update.extension_id, update.command_name,
1428 update.scope == developer::COMMAND_SCOPE_GLOBAL);
1431 if (update.keybinding) {
1432 command_service->UpdateKeybindingPrefs(
1433 update.extension_id, update.command_name, *update.keybinding);
1436 return RespondNow(NoArguments());
1440 } // namespace api
1442 } // namespace extensions