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