Home | History | Annotate | Download | only in developer_private
      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 
     82 using content::RenderViewHost;
     83 
     84 namespace extensions {
     85 
     86 namespace developer_private = api::developer_private;
     87 
     88 namespace {
     89 
     90 const char kUnpackedAppsFolder[] = "apps_target";
     91 
     92 ExtensionService* GetExtensionService(Profile* profile) {
     93   return ExtensionSystem::Get(profile)->extension_service();
     94 }
     95 
     96 ExtensionUpdater* GetExtensionUpdater(Profile* profile) {
     97   return GetExtensionService(profile)->updater();
     98 }
     99 
    100 GURL 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 
    109 GURL 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.
    130 GURL 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 
    138 std::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 
    145 void 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 
    161 namespace AllowFileAccess = api::developer_private::AllowFileAccess;
    162 namespace AllowIncognito = api::developer_private::AllowIncognito;
    163 namespace ChoosePath = api::developer_private::ChoosePath;
    164 namespace Enable = api::developer_private::Enable;
    165 namespace GetItemsInfo = api::developer_private::GetItemsInfo;
    166 namespace Inspect = api::developer_private::Inspect;
    167 namespace PackDirectory = api::developer_private::PackDirectory;
    168 namespace Reload = api::developer_private::Reload;
    169 
    170 static base::LazyInstance<BrowserContextKeyedAPIFactory<DeveloperPrivateAPI> >
    171     g_factory = LAZY_INSTANCE_INITIALIZER;
    172 
    173 // static
    174 BrowserContextKeyedAPIFactory<DeveloperPrivateAPI>*
    175 DeveloperPrivateAPI::GetFactoryInstance() {
    176   return g_factory.Pointer();
    177 }
    178 
    179 // static
    180 DeveloperPrivateAPI* DeveloperPrivateAPI::Get(
    181     content::BrowserContext* context) {
    182   return GetFactoryInstance()->Get(context);
    183 }
    184 
    185 DeveloperPrivateAPI::DeveloperPrivateAPI(content::BrowserContext* context)
    186     : profile_(Profile::FromBrowserContext(context)) {
    187   RegisterNotifications();
    188 }
    189 
    190 DeveloperPrivateEventRouter::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 
    205 DeveloperPrivateEventRouter::~DeveloperPrivateEventRouter() {
    206   ErrorConsole::Get(profile_)->RemoveObserver(this);
    207 }
    208 
    209 void DeveloperPrivateEventRouter::AddExtensionId(
    210     const std::string& extension_id) {
    211   extension_ids_.insert(extension_id);
    212 }
    213 
    214 void DeveloperPrivateEventRouter::RemoveExtensionId(
    215     const std::string& extension_id) {
    216   extension_ids_.erase(extension_id);
    217 }
    218 
    219 void 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 
    249 void 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 
    257 void 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 
    266 void 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 
    277 void 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 
    286 void 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 
    297 void DeveloperPrivateAPI::SetLastUnpackedDirectory(const base::FilePath& path) {
    298   last_unpacked_directory_ = path;
    299 }
    300 
    301 void DeveloperPrivateAPI::RegisterNotifications() {
    302   EventRouter::Get(profile_)->RegisterObserver(
    303       this, developer_private::OnItemStateChanged::kEventName);
    304 }
    305 
    306 DeveloperPrivateAPI::~DeveloperPrivateAPI() {}
    307 
    308 void DeveloperPrivateAPI::Shutdown() {}
    309 
    310 void 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 
    320 void 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 
    330 namespace api {
    331 
    332 bool 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 
    340 DeveloperPrivateAutoUpdateFunction::~DeveloperPrivateAutoUpdateFunction() {}
    341 
    342 scoped_ptr<developer::ItemInfo>
    343 DeveloperPrivateGetItemsInfoFunction::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 
    457 void 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 
    478 void 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 
    507 void 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 
    535 linked_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 
    559 ItemInspectViewList 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 
    614 bool 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 
    669 DeveloperPrivateGetItemsInfoFunction::~DeveloperPrivateGetItemsInfoFunction() {}
    670 
    671 bool 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 
    700 DeveloperPrivateAllowFileAccessFunction::
    701     ~DeveloperPrivateAllowFileAccessFunction() {}
    702 
    703 bool 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 
    721 DeveloperPrivateAllowIncognitoFunction::
    722     ~DeveloperPrivateAllowIncognitoFunction() {}
    723 
    724 bool 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 
    734 bool 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 
    765 DeveloperPrivateReloadFunction::~DeveloperPrivateReloadFunction() {}
    766 
    767 // This is called when the user clicks "Revoke File Access."
    768 void 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 
    779 void DeveloperPrivateShowPermissionsDialogFunction::InstallUIAbort(
    780     bool user_initiated) {
    781   SendResponse(true);
    782   Release();
    783 }
    784 
    785 DeveloperPrivateShowPermissionsDialogFunction::
    786     DeveloperPrivateShowPermissionsDialogFunction() {}
    787 
    788 DeveloperPrivateShowPermissionsDialogFunction::
    789     ~DeveloperPrivateShowPermissionsDialogFunction() {}
    790 
    791 DeveloperPrivateEnableFunction::DeveloperPrivateEnableFunction() {}
    792 
    793 bool 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 
    857 void 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 
    870 DeveloperPrivateEnableFunction::~DeveloperPrivateEnableFunction() {}
    871 
    872 bool 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 
    907 DeveloperPrivateInspectFunction::~DeveloperPrivateInspectFunction() {}
    908 
    909 bool 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 
    924 void 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 
    933 void DeveloperPrivateLoadUnpackedFunction::FileSelectionCanceled() {
    934   SendResponse(false);
    935   Release();
    936 }
    937 
    938 bool 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 
    966 bool DeveloperPrivateChooseEntryFunction::RunAsync() {
    967   return false;
    968 }
    969 
    970 DeveloperPrivateChooseEntryFunction::~DeveloperPrivateChooseEntryFunction() {}
    971 
    972 void 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 
    984 void 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 
   1002 bool 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 
   1048 DeveloperPrivatePackDirectoryFunction::DeveloperPrivatePackDirectoryFunction()
   1049 {}
   1050 
   1051 DeveloperPrivatePackDirectoryFunction::~DeveloperPrivatePackDirectoryFunction()
   1052 {}
   1053 
   1054 DeveloperPrivateLoadUnpackedFunction::~DeveloperPrivateLoadUnpackedFunction() {}
   1055 
   1056 bool 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 
   1119 bool 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 
   1150 void 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 
   1160 void 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 
   1174 void 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 
   1186 void 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 
   1241 void 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 
   1260 void 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 
   1281 DeveloperPrivateLoadDirectoryFunction::DeveloperPrivateLoadDirectoryFunction()
   1282     : pending_copy_operations_count_(0), success_(true) {}
   1283 
   1284 DeveloperPrivateLoadDirectoryFunction::~DeveloperPrivateLoadDirectoryFunction()
   1285     {}
   1286 
   1287 bool 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 
   1327 void DeveloperPrivateChoosePathFunction::FileSelected(
   1328     const base::FilePath& path) {
   1329   SetResult(new base::StringValue(base::UTF16ToUTF8(path.LossyDisplayName())));
   1330   SendResponse(true);
   1331   Release();
   1332 }
   1333 
   1334 void DeveloperPrivateChoosePathFunction::FileSelectionCanceled() {
   1335   SendResponse(false);
   1336   Release();
   1337 }
   1338 
   1339 DeveloperPrivateChoosePathFunction::~DeveloperPrivateChoosePathFunction() {}
   1340 
   1341 bool DeveloperPrivateIsProfileManagedFunction::RunSync() {
   1342   SetResult(new base::FundamentalValue(GetProfile()->IsSupervised()));
   1343   return true;
   1344 }
   1345 
   1346 DeveloperPrivateIsProfileManagedFunction::
   1347     ~DeveloperPrivateIsProfileManagedFunction() {
   1348 }
   1349 
   1350 DeveloperPrivateRequestFileSourceFunction::
   1351     DeveloperPrivateRequestFileSourceFunction() {}
   1352 
   1353 DeveloperPrivateRequestFileSourceFunction::
   1354     ~DeveloperPrivateRequestFileSourceFunction() {}
   1355 
   1356 bool 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 
   1376 void DeveloperPrivateRequestFileSourceFunction::LaunchCallback(
   1377     const base::DictionaryValue& results) {
   1378   SetResult(results.DeepCopy());
   1379   SendResponse(true);
   1380   Release();  // Balanced in RunAsync().
   1381 }
   1382 
   1383 DeveloperPrivateOpenDevToolsFunction::DeveloperPrivateOpenDevToolsFunction() {}
   1384 DeveloperPrivateOpenDevToolsFunction::~DeveloperPrivateOpenDevToolsFunction() {}
   1385 
   1386 bool 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