// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/extensions/api/developer_private/developer_private_api.h" #include "base/base64.h" #include "base/file_util.h" #include "base/strings/string_number_conversions.h" #include "base/utf_string_conversions.h" #include "base/values.h" #include "chrome/browser/devtools/devtools_window.h" #include "chrome/browser/extensions/api/developer_private/developer_private_api_factory.h" #include "chrome/browser/extensions/api/developer_private/entry_picker.h" #include "chrome/browser/extensions/extension_disabled_ui.h" #include "chrome/browser/extensions/extension_error_reporter.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/extensions/management_policy.h" #include "chrome/browser/extensions/shell_window_registry.h" #include "chrome/browser/extensions/unpacked_installer.h" #include "chrome/browser/extensions/updater/extension_updater.h" #include "chrome/browser/platform_util.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/sync_file_system/drive_file_sync_service.h" #include "chrome/browser/ui/chrome_select_file_policy.h" #include "chrome/browser/ui/extensions/shell_window.h" #include "chrome/browser/ui/webui/extensions/extension_icon_source.h" #include "chrome/common/extensions/api/developer_private.h" #include "chrome/common/extensions/background_info.h" #include "chrome/common/extensions/extension_icon_set.h" #include "chrome/common/extensions/incognito_handler.h" #include "chrome/common/extensions/manifest_handlers/icons_handler.h" #include "chrome/common/extensions/manifest_handlers/offline_enabled_info.h" #include "chrome/common/extensions/manifest_url_handler.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/storage_partition.h" #include "content/public/browser/web_contents.h" #include "extensions/browser/view_type_utils.h" #include "extensions/common/constants.h" #include "extensions/common/extension_resource.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" #include "net/base/net_util.h" #include "ui/base/l10n/l10n_util.h" #include "webkit/blob/shareable_file_reference.h" #include "webkit/fileapi/file_system_context.h" #include "webkit/fileapi/file_system_operation.h" #include "webkit/fileapi/local_file_system_operation.h" #include "webkit/fileapi/syncable/syncable_file_system_util.h" using content::RenderViewHost; namespace extensions { namespace { const base::FilePath::CharType kUnpackedAppsFolder[] = FILE_PATH_LITERAL("apps_target"); ExtensionUpdater* GetExtensionUpdater(Profile* profile) { return profile->GetExtensionService()->updater(); } GURL ToDataURL(const base::FilePath& path) { std::string contents; if (!file_util::ReadFileToString(path, &contents)) return GURL(); std::string contents_base64; if (!base::Base64Encode(contents, &contents_base64)) return GURL(); const char kDataURLPrefix[] = "data:image;base64,"; return GURL(kDataURLPrefix + contents_base64); } std::vector<base::FilePath> ListFolder(const base::FilePath path) { file_util::FileEnumerator files(path, false, file_util::FileEnumerator::DIRECTORIES | file_util::FileEnumerator::FILES); std::vector<base::FilePath> paths; for (base::FilePath current_path = files.Next(); !current_path.empty(); current_path = files.Next()) { paths.push_back(current_path); } return paths; } bool ValidateFolderName(const base::FilePath::StringType& name) { if (!name.length() || name[0] == '.' || name[0] == '-') return false; for (size_t i = 0; i < name.length(); ++i) { if (!((name[i] >= 'A' && name[i] <= 'Z') || (name[i] >= 'a' && name[i] <= 'z') || (name[i] >= '0' && name[i] <= '9') || (name[i] == '_' || name[i] == '-' || name[i] == '.'))) { return false; } } return true; } } // namespace namespace AllowFileAccess = api::developer_private::AllowFileAccess; namespace AllowIncognito = api::developer_private::AllowIncognito; namespace ChoosePath = api::developer_private::ChoosePath; namespace Enable = api::developer_private::Enable; namespace GetItemsInfo = api::developer_private::GetItemsInfo; namespace Inspect = api::developer_private::Inspect; namespace PackDirectory = api::developer_private::PackDirectory; namespace Reload = api::developer_private::Reload; namespace Restart = api::developer_private::Restart; DeveloperPrivateAPI* DeveloperPrivateAPI::Get(Profile* profile) { return DeveloperPrivateAPIFactory::GetForProfile(profile); } DeveloperPrivateAPI::DeveloperPrivateAPI(Profile* profile) { RegisterNotifications(); } void DeveloperPrivateAPI::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { switch (type) { // TODO(grv): Listen to other notifications and expose them // as events in API. case chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED: break; default: NOTREACHED(); } } void DeveloperPrivateAPI::SetLastUnpackedDirectory(const base::FilePath& path) { last_unpacked_directory_ = path; } void DeveloperPrivateAPI::RegisterNotifications() { registrar_.Add(this, chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED, content::NotificationService::AllBrowserContextsAndSources()); } DeveloperPrivateAPI::~DeveloperPrivateAPI() {} void DeveloperPrivateAPI::Shutdown() {} namespace api { bool DeveloperPrivateAutoUpdateFunction::RunImpl() { ExtensionUpdater* updater = GetExtensionUpdater(profile()); if (updater) updater->CheckNow(ExtensionUpdater::CheckParams()); SetResult(Value::CreateBooleanValue(true)); return true; } DeveloperPrivateAutoUpdateFunction::~DeveloperPrivateAutoUpdateFunction() {} scoped_ptr<developer::ItemInfo> DeveloperPrivateGetItemsInfoFunction::CreateItemInfo( const Extension& item, bool item_is_enabled) { scoped_ptr<developer::ItemInfo> info(new developer::ItemInfo()); ExtensionSystem* system = ExtensionSystem::Get(profile()); ExtensionService* service = profile()->GetExtensionService(); info->id = item.id(); info->name = item.name(); info->enabled = service->IsExtensionEnabled(info->id); info->offline_enabled = OfflineEnabledInfo::IsOfflineEnabled(&item); info->version = item.VersionString(); info->description = item.description(); if (item.is_app()) { if (item.is_legacy_packaged_app()) info->type = developer::ITEM_TYPE_LEGACY_PACKAGED_APP; else if (item.is_hosted_app()) info->type = developer::ITEM_TYPE_HOSTED_APP; else if (item.is_platform_app()) info->type = developer::ITEM_TYPE_PACKAGED_APP; else NOTREACHED(); } else if (item.is_theme()) { info->type = developer::ITEM_TYPE_THEME; } else if (item.is_extension()) { info->type = developer::ITEM_TYPE_EXTENSION; } else { NOTREACHED(); } if (Manifest::IsUnpackedLocation(item.location())) { info->path.reset( new std::string(UTF16ToUTF8(item.path().LossyDisplayName()))); } info->incognito_enabled = service->IsIncognitoEnabled(item.id()); info->wants_file_access = item.wants_file_access(); info->allow_file_access = service->AllowFileAccess(&item); info->allow_reload = Manifest::IsUnpackedLocation(item.location()); info->is_unpacked = Manifest::IsUnpackedLocation(item.location()); info->terminated = service->terminated_extensions()->Contains(item.id()); info->allow_incognito = item.can_be_incognito_enabled(); info->homepage_url.reset(new std::string( ManifestURL::GetHomepageURL(&item).spec())); if (!ManifestURL::GetOptionsPage(&item).is_empty()) { info->options_url.reset( new std::string(ManifestURL::GetOptionsPage(&item).spec())); } if (!ManifestURL::GetUpdateURL(&item).is_empty()) { info->update_url.reset( new std::string(ManifestURL::GetUpdateURL(&item).spec())); } if (item.is_app()) { info->app_launch_url.reset(new std::string( item.GetFullLaunchURL().spec())); } info->may_disable = system->management_policy()-> UserMayModifySettings(&item, NULL); info->is_app = item.is_app(); info->views = GetInspectablePagesForExtension(&item, item_is_enabled); return info.Pass(); } void DeveloperPrivateGetItemsInfoFunction::GetIconsOnFileThread( ItemInfoList item_list, const std::map<std::string, ExtensionResource> idToIcon) { for (ItemInfoList::iterator iter = item_list.begin(); iter != item_list.end(); ++iter) { developer_private::ItemInfo* info = iter->get(); std::map<std::string, ExtensionResource>::const_iterator resource_ptr = idToIcon.find(info->id); if (resource_ptr != idToIcon.end()) { info->icon = ToDataURL(resource_ptr->second.GetFilePath()).spec(); } } results_ = developer::GetItemsInfo::Results::Create(item_list); content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, base::Bind(&DeveloperPrivateGetItemsInfoFunction::SendResponse, this, true)); } void DeveloperPrivateGetItemsInfoFunction:: GetInspectablePagesForExtensionProcess( const std::set<content::RenderViewHost*>& views, ItemInspectViewList* result) { for (std::set<content::RenderViewHost*>::const_iterator iter = views.begin(); iter != views.end(); ++iter) { content::RenderViewHost* host = *iter; content::WebContents* web_contents = content::WebContents::FromRenderViewHost(host); ViewType host_type = GetViewType(web_contents); if (VIEW_TYPE_EXTENSION_POPUP == host_type || VIEW_TYPE_EXTENSION_DIALOG == host_type) continue; content::RenderProcessHost* process = host->GetProcess(); result->push_back( constructInspectView(web_contents->GetURL(), process->GetID(), host->GetRoutingID(), process->GetBrowserContext()->IsOffTheRecord())); } } void DeveloperPrivateGetItemsInfoFunction:: GetShellWindowPagesForExtensionProfile( const Extension* extension, ItemInspectViewList* result) { ShellWindowRegistry* registry = ShellWindowRegistry::Get(profile()); if (!registry) return; const ShellWindowRegistry::ShellWindowSet windows = registry->GetShellWindowsForApp(extension->id()); for (ShellWindowRegistry::const_iterator it = windows.begin(); it != windows.end(); ++it) { content::WebContents* web_contents = (*it)->web_contents(); RenderViewHost* host = web_contents->GetRenderViewHost(); content::RenderProcessHost* process = host->GetProcess(); result->push_back( constructInspectView(web_contents->GetURL(), process->GetID(), host->GetRoutingID(), process->GetBrowserContext()->IsOffTheRecord())); } } linked_ptr<developer::ItemInspectView> DeveloperPrivateGetItemsInfoFunction:: constructInspectView( const GURL& url, int render_process_id, int render_view_id, bool incognito) { linked_ptr<developer::ItemInspectView> view(new developer::ItemInspectView()); if (url.scheme() == kExtensionScheme) { // No leading slash. view->path = url.path().substr(1); } else { // For live pages, use the full URL. view->path = url.spec(); } view->render_process_id = render_process_id; view->render_view_id = render_view_id; view->incognito = incognito; return view; } ItemInspectViewList DeveloperPrivateGetItemsInfoFunction:: GetInspectablePagesForExtension( const Extension* extension, bool extension_is_enabled) { ItemInspectViewList result; // Get the extension process's active views. ExtensionProcessManager* process_manager = ExtensionSystem::Get(profile())->process_manager(); GetInspectablePagesForExtensionProcess( process_manager->GetRenderViewHostsForExtension(extension->id()), &result); // Get shell window views GetShellWindowPagesForExtensionProfile(extension, &result); // Include a link to start the lazy background page, if applicable. if (BackgroundInfo::HasLazyBackgroundPage(extension) && extension_is_enabled && !process_manager->GetBackgroundHostForExtension(extension->id())) { result.push_back(constructInspectView( BackgroundInfo::GetBackgroundURL(extension), -1, -1, false)); } ExtensionService* service = profile()->GetExtensionService(); // Repeat for the incognito process, if applicable. Don't try to get // shell windows for incognito process. if (service->profile()->HasOffTheRecordProfile() && IncognitoInfo::IsSplitMode(extension)) { process_manager = ExtensionSystem::Get( service->profile()->GetOffTheRecordProfile())->process_manager(); GetInspectablePagesForExtensionProcess( process_manager->GetRenderViewHostsForExtension(extension->id()), &result); if (BackgroundInfo::HasLazyBackgroundPage(extension) && extension_is_enabled && !process_manager->GetBackgroundHostForExtension(extension->id())) { result.push_back(constructInspectView( BackgroundInfo::GetBackgroundURL(extension), -1, -1, false)); } } return result; } bool DeveloperPrivateGetItemsInfoFunction::RunImpl() { scoped_ptr<developer::GetItemsInfo::Params> params( developer::GetItemsInfo::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get() != NULL); bool include_disabled = params->include_disabled; bool include_terminated = params->include_terminated; ExtensionSet items; ExtensionService* service = profile()->GetExtensionService(); items.InsertAll(*service->extensions()); if (include_disabled) { items.InsertAll(*service->disabled_extensions()); } if (include_terminated) { items.InsertAll(*service->terminated_extensions()); } std::map<std::string, ExtensionResource> id_to_icon; ItemInfoList item_list; for (ExtensionSet::const_iterator iter = items.begin(); iter != items.end(); ++iter) { const Extension& item = **iter; ExtensionResource item_resource = IconsInfo::GetIconResource(&item, 48, ExtensionIconSet::MATCH_BIGGER); id_to_icon[item.id()] = item_resource; if (item.location() == Manifest::COMPONENT) continue; // Skip built-in extensions / apps; item_list.push_back(make_linked_ptr<developer::ItemInfo>( CreateItemInfo(item, false).release())); } content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE, base::Bind(&DeveloperPrivateGetItemsInfoFunction::GetIconsOnFileThread, this, item_list, id_to_icon)); return true; } DeveloperPrivateGetItemsInfoFunction::~DeveloperPrivateGetItemsInfoFunction() {} bool DeveloperPrivateAllowFileAccessFunction::RunImpl() { scoped_ptr<AllowFileAccess::Params> params( AllowFileAccess::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); EXTENSION_FUNCTION_VALIDATE(user_gesture_); ExtensionSystem* system = ExtensionSystem::Get(profile()); ManagementPolicy* management_policy = system->management_policy(); ExtensionService* service = profile()->GetExtensionService(); const Extension* extension = service->GetInstalledExtension(params->item_id); bool result = true; if (!extension) { result = false; } else if (!management_policy->UserMayModifySettings(extension, NULL)) { LOG(ERROR) << "Attempt to change allow file access of an extension that " << "non-usermanagable was made. Extension id : " << extension->id(); result = false; } else { service->SetAllowFileAccess(extension, params->allow); result = true; } return result; } DeveloperPrivateAllowFileAccessFunction:: ~DeveloperPrivateAllowFileAccessFunction() {} bool DeveloperPrivateAllowIncognitoFunction::RunImpl() { scoped_ptr<AllowIncognito::Params> params( AllowIncognito::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); ExtensionService* service = profile()->GetExtensionService(); const Extension* extension = service->GetInstalledExtension(params->item_id); bool result = true; if (!extension) result = false; else service->SetIsIncognitoEnabled(extension->id(), params->allow); return result; } DeveloperPrivateAllowIncognitoFunction:: ~DeveloperPrivateAllowIncognitoFunction() {} bool DeveloperPrivateReloadFunction::RunImpl() { scoped_ptr<Reload::Params> params(Reload::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); ExtensionService* service = profile()->GetExtensionService(); CHECK(!params->item_id.empty()); service->ReloadExtension(params->item_id); return true; } bool DeveloperPrivateShowPermissionsDialogFunction::RunImpl() { std::string extension_id; EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &extension_id)); ExtensionService* service = profile()->GetExtensionService(); CHECK(!extension_id.empty()); ShellWindowRegistry* registry = ShellWindowRegistry::Get(profile()); DCHECK(registry); ShellWindow* shell_window = registry->GetShellWindowForRenderViewHost( render_view_host()); prompt_.reset(new ExtensionInstallPrompt(shell_window->web_contents())); const Extension* extension = service->GetInstalledExtension(extension_id); if (!extension) return false; // Released by InstallUIAbort. AddRef(); prompt_->ReviewPermissions(this, extension); return true; } DeveloperPrivateReloadFunction::~DeveloperPrivateReloadFunction() {} void DeveloperPrivateShowPermissionsDialogFunction::InstallUIProceed() { // The permissions dialog only contains a close button. NOTREACHED(); } void DeveloperPrivateShowPermissionsDialogFunction::InstallUIAbort( bool user_initiated) { SendResponse(true); Release(); } DeveloperPrivateShowPermissionsDialogFunction:: DeveloperPrivateShowPermissionsDialogFunction() {} DeveloperPrivateShowPermissionsDialogFunction:: ~DeveloperPrivateShowPermissionsDialogFunction() {} bool DeveloperPrivateRestartFunction::RunImpl() { scoped_ptr<Restart::Params> params(Restart::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); ExtensionService* service = profile()->GetExtensionService(); EXTENSION_FUNCTION_VALIDATE(!params->item_id.empty()); service->RestartExtension(params->item_id); return true; } DeveloperPrivateRestartFunction::~DeveloperPrivateRestartFunction() {} DeveloperPrivateEnableFunction::DeveloperPrivateEnableFunction() {} bool DeveloperPrivateEnableFunction::RunImpl() { scoped_ptr<Enable::Params> params(Enable::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); std::string extension_id = params->item_id; ExtensionSystem* system = ExtensionSystem::Get(profile()); ManagementPolicy* management_policy = system->management_policy(); ExtensionService* service = profile()->GetExtensionService(); const Extension* extension = service->GetInstalledExtension(extension_id); if (!extension || !management_policy->UserMayModifySettings(extension, NULL)) { LOG(ERROR) << "Attempt to enable an extension that is non-usermanagable " "was made. Extension id: " << extension->id(); return false; } if (params->enable) { ExtensionPrefs* prefs = service->extension_prefs(); if (prefs->DidExtensionEscalatePermissions(extension_id)) { ShellWindowRegistry* registry = ShellWindowRegistry::Get(profile()); CHECK(registry); ShellWindow* shell_window = registry->GetShellWindowForRenderViewHost( render_view_host()); if (!shell_window) { return false; } ShowExtensionDisabledDialog( service, shell_window->web_contents(), extension); } else if ((prefs->GetDisableReasons(extension_id) & Extension::DISABLE_UNSUPPORTED_REQUIREMENT) && !requirements_checker_.get()) { // Recheck the requirements. scoped_refptr<const Extension> extension = service->GetExtensionById(extension_id, true );// include_disabled requirements_checker_.reset(new RequirementsChecker); // Released by OnRequirementsChecked. AddRef(); requirements_checker_->Check( extension, base::Bind(&DeveloperPrivateEnableFunction::OnRequirementsChecked, this, extension_id)); } else { service->EnableExtension(extension_id); // Make sure any browser action contained within it is not hidden. prefs->SetBrowserActionVisibility(extension, true); } } else { service->DisableExtension(extension_id, Extension::DISABLE_USER_ACTION); } return true; } void DeveloperPrivateEnableFunction::OnRequirementsChecked( std::string extension_id, std::vector<std::string> requirements_errors) { if (requirements_errors.empty()) { ExtensionService* service = profile()->GetExtensionService(); service->EnableExtension(extension_id); } else { ExtensionErrorReporter::GetInstance()->ReportError( UTF8ToUTF16(JoinString(requirements_errors, ' ')), true /* be noisy */); } Release(); } DeveloperPrivateEnableFunction::~DeveloperPrivateEnableFunction() {} bool DeveloperPrivateInspectFunction::RunImpl() { scoped_ptr<developer::Inspect::Params> params( developer::Inspect::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get() != NULL); const developer::InspectOptions& options = params->options; int render_process_id; base::StringToInt(options.render_process_id, &render_process_id); if (render_process_id == -1) { // This is a lazy background page. Identify if it is a normal // or incognito background page. ExtensionService* service = profile()->GetExtensionService(); if (options.incognito) service = ExtensionSystem::Get( service->profile()->GetOffTheRecordProfile())->extension_service(); const Extension* extension = service->extensions()->GetByID( options.extension_id); DCHECK(extension); // Wakes up the background page and opens the inspect window. service->InspectBackgroundPage(extension); return false; } int render_view_id; base::StringToInt(options.render_view_id, &render_view_id); content::RenderViewHost* host = content::RenderViewHost::FromID( render_process_id, render_view_id); if (!host) { // This can happen if the host has gone away since the page was displayed. return false; } DevToolsWindow::OpenDevToolsWindow(host); return true; } DeveloperPrivateInspectFunction::~DeveloperPrivateInspectFunction() {} bool DeveloperPrivateLoadUnpackedFunction::RunImpl() { string16 select_title = l10n_util::GetStringUTF16(IDS_EXTENSION_LOAD_FROM_DIRECTORY); // Balanced in FileSelected / FileSelectionCanceled. AddRef(); bool result = ShowPicker( ui::SelectFileDialog::SELECT_FOLDER, DeveloperPrivateAPI::Get(profile())->GetLastUnpackedDirectory(), select_title, ui::SelectFileDialog::FileTypeInfo(), 0); return result; } void DeveloperPrivateLoadUnpackedFunction::FileSelected( const base::FilePath& path) { ExtensionService* service = profile()->GetExtensionService(); UnpackedInstaller::Create(service)->Load(path); DeveloperPrivateAPI::Get(profile())->SetLastUnpackedDirectory(path); SendResponse(true); Release(); } void DeveloperPrivateLoadUnpackedFunction::FileSelectionCanceled() { SendResponse(false); Release(); } bool DeveloperPrivateChooseEntryFunction::ShowPicker( ui::SelectFileDialog::Type picker_type, const base::FilePath& last_directory, const string16& select_title, const ui::SelectFileDialog::FileTypeInfo& info, int file_type_index) { ShellWindowRegistry* registry = ShellWindowRegistry::Get(profile()); DCHECK(registry); ShellWindow* shell_window = registry->GetShellWindowForRenderViewHost( render_view_host()); if (!shell_window) { return false; } // The entry picker will hold a reference to this function instance, // and subsequent sending of the function response) until the user has // selected a file or cancelled the picker. At that point, the picker will // delete itself. new EntryPicker(this, shell_window->web_contents(), picker_type, last_directory, select_title, info, file_type_index); return true; } bool DeveloperPrivateChooseEntryFunction::RunImpl() { return false; } DeveloperPrivateChooseEntryFunction::~DeveloperPrivateChooseEntryFunction() {} void DeveloperPrivatePackDirectoryFunction::OnPackSuccess( const base::FilePath& crx_file, const base::FilePath& pem_file) { developer::PackDirectoryResponse response; response.message = UTF16ToUTF8(PackExtensionJob::StandardSuccessMessage(crx_file, pem_file)); response.status = developer::PACK_STATUS_SUCCESS; results_ = developer::PackDirectory::Results::Create(response); SendResponse(true); Release(); } void DeveloperPrivatePackDirectoryFunction::OnPackFailure( const std::string& error, ExtensionCreator::ErrorType error_type) { developer::PackDirectoryResponse response; response.message = error; if (error_type == ExtensionCreator::kCRXExists) { response.item_path = item_path_str_; response.pem_path = key_path_str_; response.override_flags = ExtensionCreator::kOverwriteCRX; response.status = developer::PACK_STATUS_WARNING; } else { response.status = developer::PACK_STATUS_ERROR; } results_ = developer::PackDirectory::Results::Create(response); SendResponse(true); Release(); } bool DeveloperPrivatePackDirectoryFunction::RunImpl() { scoped_ptr<PackDirectory::Params> params( PackDirectory::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get()); int flags = params->flags; item_path_str_ = params->path; key_path_str_ = params->private_key_path; base::FilePath root_directory = base::FilePath::FromWStringHack(UTF8ToWide(item_path_str_)); base::FilePath key_file = base::FilePath::FromWStringHack(UTF8ToWide(key_path_str_)); developer::PackDirectoryResponse response; if (root_directory.empty()) { if (item_path_str_.empty()) response.message = l10n_util::GetStringUTF8( IDS_EXTENSION_PACK_DIALOG_ERROR_ROOT_REQUIRED); else response.message = l10n_util::GetStringUTF8( IDS_EXTENSION_PACK_DIALOG_ERROR_ROOT_INVALID); response.status = developer::PACK_STATUS_ERROR; results_ = developer::PackDirectory::Results::Create(response); SendResponse(true); return true; } if (!key_path_str_.empty() && key_file.empty()) { response.message = l10n_util::GetStringUTF8( IDS_EXTENSION_PACK_DIALOG_ERROR_KEY_INVALID); response.status = developer::PACK_STATUS_ERROR; results_ = developer::PackDirectory::Results::Create(response); SendResponse(true); return true; } // Balanced in OnPackSuccess / OnPackFailure. AddRef(); pack_job_ = new PackExtensionJob(this, root_directory, key_file, flags); pack_job_->Start(); return true; } DeveloperPrivatePackDirectoryFunction::DeveloperPrivatePackDirectoryFunction() {} DeveloperPrivatePackDirectoryFunction::~DeveloperPrivatePackDirectoryFunction() {} DeveloperPrivateLoadUnpackedFunction::~DeveloperPrivateLoadUnpackedFunction() {} bool DeveloperPrivateExportSyncfsFolderToLocalfsFunction::RunImpl() { // TODO(grv) : add unittests. base::FilePath::StringType project_name; EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &project_name)); if (!ValidateFolderName(project_name)) { DLOG(INFO) << "Invalid project_name : [" << project_name << "]"; return false; } context_ = content::BrowserContext::GetStoragePartition(profile(), render_view_host()->GetSiteInstance())->GetFileSystemContext(); content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, base::Bind(&DeveloperPrivateExportSyncfsFolderToLocalfsFunction:: ReadSyncFileSystemDirectory, this, project_name)); return true; } void DeveloperPrivateExportSyncfsFolderToLocalfsFunction:: ReadSyncFileSystemDirectory( const base::FilePath::StringType& project_name) { std::string origin_url( Extension::GetBaseURLFromExtensionId(extension_id()).spec()); fileapi::FileSystemURL url(sync_file_system::CreateSyncableFileSystemURL( GURL(origin_url), sync_file_system::DriveFileSyncService::kServiceName, base::FilePath())); base::PlatformFileError error_code; fileapi::FileSystemOperation* op = context_->CreateFileSystemOperation(url, &error_code); DCHECK(op); op->ReadDirectory(url, base::Bind( &DeveloperPrivateExportSyncfsFolderToLocalfsFunction:: ReadSyncFileSystemDirectoryCb, this, project_name)); } void DeveloperPrivateExportSyncfsFolderToLocalfsFunction:: ReadSyncFileSystemDirectoryCb( const base::FilePath::StringType& project_name, base::PlatformFileError status, const fileapi::FileSystemOperation::FileEntryList& file_list, bool has_more) { if (status != base::PLATFORM_FILE_OK) { DLOG(ERROR) << "Error in copying files from sync filesystem."; return; } for (size_t i = 0; i < file_list.size(); ++i) { std::string origin_url( Extension::GetBaseURLFromExtensionId(extension_id()).spec()); fileapi::FileSystemURL url(sync_file_system::CreateSyncableFileSystemURL( GURL(origin_url), sync_file_system::DriveFileSyncService::kServiceName, base::FilePath(file_list[i].name))); base::FilePath target_path(profile()->GetPath()); target_path = target_path.Append(kUnpackedAppsFolder); target_path = target_path.Append(project_name); target_path = target_path.Append(file_list[i].name); base::PlatformFileError error_code; fileapi::FileSystemOperation* op = context_->CreateFileSystemOperation(url, &error_code); DCHECK(op); if (error_code != base::PLATFORM_FILE_OK) { DLOG(ERROR) << "Error in copying files from sync filesystem."; return; } op->CreateSnapshotFile( url, base::Bind( &DeveloperPrivateExportSyncfsFolderToLocalfsFunction:: SnapshotFileCallback, this, target_path)); } } void DeveloperPrivateExportSyncfsFolderToLocalfsFunction::SnapshotFileCallback( const base::FilePath& target_path, base::PlatformFileError result, const base::PlatformFileInfo& file_info, const base::FilePath& src_path, const scoped_refptr<webkit_blob::ShareableFileReference>& file_ref) { if (result != base::PLATFORM_FILE_OK) { DLOG(ERROR) << "Error in copying files from sync filesystem."; return; } content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE, base::Bind(&DeveloperPrivateExportSyncfsFolderToLocalfsFunction::CopyFile, this, src_path, target_path)); } void DeveloperPrivateExportSyncfsFolderToLocalfsFunction::CopyFile( const base::FilePath& src_path, const base::FilePath& target_path) { // Return silently if the directory creation fails. if (!file_util::CreateDirectory(target_path.DirName())) { DLOG(ERROR) << "Error in copying files from sync filesystem."; return; } file_util::CopyFile(src_path, target_path); } DeveloperPrivateExportSyncfsFolderToLocalfsFunction:: DeveloperPrivateExportSyncfsFolderToLocalfsFunction() {} DeveloperPrivateExportSyncfsFolderToLocalfsFunction:: ~DeveloperPrivateExportSyncfsFolderToLocalfsFunction() {} bool DeveloperPrivateLoadProjectToSyncfsFunction::RunImpl() { // TODO(grv) : add unittests. base::FilePath::StringType project_name; EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &project_name)); if (!ValidateFolderName(project_name)) { DLOG(INFO) << "Invalid project_name : [" << project_name << "]"; return false; } context_ = content::BrowserContext::GetStoragePartition(profile(), render_view_host()->GetSiteInstance())->GetFileSystemContext(); content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE, base::Bind(&DeveloperPrivateLoadProjectToSyncfsFunction:: CopyFolder, this, project_name)); return true; } void DeveloperPrivateLoadProjectToSyncfsFunction::CopyFolder( const base::FilePath::StringType& project_name) { base::FilePath path(profile()->GetPath()); path = path.Append(kUnpackedAppsFolder); path = path.Append(project_name); std::vector<base::FilePath> paths = ListFolder(path); content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, base::Bind(&DeveloperPrivateLoadProjectToSyncfsFunction:: CopyFiles, this, paths)); } void DeveloperPrivateLoadProjectToSyncfsFunction::CopyFiles( const std::vector<base::FilePath>& paths) { std::string origin_url( Extension::GetBaseURLFromExtensionId(extension_id()).spec()); fileapi::FileSystemURL url(sync_file_system::CreateSyncableFileSystemURL( GURL(origin_url), sync_file_system::DriveFileSyncService::kServiceName, base::FilePath())); pendingCallbacksCount_ = paths.size(); for (size_t i = 0; i < paths.size(); ++i) { base::PlatformFileError error_code; fileapi::FileSystemOperation* op = context_->CreateFileSystemOperation(url, &error_code); DCHECK(op); std::string origin_url( Extension::GetBaseURLFromExtensionId(extension_id()).spec()); fileapi::FileSystemURL dest_url(sync_file_system::CreateSyncableFileSystemURL( GURL(origin_url), sync_file_system::DriveFileSyncService::kServiceName, base::FilePath(paths[i].BaseName()))); op->AsLocalFileSystemOperation()->CopyInForeignFile(paths[i], dest_url, base::Bind(&DeveloperPrivateLoadProjectToSyncfsFunction:: CopyFilesCallback, this)); } } void DeveloperPrivateLoadProjectToSyncfsFunction::CopyFilesCallback( const base::PlatformFileError result) { pendingCallbacksCount_--; if (success_ && result != base::PLATFORM_FILE_OK) { SetError("Error in copying files to sync filesystem."); success_ = false; } if (!pendingCallbacksCount_) { content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, base::Bind(&DeveloperPrivateLoadProjectToSyncfsFunction::SendResponse, this, success_)); } } DeveloperPrivateLoadProjectToSyncfsFunction:: DeveloperPrivateLoadProjectToSyncfsFunction() : pendingCallbacksCount_(0), success_(true) {} DeveloperPrivateLoadProjectToSyncfsFunction:: ~DeveloperPrivateLoadProjectToSyncfsFunction() {} bool DeveloperPrivateGetProjectsInfoFunction::RunImpl() { // TODO(grv) : add unittests. content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE, base::Bind(&DeveloperPrivateGetProjectsInfoFunction::ReadFolder, this)); // Released by ReadFolder AddRef(); return true; } void DeveloperPrivateGetProjectsInfoFunction::ReadFolder() { base::FilePath path(profile()->GetPath()); path = path.Append(kUnpackedAppsFolder); std::vector<base::FilePath> paths = ListFolder(path); ProjectInfoList info_list; for (size_t i = 0; i < paths.size(); ++i) { scoped_ptr<developer::ProjectInfo> info(new developer::ProjectInfo()); info->name = paths[i].BaseName().MaybeAsASCII(); info_list.push_back( make_linked_ptr<developer::ProjectInfo>(info.release())); } results_ = developer::GetProjectsInfo::Results::Create(info_list); content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, base::Bind(&DeveloperPrivateGetProjectsInfoFunction::SendResponse, this, true)); Release(); } DeveloperPrivateGetProjectsInfoFunction:: DeveloperPrivateGetProjectsInfoFunction() {} DeveloperPrivateGetProjectsInfoFunction:: ~DeveloperPrivateGetProjectsInfoFunction() {} bool DeveloperPrivateLoadProjectFunction::RunImpl() { // TODO(grv) : add unit tests. base::FilePath::StringType project_name; EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &project_name)); if (!ValidateFolderName(project_name)) { DLOG(INFO) << "Invalid project_name : [" << project_name << "]"; return false; } base::FilePath path(profile()->GetPath()); path = path.Append(kUnpackedAppsFolder); // TODO(grv) : Sanitize / check project_name. path = path.Append(project_name); ExtensionService* service = profile()->GetExtensionService(); UnpackedInstaller::Create(service)->Load(path); SendResponse(true); return true; } DeveloperPrivateLoadProjectFunction::DeveloperPrivateLoadProjectFunction() {} DeveloperPrivateLoadProjectFunction::~DeveloperPrivateLoadProjectFunction() {} bool DeveloperPrivateChoosePathFunction::RunImpl() { scoped_ptr<developer::ChoosePath::Params> params( developer::ChoosePath::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params.get() != NULL); ui::SelectFileDialog::Type type = ui::SelectFileDialog::SELECT_FOLDER; ui::SelectFileDialog::FileTypeInfo info; if (params->select_type == developer::SELECT_TYPE_FILE) { type = ui::SelectFileDialog::SELECT_OPEN_FILE; } string16 select_title; int file_type_index = 0; if (params->file_type == developer::FILE_TYPE_LOAD) select_title = l10n_util::GetStringUTF16(IDS_EXTENSION_LOAD_FROM_DIRECTORY); else if (params->file_type== developer::FILE_TYPE_PEM) { select_title = l10n_util::GetStringUTF16( IDS_EXTENSION_PACK_DIALOG_SELECT_KEY); info.extensions.push_back(std::vector<base::FilePath::StringType>()); info.extensions.front().push_back(FILE_PATH_LITERAL("pem")); info.extension_description_overrides.push_back( l10n_util::GetStringUTF16( IDS_EXTENSION_PACK_DIALOG_KEY_FILE_TYPE_DESCRIPTION)); info.include_all_files = true; file_type_index = 1; } else { NOTREACHED(); } // Balanced by FileSelected / FileSelectionCanceled. AddRef(); bool result = ShowPicker( type, DeveloperPrivateAPI::Get(profile())->GetLastUnpackedDirectory(), select_title, info, file_type_index); return result; } void DeveloperPrivateChoosePathFunction::FileSelected( const base::FilePath& path) { SetResult(base::Value::CreateStringValue( UTF16ToUTF8(path.LossyDisplayName()))); SendResponse(true); Release(); } void DeveloperPrivateChoosePathFunction::FileSelectionCanceled() { SendResponse(false); Release(); } DeveloperPrivateChoosePathFunction::~DeveloperPrivateChoosePathFunction() {} bool DeveloperPrivateGetStringsFunction::RunImpl() { DictionaryValue* dict = new DictionaryValue(); SetResult(dict); #define SET_STRING(id, idr) \ dict->SetString(id, l10n_util::GetStringUTF16(idr)) SET_STRING("extensionSettings", IDS_MANAGE_EXTENSIONS_SETTING_WINDOWS_TITLE); SET_STRING("appsDevtoolSearch", IDS_APPS_DEVTOOL_SEARCH); SET_STRING("appsDevtoolNoApps", IDS_APPS_DEVTOOL_NO_APPS_INSTALLED); SET_STRING("appsDevtoolApps", IDS_APPS_DEVTOOL_APPS_INSTALLED); SET_STRING("appsDevtoolExtensions", IDS_APPS_DEVTOOL_EXTENSIONS_INSTALLED); SET_STRING("appsDevtoolNoExtensions", IDS_EXTENSIONS_NONE_INSTALLED); SET_STRING("appsDevtoolTitle", IDS_APPS_DEVTOOL_TITLE); SET_STRING("extensionSettingsGetMoreExtensions", IDS_GET_MORE_EXTENSIONS); SET_STRING("extensionSettingsExtensionId", IDS_EXTENSIONS_ID); SET_STRING("extensionSettingsExtensionPath", IDS_EXTENSIONS_PATH); SET_STRING("extensionSettingsInspectViews", IDS_EXTENSIONS_INSPECT_VIEWS); SET_STRING("extensionSettingsInstallWarnings", IDS_EXTENSIONS_INSTALL_WARNINGS); SET_STRING("viewIncognito", IDS_EXTENSIONS_VIEW_INCOGNITO); SET_STRING("viewInactive", IDS_EXTENSIONS_VIEW_INACTIVE); SET_STRING("extensionSettingsEnable", IDS_EXTENSIONS_ENABLE); SET_STRING("extensionSettingsEnabled", IDS_EXTENSIONS_ENABLED); SET_STRING("extensionSettingsRemove", IDS_EXTENSIONS_REMOVE); SET_STRING("extensionSettingsEnableIncognito", IDS_EXTENSIONS_ENABLE_INCOGNITO); SET_STRING("extensionSettingsAllowFileAccess", IDS_EXTENSIONS_ALLOW_FILE_ACCESS); SET_STRING("extensionSettingsReloadTerminated", IDS_EXTENSIONS_RELOAD_TERMINATED); SET_STRING("extensionSettingsReloadUnpacked", IDS_EXTENSIONS_RELOAD_UNPACKED); SET_STRING("extensionSettingsLaunch", IDS_EXTENSIONS_LAUNCH); SET_STRING("extensionSettingsRestart", IDS_EXTENSIONS_RESTART); SET_STRING("extensionSettingsOptions", IDS_EXTENSIONS_OPTIONS_LINK); SET_STRING("extensionSettingsActivity", IDS_EXTENSIONS_ACTIVITY_LINK); SET_STRING("extensionSettingsPermissions", IDS_EXTENSIONS_PERMISSIONS_LINK); SET_STRING("extensionSettingsVisitWebsite", IDS_EXTENSIONS_VISIT_WEBSITE); SET_STRING("extensionSettingsVisitWebStore", IDS_EXTENSIONS_VISIT_WEBSTORE); SET_STRING("extensionSettingsPolicyControlled", IDS_EXTENSIONS_POLICY_CONTROLLED); SET_STRING("extensionSettingsManagedMode", IDS_EXTENSIONS_LOCKED_MANAGED_MODE); SET_STRING("extensionSettingsShowButton", IDS_EXTENSIONS_SHOW_BUTTON); SET_STRING("appsDevtoolLoadUnpackedButton", IDS_APPS_DEVTOOL_LOAD_UNPACKED_BUTTON); SET_STRING("appsDevtoolPackButton", IDS_APPS_DEVTOOL_PACK_BUTTON); SET_STRING("extensionSettingsCommandsLink", IDS_EXTENSIONS_COMMANDS_CONFIGURE); SET_STRING("appsDevtoolUpdateButton", IDS_APPS_DEVTOOL_UPDATE_BUTTON); SET_STRING("extensionSettingsWarningsTitle", IDS_EXTENSION_WARNINGS_TITLE); SET_STRING("extensionSettingsShowDetails", IDS_EXTENSIONS_SHOW_DETAILS); SET_STRING("extensionSettingsHideDetails", IDS_EXTENSIONS_HIDE_DETAILS); SET_STRING("extensionUninstall", IDS_EXTENSIONS_UNINSTALL); SET_STRING("extensionsPermissionsHeading", IDS_EXTENSIONS_PERMISSIONS_HEADING); SET_STRING("extensionsPermissionsClose", IDS_EXTENSIONS_PERMISSIONS_CLOSE); // Pack Extension strings SET_STRING("packExtensionOverlay", IDS_EXTENSION_PACK_DIALOG_TITLE); SET_STRING("packExtensionHeading", IDS_EXTENSION_PACK_DIALOG_HEADING); SET_STRING("packExtensionCommit", IDS_EXTENSION_PACK_BUTTON); SET_STRING("ok",IDS_OK); SET_STRING("cancel",IDS_CANCEL); SET_STRING("packExtensionRootDir", IDS_EXTENSION_PACK_DIALOG_ROOT_DIRECTORY_LABEL); SET_STRING("packExtensionPrivateKey", IDS_EXTENSION_PACK_DIALOG_PRIVATE_KEY_LABEL); SET_STRING("packExtensionBrowseButton", IDS_EXTENSION_PACK_DIALOG_BROWSE); SET_STRING("packExtensionProceedAnyway", IDS_EXTENSION_PROCEED_ANYWAY); SET_STRING("packExtensionWarningTitle", IDS_EXTENSION_PACK_WARNING_TITLE); SET_STRING("packExtensionErrorTitle", IDS_EXTENSION_PACK_ERROR_TITLE); #undef SET_STRING return true; } DeveloperPrivateGetStringsFunction::~DeveloperPrivateGetStringsFunction() {} } // namespace api } // namespace extensions