Chromium Code Reviews| 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 } | |
| 158 | |
| 159 void ExtensionLoaderHandler::RegisterMessages() { | |
| 160 web_ui()->RegisterMessageCallback( | |
| 161 "extensionLoaderLoadUnpacked", | |
| 162 base::Bind(&ExtensionLoaderHandler::HandleLoadUnpacked, | |
| 163 weak_ptr_factory_.GetWeakPtr())); | |
| 164 web_ui()->RegisterMessageCallback( | |
| 165 "extensionLoaderRetry", | |
| 166 base::Bind(&ExtensionLoaderHandler::HandleRetry, | |
| 167 weak_ptr_factory_.GetWeakPtr())); | |
| 168 } | |
| 169 | |
| 170 void ExtensionLoaderHandler::HandleLoadUnpacked(const base::ListValue* args) { | |
| 171 DCHECK(args->empty()); | |
| 172 file_helper_->ChooseFile(); | |
| 173 } | |
| 174 | |
| 175 void ExtensionLoaderHandler::HandleRetry(const base::ListValue* args) { | |
| 176 DCHECK(args->empty()); | |
| 177 LoadUnpackedExtensionImpl(failed_path_); | |
| 178 } | |
| 179 | |
| 180 void ExtensionLoaderHandler::LoadUnpackedExtensionImpl( | |
| 181 const base::FilePath& file_path) { | |
| 182 scoped_refptr<UnpackedInstaller> installer = UnpackedInstaller::Create( | |
| 183 ExtensionSystem::Get(profile_)->extension_service()); | |
| 184 installer->set_on_failure_callback( | |
| 185 base::Bind(&ExtensionLoaderHandler::OnLoadFailure, | |
| 186 weak_ptr_factory_.GetWeakPtr())); | |
| 187 installer->Load(file_path); | |
| 188 } | |
| 189 | |
| 190 void ExtensionLoaderHandler::OnLoadFailure(const base::FilePath& file_path, | |
| 191 const std::string& error) { | |
| 192 failed_path_ = file_path; | |
| 193 size_t line = 1u; | |
| 194 size_t column = 1u; | |
|
Finnur
2014/04/25 16:47:21
Would probably make sense to have these be invalid
Devlin
2014/04/25 18:13:12
Sure, done.
| |
| 195 std::string regex = | |
| 196 base::StringPrintf("%s Line: (\\d+), column: (\\d+), Syntax error.", | |
| 197 manifest_errors::kManifestParseError); | |
| 198 // If this was a manifest error, then we can get the manifest and | |
| 199 // highlight the error. This will read the manifest to disk, and call | |
| 200 // NotifyFrontendOfFailure with the read manifest contents. | |
| 201 LOG(WARNING) << "Error: " << error; | |
| 202 LOG(WARNING) << "Regex: " << regex; | |
|
Finnur
2014/04/25 16:47:21
There are a lot of LOG(WARNING) statements in this
Devlin
2014/04/25 18:13:12
Whoops, sorry (remnant of rushing to get the revie
| |
| 203 if (re2::RE2::FullMatch(error, regex, &line, &column)) { | |
| 204 LOG(WARNING) << "Matched!"; | |
| 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 LOG(WARNING) << "Manifest not empty!"; | |
| 229 SourceHighlighter highlighter(manifest, line_number); | |
| 230 highlighter.SetHighlightedRegions(&manifest_value); | |
| 231 } | |
| 232 | |
| 233 base::StringValue file_value(file_path.LossyDisplayName()); | |
| 234 base::StringValue error_value(base::UTF8ToUTF16(error)); | |
| 235 web_ui()->CallJavascriptFunction( | |
| 236 "extensions.ExtensionLoader.notifyLoadFailed", | |
| 237 file_value, | |
| 238 error_value, | |
| 239 manifest_value); | |
| 240 } | |
| 241 | |
| 242 } // namespace extensions | |
| OLD | NEW |