Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / webui / extensions / extension_loader_handler.cc
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/files/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 "chrome/browser/extensions/path_util.h"
16 #include "chrome/browser/extensions/unpacked_installer.h"
17 #include "chrome/browser/extensions/zipfile_installer.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/ui/chrome_select_file_policy.h"
20 #include "chrome/grit/generated_resources.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "content/public/browser/user_metrics.h"
23 #include "content/public/browser/web_contents.h"
24 #include "content/public/browser/web_ui.h"
25 #include "content/public/browser/web_ui_data_source.h"
26 #include "extensions/browser/extension_system.h"
27 #include "extensions/browser/file_highlighter.h"
28 #include "extensions/common/constants.h"
29 #include "extensions/common/extension.h"
30 #include "extensions/common/manifest_constants.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()->GetWebContents()->GetTopLevelNativeWindow(),
117       NULL);
118
119   content::RecordComputedAction("Options_LoadUnpackedExtension");
120 }
121
122 void ExtensionLoaderHandler::FileHelper::FileSelected(
123     const base::FilePath& path, int index, void* params) {
124   loader_handler_->LoadUnpackedExtensionImpl(path);
125 }
126
127 void ExtensionLoaderHandler::FileHelper::MultiFilesSelected(
128       const std::vector<base::FilePath>& files, void* params) {
129   NOTREACHED();
130 }
131
132 ExtensionLoaderHandler::ExtensionLoaderHandler(Profile* profile)
133     : profile_(profile),
134       file_helper_(new FileHelper(this)),
135       extension_error_reporter_observer_(this),
136       ui_ready_(false),
137       weak_ptr_factory_(this) {
138   DCHECK(profile_);
139   extension_error_reporter_observer_.Add(ExtensionErrorReporter::GetInstance());
140 }
141
142 ExtensionLoaderHandler::~ExtensionLoaderHandler() {
143 }
144
145 void ExtensionLoaderHandler::GetLocalizedValues(
146     content::WebUIDataSource* source) {
147   source->AddString(
148       "extensionLoadErrorHeading",
149       l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_ERROR_HEADING));
150   source->AddString(
151       "extensionLoadErrorMessage",
152       l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_ERROR_MESSAGE));
153   source->AddString(
154       "extensionLoadErrorRetry",
155       l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_ERROR_RETRY));
156   source->AddString(
157       "extensionLoadErrorGiveUp",
158       l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_ERROR_GIVE_UP));
159   source->AddString(
160       "extensionLoadCouldNotLoadManifest",
161       l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_COULD_NOT_LOAD_MANIFEST));
162   source->AddString(
163       "extensionLoadAdditionalFailures",
164       l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_ADDITIONAL_FAILURES));
165 }
166
167 void ExtensionLoaderHandler::RegisterMessages() {
168   // We observe WebContents in order to detect page refreshes, since notifying
169   // the frontend of load failures must be delayed until the page finishes
170   // loading. We never call Observe(NULL) because this object is constructed
171   // on page load and persists between refreshes.
172   content::WebContentsObserver::Observe(web_ui()->GetWebContents());
173
174   web_ui()->RegisterMessageCallback(
175       "extensionLoaderLoadUnpacked",
176       base::Bind(&ExtensionLoaderHandler::HandleLoadUnpacked,
177                  weak_ptr_factory_.GetWeakPtr()));
178   web_ui()->RegisterMessageCallback(
179       "extensionLoaderRetry",
180       base::Bind(&ExtensionLoaderHandler::HandleRetry,
181                  weak_ptr_factory_.GetWeakPtr()));
182   web_ui()->RegisterMessageCallback(
183       "extensionLoaderIgnoreFailure",
184       base::Bind(&ExtensionLoaderHandler::HandleIgnoreFailure,
185                  weak_ptr_factory_.GetWeakPtr()));
186   web_ui()->RegisterMessageCallback(
187       "extensionLoaderDisplayFailures",
188       base::Bind(&ExtensionLoaderHandler::HandleDisplayFailures,
189                  weak_ptr_factory_.GetWeakPtr()));
190 }
191
192 void ExtensionLoaderHandler::HandleLoadUnpacked(const base::ListValue* args) {
193   DCHECK(args->empty());
194   file_helper_->ChooseFile();
195 }
196
197 void ExtensionLoaderHandler::HandleRetry(const base::ListValue* args) {
198   DCHECK(args->empty());
199   const base::FilePath file_path = failed_paths_.back();
200   failed_paths_.pop_back();
201   LoadUnpackedExtensionImpl(file_path);
202 }
203
204 void ExtensionLoaderHandler::HandleIgnoreFailure(const base::ListValue* args) {
205   DCHECK(args->empty());
206   failed_paths_.pop_back();
207 }
208
209 void ExtensionLoaderHandler::HandleDisplayFailures(
210     const base::ListValue* args) {
211   DCHECK(args->empty());
212   ui_ready_ = true;
213
214   // Notify the frontend of any load failures that were triggered while the
215   // chrome://extensions page was loading.
216   if (!failures_.empty())
217     NotifyFrontendOfFailure();
218 }
219
220 void ExtensionLoaderHandler::LoadUnpackedExtensionImpl(
221     const base::FilePath& file_path) {
222   if (EndsWith(file_path.AsUTF16Unsafe(),
223                base::ASCIIToUTF16(".zip"),
224                false /* case insensitive */)) {
225     scoped_refptr<ZipFileInstaller> installer = ZipFileInstaller::Create(
226         ExtensionSystem::Get(profile_)->extension_service());
227
228     // We do our own error handling, so we don't want a load failure to trigger
229     // a dialog.
230     installer->set_be_noisy_on_failure(false);
231
232     installer->LoadFromZipFile(file_path);
233   } else {
234     scoped_refptr<UnpackedInstaller> installer = UnpackedInstaller::Create(
235         ExtensionSystem::Get(profile_)->extension_service());
236
237     // We do our own error handling, so we don't want a load failure to trigger
238     // a dialog.
239     installer->set_be_noisy_on_failure(false);
240
241     installer->Load(file_path);
242   }
243 }
244
245 void ExtensionLoaderHandler::OnLoadFailure(
246     content::BrowserContext* browser_context,
247     const base::FilePath& file_path,
248     const std::string& error) {
249   // Only show errors from our browser context.
250   if (web_ui()->GetWebContents()->GetBrowserContext() != browser_context)
251     return;
252
253   size_t line = 0u;
254   size_t column = 0u;
255   std::string regex =
256       base::StringPrintf("%s  Line: (\\d+), column: (\\d+), .*",
257                          manifest_errors::kManifestParseError);
258   // If this was a JSON parse error, we can highlight the exact line with the
259   // error. Otherwise, we should still display the manifest (for consistency,
260   // reference, and so that if we ever make this really fancy and add an editor,
261   // it's ready).
262   //
263   // This regex call can fail, but if it does, we just don't highlight anything.
264   re2::RE2::FullMatch(error, regex, &line, &column);
265
266   // This will read the manifest and call AddFailure with the read manifest
267   // contents.
268   base::PostTaskAndReplyWithResult(
269       content::BrowserThread::GetBlockingPool(),
270       FROM_HERE,
271       base::Bind(&ReadFileToString, file_path.Append(kManifestFilename)),
272       base::Bind(&ExtensionLoaderHandler::AddFailure,
273                  weak_ptr_factory_.GetWeakPtr(),
274                  file_path,
275                  error,
276                  line));
277 }
278
279 void ExtensionLoaderHandler::DidStartNavigationToPendingEntry(
280     const GURL& url,
281     content::NavigationController::ReloadType reload_type) {
282   // In the event of a page reload, we ensure that the frontend is not notified
283   // until the UI finishes loading, so we set |ui_ready_| to false. This is
284   // balanced in HandleDisplayFailures, which is called when the frontend is
285   // ready to receive failure notifications.
286   if (reload_type != content::NavigationController::NO_RELOAD)
287     ui_ready_ = false;
288 }
289
290 void ExtensionLoaderHandler::AddFailure(
291     const base::FilePath& file_path,
292     const std::string& error,
293     size_t line_number,
294     const std::string& manifest) {
295   failed_paths_.push_back(file_path);
296   base::FilePath prettified_path = path_util::PrettifyPath(file_path);
297
298   scoped_ptr<base::DictionaryValue> manifest_value(new base::DictionaryValue());
299   SourceHighlighter highlighter(manifest, line_number);
300   // If the line number is 0, this highlights no regions, but still adds the
301   // full manifest.
302   highlighter.SetHighlightedRegions(manifest_value.get());
303
304   scoped_ptr<base::DictionaryValue> failure(new base::DictionaryValue());
305   failure->Set("path",
306                new base::StringValue(prettified_path.LossyDisplayName()));
307   failure->Set("error", new base::StringValue(base::UTF8ToUTF16(error)));
308   failure->Set("manifest", manifest_value.release());
309   failures_.Append(failure.release());
310
311   // Only notify the frontend if the frontend UI is ready.
312   if (ui_ready_)
313     NotifyFrontendOfFailure();
314 }
315
316 void ExtensionLoaderHandler::NotifyFrontendOfFailure() {
317   web_ui()->CallJavascriptFunction(
318       "extensions.ExtensionLoader.notifyLoadFailed",
319       failures_);
320   failures_.Clear();
321 }
322
323 }  // namespace extensions