Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / extension_web_ui.cc
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.
4
5 #include "chrome/browser/extensions/extension_web_ui.h"
6
7 #include <set>
8 #include <vector>
9
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"
41
42 using content::WebContents;
43 using extensions::Extension;
44 using extensions::URLOverrides;
45
46 namespace {
47
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;
51
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;
58     }
59
60     if (seen_values.find(value) == seen_values.end())
61       seen_values.insert(value);
62     else
63       list->Remove(i, NULL);
64   }
65 }
66
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(
70     const std::string& page, Profile* profile, WebContents* web_contents) {
71   if (Profile::FromBrowserContext(web_contents->GetBrowserContext()) != profile)
72     return;
73
74   GURL url = web_contents->GetURL();
75   if (!url.SchemeIs(chrome::kChromeUIScheme) || url.host() != page)
76     return;
77
78   // Don't use Reload() since |url| isn't the same as the internal URL that
79   // NavigationController has.
80   web_contents->GetController().LoadURL(
81       url, content::Referrer(url, blink::WebReferrerPolicyDefault),
82       content::PAGE_TRANSITION_RELOAD, std::string());
83 }
84
85 // Run favicon callbck with image result. If no favicon was available then
86 // |image| will be empty.
87 void RunFaviconCallbackAsync(
88     const FaviconService::FaviconResultsCallback& callback,
89     const gfx::Image& image) {
90   std::vector<chrome::FaviconBitmapResult>* favicon_bitmap_results =
91       new std::vector<chrome::FaviconBitmapResult>();
92
93   const std::vector<gfx::ImageSkiaRep>& image_reps =
94       image.AsImageSkia().image_reps();
95   for (size_t i = 0; i < image_reps.size(); ++i) {
96     const gfx::ImageSkiaRep& image_rep = image_reps[i];
97     scoped_refptr<base::RefCountedBytes> bitmap_data(
98         new base::RefCountedBytes());
99     if (gfx::PNGCodec::EncodeBGRASkBitmap(image_rep.sk_bitmap(),
100                                           false,
101                                           &bitmap_data->data())) {
102       chrome::FaviconBitmapResult bitmap_result;
103       bitmap_result.bitmap_data = bitmap_data;
104       bitmap_result.pixel_size = gfx::Size(image_rep.pixel_width(),
105                                             image_rep.pixel_height());
106       // Leave |bitmap_result|'s icon URL as the default of GURL().
107       bitmap_result.icon_type = chrome::FAVICON;
108
109       favicon_bitmap_results->push_back(bitmap_result);
110     } else {
111       NOTREACHED() << "Could not encode extension favicon";
112     }
113   }
114
115   base::MessageLoopProxy::current()->PostTask(
116       FROM_HERE,
117       base::Bind(&FaviconService::FaviconResultsCallbackRunner,
118                  callback,
119                  base::Owned(favicon_bitmap_results)));
120 }
121
122 }  // namespace
123
124 const char ExtensionWebUI::kExtensionURLOverrides[] =
125     "extensions.chrome_url_overrides";
126
127 ExtensionWebUI::ExtensionWebUI(content::WebUI* web_ui, const GURL& url)
128     : WebUIController(web_ui),
129       url_(url) {
130   Profile* profile = Profile::FromWebUI(web_ui);
131   ExtensionService* service = profile->GetExtensionService();
132   const Extension* extension =
133       service->extensions()->GetExtensionOrAppByURL(url);
134   DCHECK(extension);
135
136   // The base class defaults to enabling WebUI bindings, but we don't need
137   // those (this is also reflected in ChromeWebUIControllerFactory::
138   // UseWebUIBindingsForURL).
139   int bindings = 0;
140   web_ui->SetBindings(bindings);
141
142   // Hack: A few things we specialize just for the bookmark manager.
143   if (extension->id() == extension_misc::kBookmarkManagerId) {
144     bookmark_manager_private_event_router_.reset(
145         new extensions::BookmarkManagerPrivateEventRouter(
146             profile, web_ui->GetWebContents()));
147
148     web_ui->SetLinkTransitionType(content::PAGE_TRANSITION_AUTO_BOOKMARK);
149   }
150 }
151
152 ExtensionWebUI::~ExtensionWebUI() {}
153
154 extensions::BookmarkManagerPrivateEventRouter*
155 ExtensionWebUI::bookmark_manager_private_event_router() {
156   return bookmark_manager_private_event_router_.get();
157 }
158
159 ////////////////////////////////////////////////////////////////////////////////
160 // chrome:// URL overrides
161
162 // static
163 void ExtensionWebUI::RegisterProfilePrefs(
164     user_prefs::PrefRegistrySyncable* registry) {
165   registry->RegisterDictionaryPref(
166       kExtensionURLOverrides,
167       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
168 }
169
170 // static
171 bool ExtensionWebUI::HandleChromeURLOverride(
172     GURL* url, content::BrowserContext* browser_context) {
173   if (!url->SchemeIs(chrome::kChromeUIScheme))
174     return false;
175
176   Profile* profile = Profile::FromBrowserContext(browser_context);
177   const base::DictionaryValue* overrides =
178       profile->GetPrefs()->GetDictionary(kExtensionURLOverrides);
179   std::string page = url->host();
180   const base::ListValue* url_list = NULL;
181   if (!overrides || !overrides->GetList(page, &url_list))
182     return false;
183
184   ExtensionService* service = profile->GetExtensionService();
185
186   size_t i = 0;
187   while (i < url_list->GetSize()) {
188     const base::Value* val = NULL;
189     url_list->Get(i, &val);
190
191     // Verify that the override value is good.  If not, unregister it and find
192     // the next one.
193     std::string override;
194     if (!val->GetAsString(&override)) {
195       NOTREACHED();
196       UnregisterChromeURLOverride(page, profile, val);
197       continue;
198     }
199
200     if (!url->query().empty())
201       override += "?" + url->query();
202     if (!url->ref().empty())
203       override += "#" + url->ref();
204     GURL extension_url(override);
205     if (!extension_url.is_valid()) {
206       NOTREACHED();
207       UnregisterChromeURLOverride(page, profile, val);
208       continue;
209     }
210
211     // Verify that the extension that's being referred to actually exists.
212     const Extension* extension =
213         service->extensions()->GetByID(extension_url.host());
214     if (!extension) {
215       // This can currently happen if you use --load-extension one run, and
216       // then don't use it the next.  It could also happen if an extension
217       // were deleted directly from the filesystem, etc.
218       LOG(WARNING) << "chrome URL override present for non-existant extension";
219       UnregisterChromeURLOverride(page, profile, val);
220       continue;
221     }
222
223     // We can't handle chrome-extension URLs in incognito mode unless the
224     // extension uses split mode.
225     bool incognito_override_allowed =
226         extensions::IncognitoInfo::IsSplitMode(extension) &&
227         extensions::util::IsIncognitoEnabled(extension->id(), profile);
228     if (profile->IsOffTheRecord() && !incognito_override_allowed) {
229       ++i;
230       continue;
231     }
232
233     *url = extension_url;
234     return true;
235   }
236   return false;
237 }
238
239 // static
240 bool ExtensionWebUI::HandleChromeURLOverrideReverse(
241     GURL* url, content::BrowserContext* browser_context) {
242   Profile* profile = Profile::FromBrowserContext(browser_context);
243   const base::DictionaryValue* overrides =
244       profile->GetPrefs()->GetDictionary(kExtensionURLOverrides);
245   if (!overrides)
246     return false;
247
248   // Find the reverse mapping based on the given URL. For example this maps the
249   // internal URL
250   // chrome-extension://eemcgdkfndhakfknompkggombfjjjeno/main.html#1 to
251   // chrome://bookmarks/#1 for display in the omnibox.
252   for (base::DictionaryValue::Iterator it(*overrides); !it.IsAtEnd();
253        it.Advance()) {
254     const base::ListValue* url_list = NULL;
255     if (!it.value().GetAsList(&url_list))
256       continue;
257
258     for (base::ListValue::const_iterator it2 = url_list->begin();
259          it2 != url_list->end(); ++it2) {
260       std::string override;
261       if (!(*it2)->GetAsString(&override))
262         continue;
263       if (StartsWithASCII(url->spec(), override, true)) {
264         GURL original_url(chrome::kChromeUIScheme + std::string("://") +
265                           it.key() + url->spec().substr(override.length()));
266         *url = original_url;
267         return true;
268       }
269     }
270   }
271
272   return false;
273 }
274
275 // static
276 void ExtensionWebUI::RegisterChromeURLOverrides(
277     Profile* profile, const URLOverrides::URLOverrideMap& overrides) {
278   if (overrides.empty())
279     return;
280
281   PrefService* prefs = profile->GetPrefs();
282   DictionaryPrefUpdate update(prefs, kExtensionURLOverrides);
283   base::DictionaryValue* all_overrides = update.Get();
284
285   // For each override provided by the extension, add it to the front of
286   // the override list if it's not already in the list.
287   URLOverrides::URLOverrideMap::const_iterator iter = overrides.begin();
288   for (; iter != overrides.end(); ++iter) {
289     const std::string& key = iter->first;
290     base::ListValue* page_overrides = NULL;
291     if (!all_overrides->GetList(key, &page_overrides)) {
292       page_overrides = new base::ListValue();
293       all_overrides->Set(key, page_overrides);
294     } else {
295       CleanUpDuplicates(page_overrides);
296
297       // Verify that the override isn't already in the list.
298       base::ListValue::iterator i = page_overrides->begin();
299       for (; i != page_overrides->end(); ++i) {
300         std::string override_val;
301         if (!(*i)->GetAsString(&override_val)) {
302           NOTREACHED();
303           continue;
304         }
305         if (override_val == iter->second.spec())
306           break;
307       }
308       // This value is already in the list, leave it alone.
309       if (i != page_overrides->end())
310         continue;
311     }
312     // Insert the override at the front of the list.  Last registered override
313     // wins.
314     page_overrides->Insert(0, new base::StringValue(iter->second.spec()));
315   }
316 }
317
318 // static
319 void ExtensionWebUI::UnregisterAndReplaceOverride(const std::string& page,
320                                                   Profile* profile,
321                                                   base::ListValue* list,
322                                                   const base::Value* override) {
323   size_t index = 0;
324   bool found = list->Remove(*override, &index);
325   if (found && index == 0) {
326     // This is the active override, so we need to find all existing
327     // tabs for this override and get them to reload the original URL.
328     base::Callback<void(WebContents*)> callback =
329         base::Bind(&UnregisterAndReplaceOverrideForWebContents, page, profile);
330     extensions::ExtensionTabUtil::ForEachTab(callback);
331   }
332 }
333
334 // static
335 void ExtensionWebUI::UnregisterChromeURLOverride(const std::string& page,
336                                                  Profile* profile,
337                                                  const base::Value* override) {
338   if (!override)
339     return;
340   PrefService* prefs = profile->GetPrefs();
341   DictionaryPrefUpdate update(prefs, kExtensionURLOverrides);
342   base::DictionaryValue* all_overrides = update.Get();
343   base::ListValue* page_overrides = NULL;
344   if (!all_overrides->GetList(page, &page_overrides)) {
345     // If it's being unregistered, it should already be in the list.
346     NOTREACHED();
347     return;
348   } else {
349     UnregisterAndReplaceOverride(page, profile, page_overrides, override);
350   }
351 }
352
353 // static
354 void ExtensionWebUI::UnregisterChromeURLOverrides(
355     Profile* profile, const URLOverrides::URLOverrideMap& overrides) {
356   if (overrides.empty())
357     return;
358   PrefService* prefs = profile->GetPrefs();
359   DictionaryPrefUpdate update(prefs, kExtensionURLOverrides);
360   base::DictionaryValue* all_overrides = update.Get();
361   URLOverrides::URLOverrideMap::const_iterator iter = overrides.begin();
362   for (; iter != overrides.end(); ++iter) {
363     const std::string& page = iter->first;
364     base::ListValue* page_overrides = NULL;
365     if (!all_overrides->GetList(page, &page_overrides)) {
366       // If it's being unregistered, it should already be in the list.
367       NOTREACHED();
368       continue;
369     } else {
370       base::StringValue override(iter->second.spec());
371       UnregisterAndReplaceOverride(iter->first, profile,
372                                    page_overrides, &override);
373     }
374   }
375 }
376
377 // static
378 void ExtensionWebUI::GetFaviconForURL(
379     Profile* profile,
380     const GURL& page_url,
381     const FaviconService::FaviconResultsCallback& callback) {
382   // Even when the extensions service is enabled by default, it's still
383   // disabled in incognito mode.
384   ExtensionService* service = profile->GetExtensionService();
385   if (!service) {
386     RunFaviconCallbackAsync(callback, gfx::Image());
387     return;
388   }
389   const Extension* extension = service->extensions()->GetByID(page_url.host());
390   if (!extension) {
391     RunFaviconCallbackAsync(callback, gfx::Image());
392     return;
393   }
394
395   // Fetch resources for all supported scale factors for which there are
396   // resources. Load image reps for all supported scale factors (in addition to
397   // 1x) immediately instead of in an as needed fashion to be consistent with
398   // how favicons are requested for chrome:// and page URLs.
399   const std::vector<ui::ScaleFactor>& scale_factors =
400       FaviconUtil::GetFaviconScaleFactors();
401   std::vector<extensions::ImageLoader::ImageRepresentation> info_list;
402   for (size_t i = 0; i < scale_factors.size(); ++i) {
403     float scale = ui::GetImageScale(scale_factors[i]);
404     int pixel_size = static_cast<int>(gfx::kFaviconSize * scale);
405     extensions::ExtensionResource icon_resource =
406         extensions::IconsInfo::GetIconResource(extension,
407                                                pixel_size,
408                                                ExtensionIconSet::MATCH_BIGGER);
409
410     info_list.push_back(
411         extensions::ImageLoader::ImageRepresentation(
412             icon_resource,
413             extensions::ImageLoader::ImageRepresentation::ALWAYS_RESIZE,
414             gfx::Size(pixel_size, pixel_size),
415             scale_factors[i]));
416   }
417
418   // LoadImagesAsync actually can run callback synchronously. We want to force
419   // async.
420   extensions::ImageLoader::Get(profile)->LoadImagesAsync(
421       extension, info_list, base::Bind(&RunFaviconCallbackAsync, callback));
422 }