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