OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 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/ui/webui/extensions/extension_loader_handler.h" |
| 6 |
| 7 #include "base/bind.h" |
| 8 #include "base/file_util.h" |
| 9 #include "base/logging.h" |
| 10 #include "base/memory/ref_counted.h" |
| 11 #include "base/strings/string16.h" |
| 12 #include "base/strings/string_util.h" |
| 13 #include "base/strings/stringprintf.h" |
| 14 #include "base/strings/utf_string_conversions.h" |
| 15 #include "base/values.h" |
| 16 #include "chrome/browser/extensions/unpacked_installer.h" |
| 17 #include "chrome/browser/profiles/profile.h" |
| 18 #include "chrome/browser/ui/chrome_select_file_policy.h" |
| 19 #include "content/public/browser/browser_thread.h" |
| 20 #include "content/public/browser/user_metrics.h" |
| 21 #include "content/public/browser/web_contents.h" |
| 22 #include "content/public/browser/web_contents_view.h" |
| 23 #include "content/public/browser/web_ui.h" |
| 24 #include "content/public/browser/web_ui_data_source.h" |
| 25 #include "extensions/browser/extension_system.h" |
| 26 #include "extensions/browser/file_highlighter.h" |
| 27 #include "extensions/common/constants.h" |
| 28 #include "extensions/common/extension.h" |
| 29 #include "extensions/common/manifest_constants.h" |
| 30 #include "grit/generated_resources.h" |
| 31 #include "third_party/re2/re2/re2.h" |
| 32 #include "ui/base/l10n/l10n_util.h" |
| 33 #include "ui/shell_dialogs/select_file_dialog.h" |
| 34 |
| 35 namespace extensions { |
| 36 |
| 37 namespace { |
| 38 |
| 39 // Read a file to a string and return. |
| 40 std::string ReadFileToString(const base::FilePath& path) { |
| 41 std::string data; |
| 42 // This call can fail, but it doesn't matter for our purposes. If it fails, |
| 43 // we simply return an empty string for the manifest, and ignore it. |
| 44 base::ReadFileToString(path, &data); |
| 45 return data; |
| 46 } |
| 47 |
| 48 } // namespace |
| 49 |
| 50 class ExtensionLoaderHandler::FileHelper |
| 51 : public ui::SelectFileDialog::Listener { |
| 52 public: |
| 53 explicit FileHelper(ExtensionLoaderHandler* loader_handler); |
| 54 virtual ~FileHelper(); |
| 55 |
| 56 // Create a FileDialog for the user to select the unpacked extension |
| 57 // directory. |
| 58 void ChooseFile(); |
| 59 |
| 60 private: |
| 61 // ui::SelectFileDialog::Listener implementation. |
| 62 virtual void FileSelected(const base::FilePath& path, |
| 63 int index, |
| 64 void* params) OVERRIDE; |
| 65 virtual void MultiFilesSelected( |
| 66 const std::vector<base::FilePath>& files, void* params) OVERRIDE; |
| 67 |
| 68 // The associated ExtensionLoaderHandler. Weak, but guaranteed to be alive, |
| 69 // as it owns this object. |
| 70 ExtensionLoaderHandler* loader_handler_; |
| 71 |
| 72 // The dialog used to pick a directory when loading an unpacked extension. |
| 73 scoped_refptr<ui::SelectFileDialog> load_extension_dialog_; |
| 74 |
| 75 // The last selected directory, so we can start in the same spot. |
| 76 base::FilePath last_unpacked_directory_; |
| 77 |
| 78 // The title of the dialog. |
| 79 base::string16 title_; |
| 80 |
| 81 DISALLOW_COPY_AND_ASSIGN(FileHelper); |
| 82 }; |
| 83 |
| 84 ExtensionLoaderHandler::FileHelper::FileHelper( |
| 85 ExtensionLoaderHandler* loader_handler) |
| 86 : loader_handler_(loader_handler), |
| 87 title_(l10n_util::GetStringUTF16(IDS_EXTENSION_LOAD_FROM_DIRECTORY)) { |
| 88 } |
| 89 |
| 90 ExtensionLoaderHandler::FileHelper::~FileHelper() { |
| 91 // There may be a pending file dialog; inform it the listener is destroyed so |
| 92 // it doesn't try and call back. |
| 93 if (load_extension_dialog_.get()) |
| 94 load_extension_dialog_->ListenerDestroyed(); |
| 95 } |
| 96 |
| 97 void ExtensionLoaderHandler::FileHelper::ChooseFile() { |
| 98 static const int kFileTypeIndex = 0; // No file type information to index. |
| 99 static const ui::SelectFileDialog::Type kSelectType = |
| 100 ui::SelectFileDialog::SELECT_FOLDER; |
| 101 |
| 102 if (!load_extension_dialog_.get()) { |
| 103 load_extension_dialog_ = ui::SelectFileDialog::Create( |
| 104 this, |
| 105 new ChromeSelectFilePolicy( |
| 106 loader_handler_->web_ui()->GetWebContents())); |
| 107 } |
| 108 |
| 109 load_extension_dialog_->SelectFile( |
| 110 kSelectType, |
| 111 title_, |
| 112 last_unpacked_directory_, |
| 113 NULL, |
| 114 kFileTypeIndex, |
| 115 base::FilePath::StringType(), |
| 116 loader_handler_->web_ui()-> |
| 117 GetWebContents()->GetView()->GetTopLevelNativeWindow(), |
| 118 NULL); |
| 119 |
| 120 content::RecordComputedAction("Options_LoadUnpackedExtension"); |
| 121 } |
| 122 |
| 123 void ExtensionLoaderHandler::FileHelper::FileSelected( |
| 124 const base::FilePath& path, int index, void* params) { |
| 125 loader_handler_->LoadUnpackedExtensionImpl(path); |
| 126 } |
| 127 |
| 128 void ExtensionLoaderHandler::FileHelper::MultiFilesSelected( |
| 129 const std::vector<base::FilePath>& files, void* params) { |
| 130 NOTREACHED(); |
| 131 } |
| 132 |
| 133 ExtensionLoaderHandler::ExtensionLoaderHandler(Profile* profile) |
| 134 : profile_(profile), |
| 135 file_helper_(new FileHelper(this)), |
| 136 weak_ptr_factory_(this) { |
| 137 DCHECK(profile_); |
| 138 } |
| 139 |
| 140 ExtensionLoaderHandler::~ExtensionLoaderHandler() { |
| 141 } |
| 142 |
| 143 void ExtensionLoaderHandler::GetLocalizedValues( |
| 144 content::WebUIDataSource* source) { |
| 145 source->AddString( |
| 146 "extensionLoadErrorHeading", |
| 147 l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_ERROR_HEADING)); |
| 148 source->AddString( |
| 149 "extensionLoadErrorMessage", |
| 150 l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_ERROR_MESSAGE)); |
| 151 source->AddString( |
| 152 "extensionLoadErrorRetry", |
| 153 l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_ERROR_RETRY)); |
| 154 source->AddString( |
| 155 "extensionLoadErrorGiveUp", |
| 156 l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_ERROR_GIVE_UP)); |
| 157 source->AddString( |
| 158 "extensionLoadCouldNotLoadManifest", |
| 159 l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_COULD_NOT_LOAD_MANIFEST)); |
| 160 } |
| 161 |
| 162 void ExtensionLoaderHandler::RegisterMessages() { |
| 163 web_ui()->RegisterMessageCallback( |
| 164 "extensionLoaderLoadUnpacked", |
| 165 base::Bind(&ExtensionLoaderHandler::HandleLoadUnpacked, |
| 166 weak_ptr_factory_.GetWeakPtr())); |
| 167 web_ui()->RegisterMessageCallback( |
| 168 "extensionLoaderRetry", |
| 169 base::Bind(&ExtensionLoaderHandler::HandleRetry, |
| 170 weak_ptr_factory_.GetWeakPtr())); |
| 171 } |
| 172 |
| 173 void ExtensionLoaderHandler::HandleLoadUnpacked(const base::ListValue* args) { |
| 174 DCHECK(args->empty()); |
| 175 file_helper_->ChooseFile(); |
| 176 } |
| 177 |
| 178 void ExtensionLoaderHandler::HandleRetry(const base::ListValue* args) { |
| 179 DCHECK(args->empty()); |
| 180 LoadUnpackedExtensionImpl(failed_path_); |
| 181 } |
| 182 |
| 183 void ExtensionLoaderHandler::LoadUnpackedExtensionImpl( |
| 184 const base::FilePath& file_path) { |
| 185 scoped_refptr<UnpackedInstaller> installer = UnpackedInstaller::Create( |
| 186 ExtensionSystem::Get(profile_)->extension_service()); |
| 187 installer->set_on_failure_callback( |
| 188 base::Bind(&ExtensionLoaderHandler::OnLoadFailure, |
| 189 weak_ptr_factory_.GetWeakPtr())); |
| 190 installer->Load(file_path); |
| 191 } |
| 192 |
| 193 void ExtensionLoaderHandler::OnLoadFailure(const base::FilePath& file_path, |
| 194 const std::string& error) { |
| 195 failed_path_ = file_path; |
| 196 size_t line = 0u; |
| 197 size_t column = 0u; |
| 198 std::string regex = |
| 199 base::StringPrintf("%s Line: (\\d+), column: (\\d+), Syntax error.", |
| 200 manifest_errors::kManifestParseError); |
| 201 // If this was a manifest error, then we can get the manifest and |
| 202 // highlight the error. This will read the manifest to disk, and call |
| 203 // NotifyFrontendOfFailure with the read manifest contents. |
| 204 if (re2::RE2::FullMatch(error, regex, &line, &column)) { |
| 205 base::PostTaskAndReplyWithResult( |
| 206 content::BrowserThread::GetBlockingPool(), |
| 207 FROM_HERE, |
| 208 base::Bind(&ReadFileToString, file_path.Append(kManifestFilename)), |
| 209 base::Bind(&ExtensionLoaderHandler::NotifyFrontendOfFailure, |
| 210 weak_ptr_factory_.GetWeakPtr(), |
| 211 file_path, |
| 212 error, |
| 213 line)); |
| 214 return; |
| 215 } |
| 216 |
| 217 // Otherwise, just show the error. |
| 218 NotifyFrontendOfFailure(file_path, error, 0u, base::EmptyString()); |
| 219 } |
| 220 |
| 221 void ExtensionLoaderHandler::NotifyFrontendOfFailure( |
| 222 const base::FilePath& file_path, |
| 223 const std::string& error, |
| 224 size_t line_number, |
| 225 const std::string& manifest) { |
| 226 base::DictionaryValue manifest_value; |
| 227 if (!manifest.empty()) { |
| 228 SourceHighlighter highlighter(manifest, line_number); |
| 229 highlighter.SetHighlightedRegions(&manifest_value); |
| 230 } |
| 231 |
| 232 base::StringValue file_value(file_path.LossyDisplayName()); |
| 233 base::StringValue error_value(base::UTF8ToUTF16(error)); |
| 234 web_ui()->CallJavascriptFunction( |
| 235 "extensions.ExtensionLoader.notifyLoadFailed", |
| 236 file_value, |
| 237 error_value, |
| 238 manifest_value); |
| 239 } |
| 240 |
| 241 } // namespace extensions |
OLD | NEW |