developer_private_api.cc revision 46d4c2bc3267f3f028f39e7e311b0f89aba2e4fd
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/extensions/api/developer_private/developer_private_api.h"
6
7#include "apps/app_load_service.h"
8#include "apps/app_restore_service.h"
9#include "apps/app_window.h"
10#include "apps/app_window_registry.h"
11#include "apps/saved_files_service.h"
12#include "base/base64.h"
13#include "base/bind.h"
14#include "base/command_line.h"
15#include "base/file_util.h"
16#include "base/files/file_enumerator.h"
17#include "base/i18n/file_util_icu.h"
18#include "base/lazy_instance.h"
19#include "base/strings/string_number_conversions.h"
20#include "base/strings/utf_string_conversions.h"
21#include "base/values.h"
22#include "chrome/browser/chrome_notification_types.h"
23#include "chrome/browser/devtools/devtools_window.h"
24#include "chrome/browser/extensions/api/developer_private/entry_picker.h"
25#include "chrome/browser/extensions/api/extension_action/extension_action_api.h"
26#include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h"
27#include "chrome/browser/extensions/devtools_util.h"
28#include "chrome/browser/extensions/extension_disabled_ui.h"
29#include "chrome/browser/extensions/extension_error_reporter.h"
30#include "chrome/browser/extensions/extension_service.h"
31#include "chrome/browser/extensions/extension_ui_util.h"
32#include "chrome/browser/extensions/extension_util.h"
33#include "chrome/browser/extensions/unpacked_installer.h"
34#include "chrome/browser/extensions/updater/extension_updater.h"
35#include "chrome/browser/platform_util.h"
36#include "chrome/browser/profiles/profile.h"
37#include "chrome/browser/sync_file_system/drive_backend_v1/drive_file_sync_service.h"
38#include "chrome/browser/sync_file_system/syncable_file_system_util.h"
39#include "chrome/browser/ui/chrome_select_file_policy.h"
40#include "chrome/browser/ui/webui/extensions/extension_error_ui_util.h"
41#include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
42#include "chrome/common/extensions/api/developer_private.h"
43#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
44#include "chrome/common/extensions/manifest_url_handler.h"
45#include "chrome/common/url_constants.h"
46#include "content/public/browser/browser_thread.h"
47#include "content/public/browser/notification_service.h"
48#include "content/public/browser/render_process_host.h"
49#include "content/public/browser/render_view_host.h"
50#include "content/public/browser/site_instance.h"
51#include "content/public/browser/storage_partition.h"
52#include "content/public/browser/web_contents.h"
53#include "extensions/browser/extension_error.h"
54#include "extensions/browser/extension_prefs.h"
55#include "extensions/browser/extension_registry.h"
56#include "extensions/browser/extension_system.h"
57#include "extensions/browser/management_policy.h"
58#include "extensions/browser/view_type_utils.h"
59#include "extensions/common/constants.h"
60#include "extensions/common/extension_resource.h"
61#include "extensions/common/extension_set.h"
62#include "extensions/common/install_warning.h"
63#include "extensions/common/manifest.h"
64#include "extensions/common/manifest_handlers/background_info.h"
65#include "extensions/common/manifest_handlers/icons_handler.h"
66#include "extensions/common/manifest_handlers/incognito_info.h"
67#include "extensions/common/manifest_handlers/offline_enabled_info.h"
68#include "extensions/common/permissions/permissions_data.h"
69#include "extensions/common/switches.h"
70#include "grit/chromium_strings.h"
71#include "grit/generated_resources.h"
72#include "grit/theme_resources.h"
73#include "net/base/net_util.h"
74#include "ui/base/l10n/l10n_util.h"
75#include "ui/base/resource/resource_bundle.h"
76#include "ui/base/webui/web_ui_util.h"
77#include "webkit/browser/fileapi/external_mount_points.h"
78#include "webkit/browser/fileapi/file_system_context.h"
79#include "webkit/browser/fileapi/file_system_operation.h"
80#include "webkit/browser/fileapi/file_system_operation_runner.h"
81#include "webkit/common/blob/shareable_file_reference.h"
82
83using apps::AppWindow;
84using apps::AppWindowRegistry;
85using content::RenderViewHost;
86
87namespace extensions {
88
89namespace developer_private = api::developer_private;
90
91namespace {
92
93const base::FilePath::CharType kUnpackedAppsFolder[]
94    = FILE_PATH_LITERAL("apps_target");
95
96ExtensionUpdater* GetExtensionUpdater(Profile* profile) {
97    return profile->GetExtensionService()->updater();
98}
99
100GURL GetImageURLFromData(const std::string& contents) {
101  std::string contents_base64;
102  base::Base64Encode(contents, &contents_base64);
103
104  // TODO(dvh): make use of content::kDataScheme. Filed as crbug/297301.
105  const char kDataURLPrefix[] = "data:;base64,";
106  return GURL(kDataURLPrefix + contents_base64);
107}
108
109GURL GetDefaultImageURL(developer_private::ItemType type) {
110  int icon_resource_id;
111  switch (type) {
112    case developer::ITEM_TYPE_LEGACY_PACKAGED_APP:
113    case developer::ITEM_TYPE_HOSTED_APP:
114    case developer::ITEM_TYPE_PACKAGED_APP:
115      icon_resource_id = IDR_APP_DEFAULT_ICON;
116      break;
117    default:
118      icon_resource_id = IDR_EXTENSION_DEFAULT_ICON;
119      break;
120  }
121
122  return GetImageURLFromData(
123      ResourceBundle::GetSharedInstance().GetRawDataResourceForScale(
124          icon_resource_id, ui::SCALE_FACTOR_100P).as_string());
125}
126
127// TODO(dvh): This code should be refactored and moved to
128// extensions::ImageLoader. Also a resize should be performed to avoid
129// potential huge URLs: crbug/297298.
130GURL ToDataURL(const base::FilePath& path, developer_private::ItemType type) {
131  std::string contents;
132  if (path.empty() || !base::ReadFileToString(path, &contents))
133    return GetDefaultImageURL(type);
134
135  return GetImageURLFromData(contents);
136}
137
138std::string GetExtensionID(const RenderViewHost* render_view_host) {
139  if (!render_view_host->GetSiteInstance())
140    return std::string();
141
142  return render_view_host->GetSiteInstance()->GetSiteURL().host();
143}
144
145void BroadcastItemStateChanged(content::BrowserContext* browser_context,
146                               developer::EventType event_type,
147                               const std::string& item_id) {
148  developer::EventData event_data;
149  event_data.event_type = event_type;
150  event_data.item_id = item_id;
151
152  scoped_ptr<base::ListValue> args(new base::ListValue());
153  args->Append(event_data.ToValue().release());
154  scoped_ptr<Event> event(new Event(
155      developer_private::OnItemStateChanged::kEventName, args.Pass()));
156  EventRouter::Get(browser_context)->BroadcastEvent(event.Pass());
157}
158
159}  // namespace
160
161namespace AllowFileAccess = api::developer_private::AllowFileAccess;
162namespace AllowIncognito = api::developer_private::AllowIncognito;
163namespace ChoosePath = api::developer_private::ChoosePath;
164namespace Enable = api::developer_private::Enable;
165namespace GetItemsInfo = api::developer_private::GetItemsInfo;
166namespace Inspect = api::developer_private::Inspect;
167namespace PackDirectory = api::developer_private::PackDirectory;
168namespace Reload = api::developer_private::Reload;
169
170static base::LazyInstance<BrowserContextKeyedAPIFactory<DeveloperPrivateAPI> >
171    g_factory = LAZY_INSTANCE_INITIALIZER;
172
173// static
174BrowserContextKeyedAPIFactory<DeveloperPrivateAPI>*
175DeveloperPrivateAPI::GetFactoryInstance() {
176  return g_factory.Pointer();
177}
178
179// static
180DeveloperPrivateAPI* DeveloperPrivateAPI::Get(
181    content::BrowserContext* context) {
182  return GetFactoryInstance()->Get(context);
183}
184
185DeveloperPrivateAPI::DeveloperPrivateAPI(content::BrowserContext* context)
186    : profile_(Profile::FromBrowserContext(context)) {
187  RegisterNotifications();
188}
189
190DeveloperPrivateEventRouter::DeveloperPrivateEventRouter(Profile* profile)
191    : extension_registry_observer_(this), profile_(profile) {
192  registrar_.Add(this,
193                 chrome::NOTIFICATION_EXTENSION_VIEW_REGISTERED,
194                 content::Source<Profile>(profile_));
195  registrar_.Add(this,
196                 chrome::NOTIFICATION_EXTENSION_VIEW_UNREGISTERED,
197                 content::Source<Profile>(profile_));
198
199  // TODO(limasdf): Use scoped_observer instead.
200  ErrorConsole::Get(profile)->AddObserver(this);
201
202  extension_registry_observer_.Add(ExtensionRegistry::Get(profile_));
203}
204
205DeveloperPrivateEventRouter::~DeveloperPrivateEventRouter() {
206  ErrorConsole::Get(profile_)->RemoveObserver(this);
207}
208
209void DeveloperPrivateEventRouter::AddExtensionId(
210    const std::string& extension_id) {
211  extension_ids_.insert(extension_id);
212}
213
214void DeveloperPrivateEventRouter::RemoveExtensionId(
215    const std::string& extension_id) {
216  extension_ids_.erase(extension_id);
217}
218
219void DeveloperPrivateEventRouter::Observe(
220    int type,
221    const content::NotificationSource& source,
222    const content::NotificationDetails& details) {
223  Profile* profile = content::Source<Profile>(source).ptr();
224  CHECK(profile);
225  CHECK(profile_->IsSameProfile(profile));
226  developer::EventData event_data;
227
228  switch (type) {
229    case chrome::NOTIFICATION_EXTENSION_VIEW_UNREGISTERED: {
230      event_data.event_type = developer::EVENT_TYPE_VIEW_UNREGISTERED;
231      event_data.item_id = GetExtensionID(
232          content::Details<const RenderViewHost>(details).ptr());
233      break;
234    }
235    case chrome::NOTIFICATION_EXTENSION_VIEW_REGISTERED: {
236      event_data.event_type = developer::EVENT_TYPE_VIEW_REGISTERED;
237      event_data.item_id = GetExtensionID(
238          content::Details<const RenderViewHost>(details).ptr());
239      break;
240    }
241    default:
242      NOTREACHED();
243      return;
244  }
245
246  BroadcastItemStateChanged(profile, event_data.event_type, event_data.item_id);
247}
248
249void DeveloperPrivateEventRouter::OnExtensionLoaded(
250    content::BrowserContext* browser_context,
251    const Extension* extension) {
252  DCHECK(profile_->IsSameProfile(Profile::FromBrowserContext(browser_context)));
253  BroadcastItemStateChanged(
254      browser_context, developer::EVENT_TYPE_LOADED, extension->id());
255}
256
257void DeveloperPrivateEventRouter::OnExtensionUnloaded(
258    content::BrowserContext* browser_context,
259    const Extension* extension,
260    UnloadedExtensionInfo::Reason reason) {
261  DCHECK(profile_->IsSameProfile(Profile::FromBrowserContext(browser_context)));
262  BroadcastItemStateChanged(
263      browser_context, developer::EVENT_TYPE_UNLOADED, extension->id());
264}
265
266void DeveloperPrivateEventRouter::OnExtensionWillBeInstalled(
267    content::BrowserContext* browser_context,
268    const Extension* extension,
269    bool is_update,
270    bool from_ephemeral,
271    const std::string& old_name) {
272  DCHECK(profile_->IsSameProfile(Profile::FromBrowserContext(browser_context)));
273  BroadcastItemStateChanged(
274      browser_context, developer::EVENT_TYPE_INSTALLED, extension->id());
275}
276
277void DeveloperPrivateEventRouter::OnExtensionUninstalled(
278    content::BrowserContext* browser_context,
279    const Extension* extension) {
280  DCHECK(profile_->IsSameProfile(Profile::FromBrowserContext(browser_context)));
281  BroadcastItemStateChanged(
282      browser_context, developer::EVENT_TYPE_UNINSTALLED, extension->id());
283}
284
285void DeveloperPrivateEventRouter::OnErrorAdded(const ExtensionError* error) {
286  // We don't want to handle errors thrown by extensions subscribed to these
287  // events (currently only the Apps Developer Tool), because doing so risks
288  // entering a loop.
289  if (extension_ids_.find(error->extension_id()) != extension_ids_.end())
290    return;
291
292  BroadcastItemStateChanged(
293      profile_, developer::EVENT_TYPE_ERROR_ADDED, error->extension_id());
294}
295
296void DeveloperPrivateAPI::SetLastUnpackedDirectory(const base::FilePath& path) {
297  last_unpacked_directory_ = path;
298}
299
300void DeveloperPrivateAPI::RegisterNotifications() {
301  EventRouter::Get(profile_)->RegisterObserver(
302      this, developer_private::OnItemStateChanged::kEventName);
303}
304
305DeveloperPrivateAPI::~DeveloperPrivateAPI() {}
306
307void DeveloperPrivateAPI::Shutdown() {}
308
309void DeveloperPrivateAPI::OnListenerAdded(
310    const EventListenerInfo& details) {
311  if (!developer_private_event_router_) {
312    developer_private_event_router_.reset(
313        new DeveloperPrivateEventRouter(profile_));
314  }
315
316  developer_private_event_router_->AddExtensionId(details.extension_id);
317}
318
319void DeveloperPrivateAPI::OnListenerRemoved(
320    const EventListenerInfo& details) {
321  if (!EventRouter::Get(profile_)->HasEventListener(
322          developer_private::OnItemStateChanged::kEventName)) {
323    developer_private_event_router_.reset(NULL);
324  } else {
325    developer_private_event_router_->RemoveExtensionId(details.extension_id);
326  }
327}
328
329namespace api {
330
331bool DeveloperPrivateAutoUpdateFunction::RunSync() {
332  ExtensionUpdater* updater = GetExtensionUpdater(GetProfile());
333  if (updater)
334    updater->CheckNow(ExtensionUpdater::CheckParams());
335  SetResult(new base::FundamentalValue(true));
336  return true;
337}
338
339DeveloperPrivateAutoUpdateFunction::~DeveloperPrivateAutoUpdateFunction() {}
340
341scoped_ptr<developer::ItemInfo>
342DeveloperPrivateGetItemsInfoFunction::CreateItemInfo(const Extension& item,
343                                                     bool item_is_enabled) {
344  scoped_ptr<developer::ItemInfo> info(new developer::ItemInfo());
345
346  ExtensionSystem* system = ExtensionSystem::Get(GetProfile());
347  ExtensionService* service = system->extension_service();
348  ExtensionRegistry* registry = ExtensionRegistry::Get(GetProfile());
349
350  info->id = item.id();
351  info->name = item.name();
352  info->enabled = service->IsExtensionEnabled(info->id);
353  info->offline_enabled = OfflineEnabledInfo::IsOfflineEnabled(&item);
354  info->version = item.VersionString();
355  info->description = item.description();
356
357  if (item.is_app()) {
358    if (item.is_legacy_packaged_app())
359      info->type = developer::ITEM_TYPE_LEGACY_PACKAGED_APP;
360    else if (item.is_hosted_app())
361      info->type = developer::ITEM_TYPE_HOSTED_APP;
362    else if (item.is_platform_app())
363      info->type = developer::ITEM_TYPE_PACKAGED_APP;
364    else
365      NOTREACHED();
366  } else if (item.is_theme()) {
367    info->type = developer::ITEM_TYPE_THEME;
368  } else if (item.is_extension()) {
369    info->type = developer::ITEM_TYPE_EXTENSION;
370  } else {
371    NOTREACHED();
372  }
373
374  if (Manifest::IsUnpackedLocation(item.location())) {
375    info->path.reset(
376        new std::string(base::UTF16ToUTF8(item.path().LossyDisplayName())));
377    // If the ErrorConsole is enabled and the extension is unpacked, use the
378    // more detailed errors from the ErrorConsole. Otherwise, use the install
379    // warnings (using both is redundant).
380    ErrorConsole* error_console = ErrorConsole::Get(GetProfile());
381    if (error_console->IsEnabledForAppsDeveloperTools() &&
382        item.location() == Manifest::UNPACKED) {
383      const ErrorList& errors = error_console->GetErrorsForExtension(item.id());
384      if (!errors.empty()) {
385        for (ErrorList::const_iterator iter = errors.begin();
386             iter != errors.end();
387             ++iter) {
388          switch ((*iter)->type()) {
389            case ExtensionError::MANIFEST_ERROR:
390              info->manifest_errors.push_back(
391                  make_linked_ptr((*iter)->ToValue().release()));
392              break;
393            case ExtensionError::RUNTIME_ERROR: {
394              const RuntimeError* error =
395                  static_cast<const RuntimeError*>(*iter);
396              scoped_ptr<base::DictionaryValue> value = error->ToValue();
397              bool can_inspect = content::RenderViewHost::FromID(
398                                     error->render_process_id(),
399                                     error->render_view_id()) != NULL;
400              value->SetBoolean("canInspect", can_inspect);
401              info->runtime_errors.push_back(make_linked_ptr(value.release()));
402              break;
403            }
404            case ExtensionError::NUM_ERROR_TYPES:
405              NOTREACHED();
406              break;
407          }
408        }
409      }
410    } else {
411      for (std::vector<InstallWarning>::const_iterator it =
412               item.install_warnings().begin();
413           it != item.install_warnings().end();
414           ++it) {
415        scoped_ptr<developer::InstallWarning> warning(
416            new developer::InstallWarning);
417        warning->message = it->message;
418        info->install_warnings.push_back(make_linked_ptr(warning.release()));
419      }
420    }
421  }
422
423  info->incognito_enabled = util::IsIncognitoEnabled(item.id(), GetProfile());
424  info->wants_file_access = item.wants_file_access();
425  info->allow_file_access = util::AllowFileAccess(item.id(), GetProfile());
426  info->allow_reload = Manifest::IsUnpackedLocation(item.location());
427  info->is_unpacked = Manifest::IsUnpackedLocation(item.location());
428  info->terminated = registry->terminated_extensions().Contains(item.id());
429  info->allow_incognito = item.can_be_incognito_enabled();
430
431  info->homepage_url.reset(new std::string(
432      ManifestURL::GetHomepageURL(&item).spec()));
433  if (!ManifestURL::GetOptionsPage(&item).is_empty()) {
434    info->options_url.reset(
435        new std::string(ManifestURL::GetOptionsPage(&item).spec()));
436  }
437
438  if (!ManifestURL::GetUpdateURL(&item).is_empty()) {
439    info->update_url.reset(
440        new std::string(ManifestURL::GetUpdateURL(&item).spec()));
441  }
442
443  if (item.is_app()) {
444    info->app_launch_url.reset(
445        new std::string(AppLaunchInfo::GetFullLaunchURL(&item).spec()));
446  }
447
448  info->may_disable = system->management_policy()->
449      UserMayModifySettings(&item, NULL);
450  info->is_app = item.is_app();
451  info->views = GetInspectablePagesForExtension(&item, item_is_enabled);
452
453  return info.Pass();
454}
455
456void DeveloperPrivateGetItemsInfoFunction::GetIconsOnFileThread(
457    ItemInfoList item_list,
458    const std::map<std::string, ExtensionResource> idToIcon) {
459  for (ItemInfoList::iterator iter = item_list.begin();
460       iter != item_list.end(); ++iter) {
461    developer_private::ItemInfo* info = iter->get();
462    std::map<std::string, ExtensionResource>::const_iterator resource_ptr
463        = idToIcon.find(info->id);
464    if (resource_ptr != idToIcon.end()) {
465      info->icon_url =
466          ToDataURL(resource_ptr->second.GetFilePath(), info->type).spec();
467    }
468  }
469
470  results_ = developer::GetItemsInfo::Results::Create(item_list);
471  content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
472      base::Bind(&DeveloperPrivateGetItemsInfoFunction::SendResponse,
473                 this,
474                 true));
475}
476
477void DeveloperPrivateGetItemsInfoFunction::
478    GetInspectablePagesForExtensionProcess(
479        const Extension* extension,
480        const std::set<content::RenderViewHost*>& views,
481        ItemInspectViewList* result) {
482  bool has_generated_background_page =
483      BackgroundInfo::HasGeneratedBackgroundPage(extension);
484  for (std::set<content::RenderViewHost*>::const_iterator iter = views.begin();
485       iter != views.end(); ++iter) {
486    content::RenderViewHost* host = *iter;
487    content::WebContents* web_contents =
488        content::WebContents::FromRenderViewHost(host);
489    ViewType host_type = GetViewType(web_contents);
490    if (VIEW_TYPE_EXTENSION_POPUP == host_type ||
491        VIEW_TYPE_EXTENSION_DIALOG == host_type)
492      continue;
493
494    content::RenderProcessHost* process = host->GetProcess();
495    bool is_background_page =
496        (web_contents->GetURL() == BackgroundInfo::GetBackgroundURL(extension));
497    result->push_back(constructInspectView(
498        web_contents->GetURL(),
499        process->GetID(),
500        host->GetRoutingID(),
501        process->GetBrowserContext()->IsOffTheRecord(),
502        is_background_page && has_generated_background_page));
503  }
504}
505
506void DeveloperPrivateGetItemsInfoFunction::GetAppWindowPagesForExtensionProfile(
507    const Extension* extension,
508    ItemInspectViewList* result) {
509  AppWindowRegistry* registry = AppWindowRegistry::Get(GetProfile());
510  if (!registry) return;
511
512  const AppWindowRegistry::AppWindowList windows =
513      registry->GetAppWindowsForApp(extension->id());
514
515  bool has_generated_background_page =
516      BackgroundInfo::HasGeneratedBackgroundPage(extension);
517  for (AppWindowRegistry::const_iterator it = windows.begin();
518       it != windows.end();
519       ++it) {
520    content::WebContents* web_contents = (*it)->web_contents();
521    RenderViewHost* host = web_contents->GetRenderViewHost();
522    content::RenderProcessHost* process = host->GetProcess();
523    bool is_background_page =
524        (web_contents->GetURL() == BackgroundInfo::GetBackgroundURL(extension));
525    result->push_back(constructInspectView(
526        web_contents->GetURL(),
527        process->GetID(),
528        host->GetRoutingID(),
529        process->GetBrowserContext()->IsOffTheRecord(),
530        is_background_page && has_generated_background_page));
531  }
532}
533
534linked_ptr<developer::ItemInspectView> DeveloperPrivateGetItemsInfoFunction::
535    constructInspectView(
536        const GURL& url,
537        int render_process_id,
538        int render_view_id,
539        bool incognito,
540        bool generated_background_page) {
541  linked_ptr<developer::ItemInspectView> view(new developer::ItemInspectView());
542
543  if (url.scheme() == kExtensionScheme) {
544    // No leading slash.
545    view->path = url.path().substr(1);
546  } else {
547    // For live pages, use the full URL.
548    view->path = url.spec();
549  }
550
551  view->render_process_id = render_process_id;
552  view->render_view_id = render_view_id;
553  view->incognito = incognito;
554  view->generated_background_page = generated_background_page;
555  return view;
556}
557
558ItemInspectViewList DeveloperPrivateGetItemsInfoFunction::
559    GetInspectablePagesForExtension(
560        const Extension* extension,
561        bool extension_is_enabled) {
562  ItemInspectViewList result;
563  // Get the extension process's active views.
564  ProcessManager* process_manager =
565      ExtensionSystem::Get(GetProfile())->process_manager();
566  GetInspectablePagesForExtensionProcess(
567      extension,
568      process_manager->GetRenderViewHostsForExtension(extension->id()),
569      &result);
570
571  // Get app window views.
572  GetAppWindowPagesForExtensionProfile(extension, &result);
573
574  // Include a link to start the lazy background page, if applicable.
575  if (BackgroundInfo::HasLazyBackgroundPage(extension) &&
576      extension_is_enabled &&
577      !process_manager->GetBackgroundHostForExtension(extension->id())) {
578    result.push_back(constructInspectView(
579        BackgroundInfo::GetBackgroundURL(extension),
580        -1,
581        -1,
582        false,
583        BackgroundInfo::HasGeneratedBackgroundPage(extension)));
584  }
585
586  ExtensionService* service = GetProfile()->GetExtensionService();
587  // Repeat for the incognito process, if applicable. Don't try to get
588  // app windows for incognito process.
589  if (service->profile()->HasOffTheRecordProfile() &&
590      IncognitoInfo::IsSplitMode(extension)) {
591    process_manager = ExtensionSystem::Get(
592        service->profile()->GetOffTheRecordProfile())->process_manager();
593    GetInspectablePagesForExtensionProcess(
594        extension,
595        process_manager->GetRenderViewHostsForExtension(extension->id()),
596        &result);
597
598    if (BackgroundInfo::HasLazyBackgroundPage(extension) &&
599        extension_is_enabled &&
600        !process_manager->GetBackgroundHostForExtension(extension->id())) {
601    result.push_back(constructInspectView(
602        BackgroundInfo::GetBackgroundURL(extension),
603        -1,
604        -1,
605        false,
606        BackgroundInfo::HasGeneratedBackgroundPage(extension)));
607    }
608  }
609
610  return result;
611}
612
613bool DeveloperPrivateGetItemsInfoFunction::RunAsync() {
614  scoped_ptr<developer::GetItemsInfo::Params> params(
615      developer::GetItemsInfo::Params::Create(*args_));
616  EXTENSION_FUNCTION_VALIDATE(params.get() != NULL);
617
618  bool include_disabled = params->include_disabled;
619  bool include_terminated = params->include_terminated;
620
621  ExtensionSet items;
622
623  ExtensionRegistry* registry = ExtensionRegistry::Get(GetProfile());
624
625  items.InsertAll(registry->enabled_extensions());
626
627  if (include_disabled) {
628    items.InsertAll(registry->disabled_extensions());
629  }
630
631  if (include_terminated) {
632    items.InsertAll(registry->terminated_extensions());
633  }
634
635  ExtensionService* service =
636      ExtensionSystem::Get(GetProfile())->extension_service();
637  std::map<std::string, ExtensionResource> id_to_icon;
638  ItemInfoList item_list;
639
640  for (ExtensionSet::const_iterator iter = items.begin(); iter != items.end();
641       ++iter) {
642    const Extension& item = *iter->get();
643
644    ExtensionResource item_resource =
645        IconsInfo::GetIconResource(&item,
646                                   extension_misc::EXTENSION_ICON_MEDIUM,
647                                   ExtensionIconSet::MATCH_BIGGER);
648    id_to_icon[item.id()] = item_resource;
649
650    // Don't show component extensions and invisible apps.
651    if (ui_util::ShouldNotBeVisible(&item, GetProfile()))
652      continue;
653
654    item_list.push_back(make_linked_ptr<developer::ItemInfo>(
655        CreateItemInfo(
656            item, service->IsExtensionEnabled(item.id())).release()));
657  }
658
659  content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE,
660      base::Bind(&DeveloperPrivateGetItemsInfoFunction::GetIconsOnFileThread,
661                 this,
662                 item_list,
663                 id_to_icon));
664
665  return true;
666}
667
668DeveloperPrivateGetItemsInfoFunction::~DeveloperPrivateGetItemsInfoFunction() {}
669
670bool DeveloperPrivateAllowFileAccessFunction::RunSync() {
671  scoped_ptr<AllowFileAccess::Params> params(
672      AllowFileAccess::Params::Create(*args_));
673  EXTENSION_FUNCTION_VALIDATE(params.get());
674
675  EXTENSION_FUNCTION_VALIDATE(user_gesture_);
676
677  ExtensionSystem* system = ExtensionSystem::Get(GetProfile());
678  ManagementPolicy* management_policy = system->management_policy();
679  const Extension* extension =
680      ExtensionRegistry::Get(GetProfile())
681          ->GetExtensionById(params->item_id, ExtensionRegistry::EVERYTHING);
682  bool result = true;
683
684  if (!extension) {
685    result = false;
686  } else if (!management_policy->UserMayModifySettings(extension, NULL)) {
687    LOG(ERROR) << "Attempt to change allow file access of an extension that "
688               << "non-usermanagable was made. Extension id : "
689               << extension->id();
690    result = false;
691  } else {
692    util::SetAllowFileAccess(extension->id(), GetProfile(), params->allow);
693    result = true;
694  }
695
696  return result;
697}
698
699DeveloperPrivateAllowFileAccessFunction::
700    ~DeveloperPrivateAllowFileAccessFunction() {}
701
702bool DeveloperPrivateAllowIncognitoFunction::RunSync() {
703  scoped_ptr<AllowIncognito::Params> params(
704      AllowIncognito::Params::Create(*args_));
705  EXTENSION_FUNCTION_VALIDATE(params.get());
706
707  const Extension* extension =
708      ExtensionRegistry::Get(GetProfile())
709          ->GetExtensionById(params->item_id, ExtensionRegistry::EVERYTHING);
710  bool result = true;
711
712  if (!extension)
713    result = false;
714  else
715    util::SetIsIncognitoEnabled(extension->id(), GetProfile(), params->allow);
716
717  return result;
718}
719
720DeveloperPrivateAllowIncognitoFunction::
721    ~DeveloperPrivateAllowIncognitoFunction() {}
722
723bool DeveloperPrivateReloadFunction::RunSync() {
724  scoped_ptr<Reload::Params> params(Reload::Params::Create(*args_));
725  EXTENSION_FUNCTION_VALIDATE(params.get());
726
727  ExtensionService* service = GetProfile()->GetExtensionService();
728  CHECK(!params->item_id.empty());
729  service->ReloadExtension(params->item_id);
730  return true;
731}
732
733bool DeveloperPrivateShowPermissionsDialogFunction::RunSync() {
734  EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &extension_id_));
735  CHECK(!extension_id_.empty());
736  AppWindowRegistry* registry = AppWindowRegistry::Get(GetProfile());
737  DCHECK(registry);
738  AppWindow* app_window =
739      registry->GetAppWindowForRenderViewHost(render_view_host());
740  prompt_.reset(new ExtensionInstallPrompt(app_window->web_contents()));
741  const Extension* extension =
742      ExtensionRegistry::Get(GetProfile())
743          ->GetExtensionById(extension_id_, ExtensionRegistry::EVERYTHING);
744
745  if (!extension)
746    return false;
747
748  // Released by InstallUIAbort or InstallUIProceed.
749  AddRef();
750  std::vector<base::FilePath> retained_file_paths;
751  if (extension->permissions_data()->HasAPIPermission(
752          APIPermission::kFileSystem)) {
753    std::vector<apps::SavedFileEntry> retained_file_entries =
754        apps::SavedFilesService::Get(GetProfile())
755            ->GetAllFileEntries(extension_id_);
756    for (size_t i = 0; i < retained_file_entries.size(); i++) {
757      retained_file_paths.push_back(retained_file_entries[i].path);
758    }
759  }
760  prompt_->ReviewPermissions(this, extension, retained_file_paths);
761  return true;
762}
763
764DeveloperPrivateReloadFunction::~DeveloperPrivateReloadFunction() {}
765
766// This is called when the user clicks "Revoke File Access."
767void DeveloperPrivateShowPermissionsDialogFunction::InstallUIProceed() {
768  apps::SavedFilesService::Get(GetProfile())
769      ->ClearQueue(GetProfile()->GetExtensionService()->GetExtensionById(
770            extension_id_, true));
771  if (apps::AppRestoreService::Get(GetProfile())
772          ->IsAppRestorable(extension_id_))
773    apps::AppLoadService::Get(GetProfile())->RestartApplication(extension_id_);
774  SendResponse(true);
775  Release();
776}
777
778void DeveloperPrivateShowPermissionsDialogFunction::InstallUIAbort(
779    bool user_initiated) {
780  SendResponse(true);
781  Release();
782}
783
784DeveloperPrivateShowPermissionsDialogFunction::
785    DeveloperPrivateShowPermissionsDialogFunction() {}
786
787DeveloperPrivateShowPermissionsDialogFunction::
788    ~DeveloperPrivateShowPermissionsDialogFunction() {}
789
790DeveloperPrivateEnableFunction::DeveloperPrivateEnableFunction() {}
791
792bool DeveloperPrivateEnableFunction::RunSync() {
793  scoped_ptr<Enable::Params> params(Enable::Params::Create(*args_));
794  EXTENSION_FUNCTION_VALIDATE(params.get());
795
796  std::string extension_id = params->item_id;
797
798  const Extension* extension =
799      ExtensionRegistry::Get(GetProfile())
800          ->GetExtensionById(extension_id, ExtensionRegistry::EVERYTHING);
801  if (!extension) {
802    LOG(ERROR) << "Did not find extension with id " << extension_id;
803    return false;
804  }
805  ExtensionSystem* system = ExtensionSystem::Get(GetProfile());
806  ManagementPolicy* policy = system->management_policy();
807  bool enable = params->enable;
808  if (!policy->UserMayModifySettings(extension, NULL) ||
809      (!enable && policy->MustRemainEnabled(extension, NULL)) ||
810      (enable && policy->MustRemainDisabled(extension, NULL, NULL))) {
811    LOG(ERROR) << "Attempt to change enable state denied by management policy. "
812               << "Extension id: " << extension_id.c_str();
813    return false;
814  }
815
816  ExtensionService* service = system->extension_service();
817  if (enable) {
818    ExtensionPrefs* prefs = ExtensionPrefs::Get(GetProfile());
819    if (prefs->DidExtensionEscalatePermissions(extension_id)) {
820      AppWindowRegistry* registry = AppWindowRegistry::Get(GetProfile());
821      CHECK(registry);
822      AppWindow* app_window =
823          registry->GetAppWindowForRenderViewHost(render_view_host());
824      if (!app_window) {
825        return false;
826      }
827
828      ShowExtensionDisabledDialog(
829          service, app_window->web_contents(), extension);
830    } else if ((prefs->GetDisableReasons(extension_id) &
831                  Extension::DISABLE_UNSUPPORTED_REQUIREMENT) &&
832               !requirements_checker_.get()) {
833      // Recheck the requirements.
834      scoped_refptr<const Extension> extension =
835          service->GetExtensionById(extension_id, true);
836      requirements_checker_.reset(new RequirementsChecker);
837      // Released by OnRequirementsChecked.
838      AddRef();
839      requirements_checker_->Check(
840          extension,
841          base::Bind(&DeveloperPrivateEnableFunction::OnRequirementsChecked,
842                     this, extension_id));
843    } else {
844      service->EnableExtension(extension_id);
845
846      // Make sure any browser action contained within it is not hidden.
847      ExtensionActionAPI::SetBrowserActionVisibility(
848          prefs, extension->id(), true);
849    }
850  } else {
851    service->DisableExtension(extension_id, Extension::DISABLE_USER_ACTION);
852  }
853  return true;
854}
855
856void DeveloperPrivateEnableFunction::OnRequirementsChecked(
857    const std::string& extension_id,
858    std::vector<std::string> requirements_errors) {
859  if (requirements_errors.empty()) {
860    ExtensionService* service = GetProfile()->GetExtensionService();
861    service->EnableExtension(extension_id);
862  } else {
863    ExtensionErrorReporter::GetInstance()->ReportError(
864        base::UTF8ToUTF16(JoinString(requirements_errors, ' ')),
865        true);  // Be noisy.
866  }
867  Release();
868}
869
870DeveloperPrivateEnableFunction::~DeveloperPrivateEnableFunction() {}
871
872bool DeveloperPrivateInspectFunction::RunSync() {
873  scoped_ptr<developer::Inspect::Params> params(
874      developer::Inspect::Params::Create(*args_));
875  EXTENSION_FUNCTION_VALIDATE(params.get() != NULL);
876  const developer::InspectOptions& options = params->options;
877
878  int render_process_id;
879  base::StringToInt(options.render_process_id, &render_process_id);
880
881  if (render_process_id == -1) {
882    // This is a lazy background page. Identify if it is a normal
883    // or incognito background page.
884    ExtensionService* service = GetProfile()->GetExtensionService();
885    if (options.incognito)
886      service = ExtensionSystem::Get(
887          service->profile()->GetOffTheRecordProfile())->extension_service();
888    const Extension* extension = service->extensions()->GetByID(
889        options.extension_id);
890    DCHECK(extension);
891    // Wakes up the background page and  opens the inspect window.
892    devtools_util::InspectBackgroundPage(extension, GetProfile());
893    return false;
894  }
895
896  int render_view_id;
897  base::StringToInt(options.render_view_id, &render_view_id);
898  content::RenderViewHost* host = content::RenderViewHost::FromID(
899      render_process_id, render_view_id);
900
901  if (!host) {
902    // This can happen if the host has gone away since the page was displayed.
903    return false;
904  }
905
906  DevToolsWindow::OpenDevToolsWindow(host);
907  return true;
908}
909
910DeveloperPrivateInspectFunction::~DeveloperPrivateInspectFunction() {}
911
912bool DeveloperPrivateLoadUnpackedFunction::RunAsync() {
913  base::string16 select_title =
914      l10n_util::GetStringUTF16(IDS_EXTENSION_LOAD_FROM_DIRECTORY);
915
916  // Balanced in FileSelected / FileSelectionCanceled.
917  AddRef();
918  bool result = ShowPicker(
919      ui::SelectFileDialog::SELECT_FOLDER,
920      DeveloperPrivateAPI::Get(GetProfile())->GetLastUnpackedDirectory(),
921      select_title,
922      ui::SelectFileDialog::FileTypeInfo(),
923      0);
924  return result;
925}
926
927void DeveloperPrivateLoadUnpackedFunction::FileSelected(
928    const base::FilePath& path) {
929  ExtensionService* service = GetProfile()->GetExtensionService();
930  UnpackedInstaller::Create(service)->Load(path);
931  DeveloperPrivateAPI::Get(GetProfile())->SetLastUnpackedDirectory(path);
932  SendResponse(true);
933  Release();
934}
935
936void DeveloperPrivateLoadUnpackedFunction::FileSelectionCanceled() {
937  SendResponse(false);
938  Release();
939}
940
941bool DeveloperPrivateChooseEntryFunction::ShowPicker(
942    ui::SelectFileDialog::Type picker_type,
943    const base::FilePath& last_directory,
944    const base::string16& select_title,
945    const ui::SelectFileDialog::FileTypeInfo& info,
946    int file_type_index) {
947  AppWindowRegistry* registry = AppWindowRegistry::Get(GetProfile());
948  DCHECK(registry);
949  AppWindow* app_window =
950      registry->GetAppWindowForRenderViewHost(render_view_host());
951  if (!app_window) {
952    return false;
953  }
954
955  // The entry picker will hold a reference to this function instance,
956  // and subsequent sending of the function response) until the user has
957  // selected a file or cancelled the picker. At that point, the picker will
958  // delete itself.
959  new EntryPicker(this,
960                  app_window->web_contents(),
961                  picker_type,
962                  last_directory,
963                  select_title,
964                  info,
965                  file_type_index);
966  return true;
967}
968
969bool DeveloperPrivateChooseEntryFunction::RunAsync() {
970  return false;
971}
972
973DeveloperPrivateChooseEntryFunction::~DeveloperPrivateChooseEntryFunction() {}
974
975void DeveloperPrivatePackDirectoryFunction::OnPackSuccess(
976    const base::FilePath& crx_file,
977    const base::FilePath& pem_file) {
978  developer::PackDirectoryResponse response;
979  response.message = base::UTF16ToUTF8(
980      PackExtensionJob::StandardSuccessMessage(crx_file, pem_file));
981  response.status = developer::PACK_STATUS_SUCCESS;
982  results_ = developer::PackDirectory::Results::Create(response);
983  SendResponse(true);
984  Release();
985}
986
987void DeveloperPrivatePackDirectoryFunction::OnPackFailure(
988    const std::string& error,
989    ExtensionCreator::ErrorType error_type) {
990  developer::PackDirectoryResponse response;
991  response.message = error;
992  if (error_type == ExtensionCreator::kCRXExists) {
993    response.item_path = item_path_str_;
994    response.pem_path = key_path_str_;
995    response.override_flags = ExtensionCreator::kOverwriteCRX;
996    response.status = developer::PACK_STATUS_WARNING;
997  } else {
998    response.status = developer::PACK_STATUS_ERROR;
999  }
1000  results_ = developer::PackDirectory::Results::Create(response);
1001  SendResponse(true);
1002  Release();
1003}
1004
1005bool DeveloperPrivatePackDirectoryFunction::RunAsync() {
1006  scoped_ptr<PackDirectory::Params> params(
1007      PackDirectory::Params::Create(*args_));
1008  EXTENSION_FUNCTION_VALIDATE(params.get());
1009
1010  int flags = params->flags;
1011  item_path_str_ = params->path;
1012  key_path_str_ = params->private_key_path;
1013
1014  base::FilePath root_directory =
1015      base::FilePath::FromUTF8Unsafe(item_path_str_);
1016
1017  base::FilePath key_file = base::FilePath::FromUTF8Unsafe(key_path_str_);
1018
1019  developer::PackDirectoryResponse response;
1020  if (root_directory.empty()) {
1021    if (item_path_str_.empty())
1022      response.message = l10n_util::GetStringUTF8(
1023          IDS_EXTENSION_PACK_DIALOG_ERROR_ROOT_REQUIRED);
1024    else
1025      response.message = l10n_util::GetStringUTF8(
1026          IDS_EXTENSION_PACK_DIALOG_ERROR_ROOT_INVALID);
1027
1028    response.status = developer::PACK_STATUS_ERROR;
1029    results_ = developer::PackDirectory::Results::Create(response);
1030    SendResponse(true);
1031    return true;
1032  }
1033
1034  if (!key_path_str_.empty() && key_file.empty()) {
1035    response.message = l10n_util::GetStringUTF8(
1036        IDS_EXTENSION_PACK_DIALOG_ERROR_KEY_INVALID);
1037    response.status = developer::PACK_STATUS_ERROR;
1038    results_ = developer::PackDirectory::Results::Create(response);
1039    SendResponse(true);
1040    return true;
1041  }
1042
1043  // Balanced in OnPackSuccess / OnPackFailure.
1044  AddRef();
1045
1046  pack_job_ = new PackExtensionJob(this, root_directory, key_file, flags);
1047  pack_job_->Start();
1048  return true;
1049}
1050
1051DeveloperPrivatePackDirectoryFunction::DeveloperPrivatePackDirectoryFunction()
1052{}
1053
1054DeveloperPrivatePackDirectoryFunction::~DeveloperPrivatePackDirectoryFunction()
1055{}
1056
1057DeveloperPrivateLoadUnpackedFunction::~DeveloperPrivateLoadUnpackedFunction() {}
1058
1059bool DeveloperPrivateLoadDirectoryFunction::RunAsync() {
1060  // TODO(grv) : add unittests.
1061  std::string directory_url_str;
1062  std::string filesystem_name;
1063  std::string filesystem_path;
1064
1065  EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &filesystem_name));
1066  EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_path));
1067  EXTENSION_FUNCTION_VALIDATE(args_->GetString(2, &directory_url_str));
1068
1069  // Directory url is non empty only for syncfilesystem.
1070  if (directory_url_str != "") {
1071    context_ = content::BrowserContext::GetStoragePartition(
1072        GetProfile(), render_view_host()->GetSiteInstance())
1073                   ->GetFileSystemContext();
1074
1075    fileapi::FileSystemURL directory_url =
1076        context_->CrackURL(GURL(directory_url_str));
1077
1078    if (!directory_url.is_valid() && directory_url.type() ==
1079        fileapi::kFileSystemTypeSyncable) {
1080      SetError("DirectoryEntry of unsupported filesystem.");
1081      return false;
1082    }
1083
1084    size_t pos = 0;
1085    // Parse the project directory name from the project url. The project url is
1086    // expected to have project name as the suffix.
1087    if ((pos = directory_url_str.rfind("/")) == std::string::npos) {
1088      SetError("Invalid Directory entry.");
1089      return false;
1090    }
1091
1092    std::string project_name;
1093    project_name = directory_url_str.substr(pos + 1);
1094    project_base_url_ = directory_url_str.substr(0, pos + 1);
1095
1096    base::FilePath project_path(GetProfile()->GetPath());
1097    project_path = project_path.Append(kUnpackedAppsFolder);
1098    project_path = project_path.Append(
1099        base::FilePath::FromUTF8Unsafe(project_name));
1100
1101    project_base_path_ = project_path;
1102
1103    content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE,
1104        base::Bind(&DeveloperPrivateLoadDirectoryFunction::
1105                       ClearExistingDirectoryContent,
1106                   this,
1107                   project_base_path_));
1108  } else {
1109    // Check if the DirecotryEntry is the instance of chrome filesystem.
1110    if (!app_file_handler_util::ValidateFileEntryAndGetPath(filesystem_name,
1111                                                            filesystem_path,
1112                                                            render_view_host_,
1113                                                            &project_base_path_,
1114                                                            &error_))
1115    return false;
1116
1117    Load();
1118  }
1119
1120  return true;
1121}
1122
1123void DeveloperPrivateLoadDirectoryFunction::Load() {
1124  ExtensionService* service = GetProfile()->GetExtensionService();
1125  UnpackedInstaller::Create(service)->Load(project_base_path_);
1126
1127  // TODO(grv) : The unpacked installer should fire an event when complete
1128  // and return the extension_id.
1129  SetResult(new base::StringValue("-1"));
1130  SendResponse(true);
1131}
1132
1133void DeveloperPrivateLoadDirectoryFunction::ClearExistingDirectoryContent(
1134    const base::FilePath& project_path) {
1135
1136  // Clear the project directory before copying new files.
1137  base::DeleteFile(project_path, true/*recursive*/);
1138
1139  pending_copy_operations_count_ = 1;
1140
1141  content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE,
1142      base::Bind(&DeveloperPrivateLoadDirectoryFunction::
1143                 ReadSyncFileSystemDirectory,
1144                 this, project_path, project_path.BaseName()));
1145}
1146
1147void DeveloperPrivateLoadDirectoryFunction::ReadSyncFileSystemDirectory(
1148    const base::FilePath& project_path,
1149    const base::FilePath& destination_path) {
1150
1151  current_path_ = context_->CrackURL(GURL(project_base_url_)).path();
1152
1153  GURL project_url = GURL(project_base_url_ + destination_path.MaybeAsASCII());
1154
1155  fileapi::FileSystemURL url = context_->CrackURL(project_url);
1156
1157  context_->operation_runner()->ReadDirectory(
1158      url, base::Bind(&DeveloperPrivateLoadDirectoryFunction::
1159                      ReadSyncFileSystemDirectoryCb,
1160                      this, project_path, destination_path));
1161}
1162
1163void DeveloperPrivateLoadDirectoryFunction::ReadSyncFileSystemDirectoryCb(
1164    const base::FilePath& project_path,
1165    const base::FilePath& destination_path,
1166    base::File::Error status,
1167    const fileapi::FileSystemOperation::FileEntryList& file_list,
1168    bool has_more) {
1169
1170  if (status != base::File::FILE_OK) {
1171    DLOG(ERROR) << "Error in copying files from sync filesystem.";
1172    return;
1173  }
1174
1175  // We add 1 to the pending copy operations for both files and directories. We
1176  // release the directory copy operation once all the files under the directory
1177  // are added for copying. We do that to ensure that pendingCopyOperationsCount
1178  // does not become zero before all copy operations are finished.
1179  // In case the directory happens to be executing the last copy operation it
1180  // will call SendResponse to send the response to the API. The pending copy
1181  // operations of files are released by the CopyFile function.
1182  pending_copy_operations_count_ += file_list.size();
1183
1184  for (size_t i = 0; i < file_list.size(); ++i) {
1185    if (file_list[i].is_directory) {
1186      ReadSyncFileSystemDirectory(project_path.Append(file_list[i].name),
1187                                  destination_path.Append(file_list[i].name));
1188      continue;
1189    }
1190
1191    std::string origin_url(
1192        Extension::GetBaseURLFromExtensionId(extension_id()).spec());
1193    fileapi::FileSystemURL url(sync_file_system::CreateSyncableFileSystemURL(
1194        GURL(origin_url),
1195        current_path_.Append(destination_path.Append(file_list[i].name))));
1196    base::FilePath target_path = project_path;
1197    target_path = target_path.Append(file_list[i].name);
1198
1199    context_->operation_runner()->CreateSnapshotFile(
1200        url,
1201        base::Bind(&DeveloperPrivateLoadDirectoryFunction::SnapshotFileCallback,
1202            this,
1203            target_path));
1204  }
1205
1206  if (!has_more) {
1207    // Directory copy operation released here.
1208    pending_copy_operations_count_--;
1209
1210    if (!pending_copy_operations_count_) {
1211      content::BrowserThread::PostTask(
1212          content::BrowserThread::UI, FROM_HERE,
1213          base::Bind(&DeveloperPrivateLoadDirectoryFunction::SendResponse,
1214                     this,
1215                     success_));
1216    }
1217  }
1218}
1219
1220void DeveloperPrivateLoadDirectoryFunction::SnapshotFileCallback(
1221    const base::FilePath& target_path,
1222    base::File::Error result,
1223    const base::File::Info& file_info,
1224    const base::FilePath& src_path,
1225    const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref) {
1226  if (result != base::File::FILE_OK) {
1227    SetError("Error in copying files from sync filesystem.");
1228    success_ = false;
1229    return;
1230  }
1231
1232  content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE,
1233      base::Bind(&DeveloperPrivateLoadDirectoryFunction::CopyFile,
1234                 this,
1235                 src_path,
1236                 target_path));
1237}
1238
1239void DeveloperPrivateLoadDirectoryFunction::CopyFile(
1240    const base::FilePath& src_path,
1241    const base::FilePath& target_path) {
1242  if (!base::CreateDirectory(target_path.DirName())) {
1243    SetError("Error in copying files from sync filesystem.");
1244    success_ = false;
1245  }
1246
1247  if (success_)
1248    base::CopyFile(src_path, target_path);
1249
1250  CHECK(pending_copy_operations_count_ > 0);
1251  pending_copy_operations_count_--;
1252
1253  if (!pending_copy_operations_count_) {
1254    content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
1255        base::Bind(&DeveloperPrivateLoadDirectoryFunction::Load,
1256                   this));
1257  }
1258}
1259
1260DeveloperPrivateLoadDirectoryFunction::DeveloperPrivateLoadDirectoryFunction()
1261    : pending_copy_operations_count_(0), success_(true) {}
1262
1263DeveloperPrivateLoadDirectoryFunction::~DeveloperPrivateLoadDirectoryFunction()
1264    {}
1265
1266bool DeveloperPrivateChoosePathFunction::RunAsync() {
1267  scoped_ptr<developer::ChoosePath::Params> params(
1268      developer::ChoosePath::Params::Create(*args_));
1269  EXTENSION_FUNCTION_VALIDATE(params.get() != NULL);
1270
1271  ui::SelectFileDialog::Type type = ui::SelectFileDialog::SELECT_FOLDER;
1272  ui::SelectFileDialog::FileTypeInfo info;
1273  if (params->select_type == developer::SELECT_TYPE_FILE) {
1274    type = ui::SelectFileDialog::SELECT_OPEN_FILE;
1275  }
1276  base::string16 select_title;
1277
1278  int file_type_index = 0;
1279  if (params->file_type == developer::FILE_TYPE_LOAD) {
1280    select_title = l10n_util::GetStringUTF16(IDS_EXTENSION_LOAD_FROM_DIRECTORY);
1281  } else if (params->file_type == developer::FILE_TYPE_PEM) {
1282    select_title = l10n_util::GetStringUTF16(
1283        IDS_EXTENSION_PACK_DIALOG_SELECT_KEY);
1284    info.extensions.push_back(std::vector<base::FilePath::StringType>());
1285    info.extensions.front().push_back(FILE_PATH_LITERAL("pem"));
1286    info.extension_description_overrides.push_back(
1287        l10n_util::GetStringUTF16(
1288            IDS_EXTENSION_PACK_DIALOG_KEY_FILE_TYPE_DESCRIPTION));
1289    info.include_all_files = true;
1290    file_type_index = 1;
1291  } else {
1292    NOTREACHED();
1293  }
1294
1295  // Balanced by FileSelected / FileSelectionCanceled.
1296  AddRef();
1297  bool result = ShowPicker(
1298      type,
1299      DeveloperPrivateAPI::Get(GetProfile())->GetLastUnpackedDirectory(),
1300      select_title,
1301      info,
1302      file_type_index);
1303  return result;
1304}
1305
1306void DeveloperPrivateChoosePathFunction::FileSelected(
1307    const base::FilePath& path) {
1308  SetResult(new base::StringValue(base::UTF16ToUTF8(path.LossyDisplayName())));
1309  SendResponse(true);
1310  Release();
1311}
1312
1313void DeveloperPrivateChoosePathFunction::FileSelectionCanceled() {
1314  SendResponse(false);
1315  Release();
1316}
1317
1318DeveloperPrivateChoosePathFunction::~DeveloperPrivateChoosePathFunction() {}
1319
1320bool DeveloperPrivateIsProfileManagedFunction::RunSync() {
1321  SetResult(new base::FundamentalValue(GetProfile()->IsManaged()));
1322  return true;
1323}
1324
1325DeveloperPrivateIsProfileManagedFunction::
1326    ~DeveloperPrivateIsProfileManagedFunction() {
1327}
1328
1329DeveloperPrivateRequestFileSourceFunction::
1330    DeveloperPrivateRequestFileSourceFunction() {}
1331
1332DeveloperPrivateRequestFileSourceFunction::
1333    ~DeveloperPrivateRequestFileSourceFunction() {}
1334
1335bool DeveloperPrivateRequestFileSourceFunction::RunAsync() {
1336  scoped_ptr<developer::RequestFileSource::Params> params(
1337      developer::RequestFileSource::Params::Create(*args_));
1338  EXTENSION_FUNCTION_VALIDATE(params.get() != NULL);
1339
1340  base::DictionaryValue* dict = NULL;
1341  if (!params->dict->GetAsDictionary(&dict)) {
1342    NOTREACHED();
1343    return false;
1344  }
1345
1346  AddRef();  // Balanced in LaunchCallback().
1347  error_ui_util::HandleRequestFileSource(
1348      dict,
1349      GetProfile(),
1350      base::Bind(&DeveloperPrivateRequestFileSourceFunction::LaunchCallback,
1351                 base::Unretained(this)));
1352  return true;
1353}
1354
1355void DeveloperPrivateRequestFileSourceFunction::LaunchCallback(
1356    const base::DictionaryValue& results) {
1357  SetResult(results.DeepCopy());
1358  SendResponse(true);
1359  Release();  // Balanced in RunAsync().
1360}
1361
1362DeveloperPrivateOpenDevToolsFunction::DeveloperPrivateOpenDevToolsFunction() {}
1363DeveloperPrivateOpenDevToolsFunction::~DeveloperPrivateOpenDevToolsFunction() {}
1364
1365bool DeveloperPrivateOpenDevToolsFunction::RunAsync() {
1366  scoped_ptr<developer::OpenDevTools::Params> params(
1367      developer::OpenDevTools::Params::Create(*args_));
1368  EXTENSION_FUNCTION_VALIDATE(params.get() != NULL);
1369
1370  base::DictionaryValue* dict = NULL;
1371  if (!params->dict->GetAsDictionary(&dict)) {
1372    NOTREACHED();
1373    return false;
1374  }
1375
1376  error_ui_util::HandleOpenDevTools(dict);
1377
1378  return true;
1379}
1380
1381}  // namespace api
1382
1383}  // namespace extensions
1384