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