NaCl: Update revision in DEPS, r12770 -> r12773
[chromium-blink-merge.git] / chrome / browser / extensions / extension_web_ui.cc
blob982de267700d9dca030ff57d07288f7ec7339cb7
1 // Copyright (c) 2012 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/extensions/extension_web_ui.h"
7 #include <set>
8 #include <vector>
10 #include "base/command_line.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/prefs/scoped_user_pref_update.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.h"
16 #include "chrome/browser/extensions/extension_service.h"
17 #include "chrome/browser/extensions/extension_tab_util.h"
18 #include "chrome/browser/extensions/extension_util.h"
19 #include "chrome/browser/extensions/image_loader.h"
20 #include "chrome/browser/favicon/favicon_util.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/common/chrome_switches.h"
23 #include "chrome/common/extensions/extension_constants.h"
24 #include "chrome/common/extensions/extension_icon_set.h"
25 #include "chrome/common/extensions/manifest_handlers/icons_handler.h"
26 #include "chrome/common/url_constants.h"
27 #include "components/user_prefs/pref_registry_syncable.h"
28 #include "content/public/browser/navigation_controller.h"
29 #include "content/public/browser/web_contents.h"
30 #include "content/public/browser/web_ui.h"
31 #include "content/public/common/bindings_policy.h"
32 #include "content/public/common/page_transition_types.h"
33 #include "extensions/common/extension.h"
34 #include "extensions/common/extension_resource.h"
35 #include "extensions/common/manifest_handlers/incognito_info.h"
36 #include "net/base/file_stream.h"
37 #include "third_party/skia/include/core/SkBitmap.h"
38 #include "ui/gfx/codec/png_codec.h"
39 #include "ui/gfx/favicon_size.h"
40 #include "ui/gfx/image/image_skia.h"
42 using content::WebContents;
43 using extensions::Extension;
44 using extensions::URLOverrides;
46 namespace {
48 // De-dupes the items in |list|. Assumes the values are strings.
49 void CleanUpDuplicates(base::ListValue* list) {
50 std::set<std::string> seen_values;
52 // Loop backwards as we may be removing items.
53 for (size_t i = list->GetSize() - 1; (i + 1) > 0; --i) {
54 std::string value;
55 if (!list->GetString(i, &value)) {
56 NOTREACHED();
57 continue;
60 if (seen_values.find(value) == seen_values.end())
61 seen_values.insert(value);
62 else
63 list->Remove(i, NULL);
67 // Reloads the page in |web_contents| if it uses the same profile as |profile|
68 // and if the current URL is a chrome URL.
69 void UnregisterAndReplaceOverrideForWebContents(const std::string& page,
70 Profile* profile,
71 WebContents* web_contents) {
72 if (Profile::FromBrowserContext(web_contents->GetBrowserContext()) != profile)
73 return;
75 GURL url = web_contents->GetURL();
76 if (!url.SchemeIs(content::kChromeUIScheme) || url.host() != page)
77 return;
79 // Don't use Reload() since |url| isn't the same as the internal URL that
80 // NavigationController has.
81 web_contents->GetController().LoadURL(
82 url, content::Referrer(url, blink::WebReferrerPolicyDefault),
83 content::PAGE_TRANSITION_RELOAD, std::string());
86 // Run favicon callbck with image result. If no favicon was available then
87 // |image| will be empty.
88 void RunFaviconCallbackAsync(
89 const FaviconService::FaviconResultsCallback& callback,
90 const gfx::Image& image) {
91 std::vector<chrome::FaviconBitmapResult>* favicon_bitmap_results =
92 new std::vector<chrome::FaviconBitmapResult>();
94 const std::vector<gfx::ImageSkiaRep>& image_reps =
95 image.AsImageSkia().image_reps();
96 for (size_t i = 0; i < image_reps.size(); ++i) {
97 const gfx::ImageSkiaRep& image_rep = image_reps[i];
98 scoped_refptr<base::RefCountedBytes> bitmap_data(
99 new base::RefCountedBytes());
100 if (gfx::PNGCodec::EncodeBGRASkBitmap(image_rep.sk_bitmap(),
101 false,
102 &bitmap_data->data())) {
103 chrome::FaviconBitmapResult bitmap_result;
104 bitmap_result.bitmap_data = bitmap_data;
105 bitmap_result.pixel_size = gfx::Size(image_rep.pixel_width(),
106 image_rep.pixel_height());
107 // Leave |bitmap_result|'s icon URL as the default of GURL().
108 bitmap_result.icon_type = chrome::FAVICON;
110 favicon_bitmap_results->push_back(bitmap_result);
111 } else {
112 NOTREACHED() << "Could not encode extension favicon";
116 base::MessageLoopProxy::current()->PostTask(
117 FROM_HERE,
118 base::Bind(&FaviconService::FaviconResultsCallbackRunner,
119 callback,
120 base::Owned(favicon_bitmap_results)));
123 } // namespace
125 const char ExtensionWebUI::kExtensionURLOverrides[] =
126 "extensions.chrome_url_overrides";
128 ExtensionWebUI::ExtensionWebUI(content::WebUI* web_ui, const GURL& url)
129 : WebUIController(web_ui),
130 url_(url) {
131 Profile* profile = Profile::FromWebUI(web_ui);
132 ExtensionService* service = profile->GetExtensionService();
133 const Extension* extension =
134 service->extensions()->GetExtensionOrAppByURL(url);
135 DCHECK(extension);
137 // The base class defaults to enabling WebUI bindings, but we don't need
138 // those (this is also reflected in ChromeWebUIControllerFactory::
139 // UseWebUIBindingsForURL).
140 int bindings = 0;
141 web_ui->SetBindings(bindings);
143 // Hack: A few things we specialize just for the bookmark manager.
144 if (extension->id() == extension_misc::kBookmarkManagerId) {
145 bookmark_manager_private_event_router_.reset(
146 new extensions::BookmarkManagerPrivateEventRouter(
147 profile, web_ui->GetWebContents()));
149 web_ui->SetLinkTransitionType(content::PAGE_TRANSITION_AUTO_BOOKMARK);
153 ExtensionWebUI::~ExtensionWebUI() {}
155 extensions::BookmarkManagerPrivateEventRouter*
156 ExtensionWebUI::bookmark_manager_private_event_router() {
157 return bookmark_manager_private_event_router_.get();
160 ////////////////////////////////////////////////////////////////////////////////
161 // chrome:// URL overrides
163 // static
164 void ExtensionWebUI::RegisterProfilePrefs(
165 user_prefs::PrefRegistrySyncable* registry) {
166 registry->RegisterDictionaryPref(
167 kExtensionURLOverrides,
168 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
171 // static
172 bool ExtensionWebUI::HandleChromeURLOverride(
173 GURL* url,
174 content::BrowserContext* browser_context) {
175 if (!url->SchemeIs(content::kChromeUIScheme))
176 return false;
178 Profile* profile = Profile::FromBrowserContext(browser_context);
179 const base::DictionaryValue* overrides =
180 profile->GetPrefs()->GetDictionary(kExtensionURLOverrides);
181 std::string page = url->host();
182 const base::ListValue* url_list = NULL;
183 if (!overrides || !overrides->GetList(page, &url_list))
184 return false;
186 ExtensionService* service = profile->GetExtensionService();
188 size_t i = 0;
189 while (i < url_list->GetSize()) {
190 const base::Value* val = NULL;
191 url_list->Get(i, &val);
193 // Verify that the override value is good. If not, unregister it and find
194 // the next one.
195 std::string override;
196 if (!val->GetAsString(&override)) {
197 NOTREACHED();
198 UnregisterChromeURLOverride(page, profile, val);
199 continue;
202 if (!url->query().empty())
203 override += "?" + url->query();
204 if (!url->ref().empty())
205 override += "#" + url->ref();
206 GURL extension_url(override);
207 if (!extension_url.is_valid()) {
208 NOTREACHED();
209 UnregisterChromeURLOverride(page, profile, val);
210 continue;
213 // Verify that the extension that's being referred to actually exists.
214 const Extension* extension =
215 service->extensions()->GetByID(extension_url.host());
216 if (!extension) {
217 // This can currently happen if you use --load-extension one run, and
218 // then don't use it the next. It could also happen if an extension
219 // were deleted directly from the filesystem, etc.
220 LOG(WARNING) << "chrome URL override present for non-existant extension";
221 UnregisterChromeURLOverride(page, profile, val);
222 continue;
225 // We can't handle chrome-extension URLs in incognito mode unless the
226 // extension uses split mode.
227 bool incognito_override_allowed =
228 extensions::IncognitoInfo::IsSplitMode(extension) &&
229 extensions::util::IsIncognitoEnabled(extension->id(), profile);
230 if (profile->IsOffTheRecord() && !incognito_override_allowed) {
231 ++i;
232 continue;
235 *url = extension_url;
236 return true;
238 return false;
241 // static
242 bool ExtensionWebUI::HandleChromeURLOverrideReverse(
243 GURL* url, content::BrowserContext* browser_context) {
244 Profile* profile = Profile::FromBrowserContext(browser_context);
245 const base::DictionaryValue* overrides =
246 profile->GetPrefs()->GetDictionary(kExtensionURLOverrides);
247 if (!overrides)
248 return false;
250 // Find the reverse mapping based on the given URL. For example this maps the
251 // internal URL
252 // chrome-extension://eemcgdkfndhakfknompkggombfjjjeno/main.html#1 to
253 // chrome://bookmarks/#1 for display in the omnibox.
254 for (base::DictionaryValue::Iterator it(*overrides); !it.IsAtEnd();
255 it.Advance()) {
256 const base::ListValue* url_list = NULL;
257 if (!it.value().GetAsList(&url_list))
258 continue;
260 for (base::ListValue::const_iterator it2 = url_list->begin();
261 it2 != url_list->end(); ++it2) {
262 std::string override;
263 if (!(*it2)->GetAsString(&override))
264 continue;
265 if (StartsWithASCII(url->spec(), override, true)) {
266 GURL original_url(content::kChromeUIScheme + std::string("://") +
267 it.key() + url->spec().substr(override.length()));
268 *url = original_url;
269 return true;
274 return false;
277 // static
278 void ExtensionWebUI::RegisterChromeURLOverrides(
279 Profile* profile, const URLOverrides::URLOverrideMap& overrides) {
280 if (overrides.empty())
281 return;
283 PrefService* prefs = profile->GetPrefs();
284 DictionaryPrefUpdate update(prefs, kExtensionURLOverrides);
285 base::DictionaryValue* all_overrides = update.Get();
287 // For each override provided by the extension, add it to the front of
288 // the override list if it's not already in the list.
289 URLOverrides::URLOverrideMap::const_iterator iter = overrides.begin();
290 for (; iter != overrides.end(); ++iter) {
291 const std::string& key = iter->first;
292 base::ListValue* page_overrides = NULL;
293 if (!all_overrides->GetList(key, &page_overrides)) {
294 page_overrides = new base::ListValue();
295 all_overrides->Set(key, page_overrides);
296 } else {
297 CleanUpDuplicates(page_overrides);
299 // Verify that the override isn't already in the list.
300 base::ListValue::iterator i = page_overrides->begin();
301 for (; i != page_overrides->end(); ++i) {
302 std::string override_val;
303 if (!(*i)->GetAsString(&override_val)) {
304 NOTREACHED();
305 continue;
307 if (override_val == iter->second.spec())
308 break;
310 // This value is already in the list, leave it alone.
311 if (i != page_overrides->end())
312 continue;
314 // Insert the override at the front of the list. Last registered override
315 // wins.
316 page_overrides->Insert(0, new base::StringValue(iter->second.spec()));
320 // static
321 void ExtensionWebUI::UnregisterAndReplaceOverride(const std::string& page,
322 Profile* profile,
323 base::ListValue* list,
324 const base::Value* override) {
325 size_t index = 0;
326 bool found = list->Remove(*override, &index);
327 if (found && index == 0) {
328 // This is the active override, so we need to find all existing
329 // tabs for this override and get them to reload the original URL.
330 base::Callback<void(WebContents*)> callback =
331 base::Bind(&UnregisterAndReplaceOverrideForWebContents, page, profile);
332 extensions::ExtensionTabUtil::ForEachTab(callback);
336 // static
337 void ExtensionWebUI::UnregisterChromeURLOverride(const std::string& page,
338 Profile* profile,
339 const base::Value* override) {
340 if (!override)
341 return;
342 PrefService* prefs = profile->GetPrefs();
343 DictionaryPrefUpdate update(prefs, kExtensionURLOverrides);
344 base::DictionaryValue* all_overrides = update.Get();
345 base::ListValue* page_overrides = NULL;
346 if (!all_overrides->GetList(page, &page_overrides)) {
347 // If it's being unregistered, it should already be in the list.
348 NOTREACHED();
349 return;
350 } else {
351 UnregisterAndReplaceOverride(page, profile, page_overrides, override);
355 // static
356 void ExtensionWebUI::UnregisterChromeURLOverrides(
357 Profile* profile, const URLOverrides::URLOverrideMap& overrides) {
358 if (overrides.empty())
359 return;
360 PrefService* prefs = profile->GetPrefs();
361 DictionaryPrefUpdate update(prefs, kExtensionURLOverrides);
362 base::DictionaryValue* all_overrides = update.Get();
363 URLOverrides::URLOverrideMap::const_iterator iter = overrides.begin();
364 for (; iter != overrides.end(); ++iter) {
365 const std::string& page = iter->first;
366 base::ListValue* page_overrides = NULL;
367 if (!all_overrides->GetList(page, &page_overrides)) {
368 // If it's being unregistered, it should already be in the list.
369 NOTREACHED();
370 continue;
371 } else {
372 base::StringValue override(iter->second.spec());
373 UnregisterAndReplaceOverride(iter->first, profile,
374 page_overrides, &override);
379 // static
380 void ExtensionWebUI::GetFaviconForURL(
381 Profile* profile,
382 const GURL& page_url,
383 const FaviconService::FaviconResultsCallback& callback) {
384 // Even when the extensions service is enabled by default, it's still
385 // disabled in incognito mode.
386 ExtensionService* service = profile->GetExtensionService();
387 if (!service) {
388 RunFaviconCallbackAsync(callback, gfx::Image());
389 return;
391 const Extension* extension = service->extensions()->GetByID(page_url.host());
392 if (!extension) {
393 RunFaviconCallbackAsync(callback, gfx::Image());
394 return;
397 // Fetch resources for all supported scale factors for which there are
398 // resources. Load image reps for all supported scale factors (in addition to
399 // 1x) immediately instead of in an as needed fashion to be consistent with
400 // how favicons are requested for chrome:// and page URLs.
401 const std::vector<ui::ScaleFactor>& scale_factors =
402 FaviconUtil::GetFaviconScaleFactors();
403 std::vector<extensions::ImageLoader::ImageRepresentation> info_list;
404 for (size_t i = 0; i < scale_factors.size(); ++i) {
405 float scale = ui::GetImageScale(scale_factors[i]);
406 int pixel_size = static_cast<int>(gfx::kFaviconSize * scale);
407 extensions::ExtensionResource icon_resource =
408 extensions::IconsInfo::GetIconResource(extension,
409 pixel_size,
410 ExtensionIconSet::MATCH_BIGGER);
412 info_list.push_back(
413 extensions::ImageLoader::ImageRepresentation(
414 icon_resource,
415 extensions::ImageLoader::ImageRepresentation::ALWAYS_RESIZE,
416 gfx::Size(pixel_size, pixel_size),
417 scale_factors[i]));
420 // LoadImagesAsync actually can run callback synchronously. We want to force
421 // async.
422 extensions::ImageLoader::Get(profile)->LoadImagesAsync(
423 extension, info_list, base::Bind(&RunFaviconCallbackAsync, callback));