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