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.
5 #include "chrome/browser/ui/webui/extensions/extension_loader_handler.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/profiles/profile.h"
18 #include "chrome/grit/generated_resources.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/web_contents.h"
21 #include "content/public/browser/web_ui.h"
22 #include "content/public/browser/web_ui_data_source.h"
23 #include "extensions/browser/extension_system.h"
24 #include "extensions/browser/file_highlighter.h"
25 #include "extensions/common/constants.h"
26 #include "extensions/common/extension.h"
27 #include "extensions/common/manifest_constants.h"
28 #include "third_party/re2/re2/re2.h"
29 #include "ui/base/l10n/l10n_util.h"
31 namespace extensions
{
35 // Read a file to a string and return.
36 std::string
ReadFileToString(const base::FilePath
& path
) {
38 // This call can fail, but it doesn't matter for our purposes. If it fails,
39 // we simply return an empty string for the manifest, and ignore it.
40 base::ReadFileToString(path
, &data
);
46 ExtensionLoaderHandler::ExtensionLoaderHandler(Profile
* profile
)
48 extension_error_reporter_observer_(this),
50 weak_ptr_factory_(this) {
52 extension_error_reporter_observer_
.Add(ExtensionErrorReporter::GetInstance());
55 ExtensionLoaderHandler::~ExtensionLoaderHandler() {
58 void ExtensionLoaderHandler::GetLocalizedValues(
59 content::WebUIDataSource
* source
) {
61 "extensionLoadErrorHeading",
62 l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_ERROR_HEADING
));
64 "extensionLoadErrorMessage",
65 l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_ERROR_MESSAGE
));
67 "extensionLoadErrorRetry",
68 l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_ERROR_RETRY
));
70 "extensionLoadErrorGiveUp",
71 l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_ERROR_GIVE_UP
));
73 "extensionLoadCouldNotLoadManifest",
74 l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_COULD_NOT_LOAD_MANIFEST
));
76 "extensionLoadAdditionalFailures",
77 l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOAD_ADDITIONAL_FAILURES
));
80 void ExtensionLoaderHandler::RegisterMessages() {
81 // We observe WebContents in order to detect page refreshes, since notifying
82 // the frontend of load failures must be delayed until the page finishes
83 // loading. We never call Observe(NULL) because this object is constructed
84 // on page load and persists between refreshes.
85 content::WebContentsObserver::Observe(web_ui()->GetWebContents());
87 web_ui()->RegisterMessageCallback(
88 "extensionLoaderRetry",
89 base::Bind(&ExtensionLoaderHandler::HandleRetry
,
90 weak_ptr_factory_
.GetWeakPtr()));
91 web_ui()->RegisterMessageCallback(
92 "extensionLoaderIgnoreFailure",
93 base::Bind(&ExtensionLoaderHandler::HandleIgnoreFailure
,
94 weak_ptr_factory_
.GetWeakPtr()));
95 web_ui()->RegisterMessageCallback(
96 "extensionLoaderDisplayFailures",
97 base::Bind(&ExtensionLoaderHandler::HandleDisplayFailures
,
98 weak_ptr_factory_
.GetWeakPtr()));
101 void ExtensionLoaderHandler::HandleRetry(const base::ListValue
* args
) {
102 DCHECK(args
->empty());
103 const base::FilePath file_path
= failed_paths_
.back();
104 failed_paths_
.pop_back();
105 LoadUnpackedExtension(file_path
);
108 void ExtensionLoaderHandler::HandleIgnoreFailure(const base::ListValue
* args
) {
109 DCHECK(args
->empty());
110 failed_paths_
.pop_back();
113 void ExtensionLoaderHandler::HandleDisplayFailures(
114 const base::ListValue
* args
) {
115 DCHECK(args
->empty());
118 // Notify the frontend of any load failures that were triggered while the
119 // chrome://extensions page was loading.
120 if (!failures_
.empty())
121 NotifyFrontendOfFailure();
124 void ExtensionLoaderHandler::LoadUnpackedExtension(
125 const base::FilePath
& file_path
) {
126 scoped_refptr
<UnpackedInstaller
> installer
= UnpackedInstaller::Create(
127 ExtensionSystem::Get(profile_
)->extension_service());
129 // We do our own error handling, so we don't want a load failure to trigger
131 installer
->set_be_noisy_on_failure(false);
133 installer
->Load(file_path
);
136 void ExtensionLoaderHandler::OnLoadFailure(
137 content::BrowserContext
* browser_context
,
138 const base::FilePath
& file_path
,
139 const std::string
& error
) {
140 // Only show errors from our browser context.
141 if (web_ui()->GetWebContents()->GetBrowserContext() != browser_context
)
147 base::StringPrintf("%s Line: (\\d+), column: (\\d+), .*",
148 manifest_errors::kManifestParseError
);
149 // If this was a JSON parse error, we can highlight the exact line with the
150 // error. Otherwise, we should still display the manifest (for consistency,
151 // reference, and so that if we ever make this really fancy and add an editor,
154 // This regex call can fail, but if it does, we just don't highlight anything.
155 re2::RE2::FullMatch(error
, regex
, &line
, &column
);
157 // This will read the manifest and call AddFailure with the read manifest
159 base::PostTaskAndReplyWithResult(
160 content::BrowserThread::GetBlockingPool(),
162 base::Bind(&ReadFileToString
, file_path
.Append(kManifestFilename
)),
163 base::Bind(&ExtensionLoaderHandler::AddFailure
,
164 weak_ptr_factory_
.GetWeakPtr(),
170 void ExtensionLoaderHandler::DidStartNavigationToPendingEntry(
172 content::NavigationController::ReloadType reload_type
) {
173 // In the event of a page reload, we ensure that the frontend is not notified
174 // until the UI finishes loading, so we set |ui_ready_| to false. This is
175 // balanced in HandleDisplayFailures, which is called when the frontend is
176 // ready to receive failure notifications.
177 if (reload_type
!= content::NavigationController::NO_RELOAD
)
181 void ExtensionLoaderHandler::AddFailure(
182 const base::FilePath
& file_path
,
183 const std::string
& error
,
185 const std::string
& manifest
) {
186 failed_paths_
.push_back(file_path
);
187 base::FilePath prettified_path
= path_util::PrettifyPath(file_path
);
189 scoped_ptr
<base::DictionaryValue
> manifest_value(new base::DictionaryValue());
190 SourceHighlighter
highlighter(manifest
, line_number
);
191 // If the line number is 0, this highlights no regions, but still adds the
193 highlighter
.SetHighlightedRegions(manifest_value
.get());
195 scoped_ptr
<base::DictionaryValue
> failure(new base::DictionaryValue());
197 new base::StringValue(prettified_path
.LossyDisplayName()));
198 failure
->Set("error", new base::StringValue(base::UTF8ToUTF16(error
)));
199 failure
->Set("manifest", manifest_value
.release());
200 failures_
.Append(failure
.release());
202 // Only notify the frontend if the frontend UI is ready.
204 NotifyFrontendOfFailure();
207 void ExtensionLoaderHandler::NotifyFrontendOfFailure() {
208 web_ui()->CallJavascriptFunction(
209 "extensions.ExtensionLoader.notifyLoadFailed",
214 } // namespace extensions