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