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 |