172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file.
4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/extensions/extension_web_ui.h"
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <set>
821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include <vector>
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/string_util.h"
113345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/utf_string_conversions.h"
12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/extensions/extension_bookmark_manager_api.h"
1321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/extensions/extension_service.h"
14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/extensions/image_loading_tracker.h"
153345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "chrome/browser/prefs/pref_service.h"
16ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/prefs/scoped_user_pref_update.h"
1721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/profiles/profile.h"
184a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "chrome/browser/ui/browser.h"
19ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/ui/browser_list.h"
20ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
213345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "chrome/common/chrome_switches.h"
224a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "chrome/common/extensions/extension.h"
23ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/common/extensions/extension_constants.h"
243345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "chrome/common/extensions/extension_icon_set.h"
25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/extensions/extension_resource.h"
26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/url_constants.h"
27dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/renderer_host/render_widget_host_view.h"
28dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/tab_contents/tab_contents.h"
29ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/bindings_policy.h"
30ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/page_transition_types.h"
31ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "net/base/file_stream.h"
32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "third_party/skia/include/core/SkBitmap.h"
3372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/codec/png_codec.h"
3472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/favicon_size.h"
35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace {
37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// De-dupes the items in |list|. Assumes the values are strings.
39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CleanUpDuplicates(ListValue* list) {
40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::set<std::string> seen_values;
41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Loop backwards as we may be removing items.
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (size_t i = list->GetSize() - 1; (i + 1) > 0; --i) {
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    std::string value;
45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!list->GetString(i, &value)) {
46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NOTREACHED();
47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      continue;
48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (seen_values.find(value) == seen_values.end())
51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      seen_values.insert(value);
52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    else
53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      list->Remove(i, NULL);
54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Helper class that is used to track the loading of the favicon of an
58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// extension.
5972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenclass ExtensionWebUIImageLoadingTracker : public ImageLoadingTracker::Observer {
60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public:
6172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  ExtensionWebUIImageLoadingTracker(Profile* profile,
62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                    FaviconService::GetFaviconRequest* request,
63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                    const GURL& page_url)
64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      : ALLOW_THIS_IN_INITIALIZER_LIST(tracker_(this)),
65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        request_(request),
66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        extension_(NULL) {
67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Even when the extensions service is enabled by default, it's still
68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // disabled in incognito mode.
6921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    ExtensionService* service = profile->GetExtensionService();
70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (service)
71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      extension_ = service->GetExtensionByURL(page_url);
72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void Init() {
75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (extension_) {
76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      ExtensionResource icon_resource =
773345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick          extension_->GetIconResource(Extension::EXTENSION_ICON_BITTY,
783345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                                      ExtensionIconSet::MATCH_EXACTLY);
79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      tracker_.LoadImage(extension_, icon_resource,
81ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                         gfx::Size(kFaviconSize, kFaviconSize),
82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                         ImageLoadingTracker::DONT_CACHE);
83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else {
84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      ForwardResult(NULL);
85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
88ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  virtual void OnImageLoaded(SkBitmap* image, const ExtensionResource& resource,
89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                             int index) {
90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (image) {
91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      std::vector<unsigned char> image_data;
92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (!gfx::PNGCodec::EncodeBGRASkBitmap(*image, false, &image_data)) {
93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        NOTREACHED() << "Could not encode extension favicon";
94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      ForwardResult(RefCountedBytes::TakeVector(&image_data));
96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else {
97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      ForwardResult(NULL);
98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch private:
10272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  ~ExtensionWebUIImageLoadingTracker() {}
103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Forwards the result on the request. If no favicon was available then
105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // |icon_data| may be backed by NULL. Once the result has been forwarded the
106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // instance is deleted.
107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void ForwardResult(scoped_refptr<RefCountedMemory> icon_data) {
108ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    history::FaviconData favicon;
109ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    favicon.known_icon = icon_data.get() != NULL && icon_data->size() > 0;
110ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    favicon.image_data = icon_data;
111ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    favicon.icon_type = history::FAVICON;
112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    request_->ForwardResultAsync(
113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        FaviconService::FaviconDataCallback::TupleType(request_->handle(),
114ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                                       favicon));
115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    delete this;
116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ImageLoadingTracker tracker_;
119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  scoped_refptr<FaviconService::GetFaviconRequest> request_;
120513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  const Extension* extension_;
121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
12272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  DISALLOW_COPY_AND_ASSIGN(ExtensionWebUIImageLoadingTracker);
123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch};
124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}  // namespace
126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
12772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenconst char ExtensionWebUI::kExtensionURLOverrides[] =
1283345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    "extensions.chrome_url_overrides";
129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
13072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenExtensionWebUI::ExtensionWebUI(TabContents* tab_contents, const GURL& url)
13172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    : WebUI(tab_contents),
132513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      url_(url) {
13321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  ExtensionService* service = tab_contents->profile()->GetExtensionService();
134513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  const Extension* extension = service->GetExtensionByURL(url);
1353345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (!extension)
1363345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    extension = service->GetExtensionByWebExtent(url);
1373345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  DCHECK(extension);
1383345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Only hide the url for internal pages (e.g. chrome-extension or packaged
1393345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // component apps like bookmark manager.
1403345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  should_hide_url_ = !extension->is_hosted_app();
141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1423345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  bindings_ = BindingsPolicy::EXTENSION;
14372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // Bind externalHost to Extension WebUI loaded in Chrome Frame.
1443345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
1453345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (browser_command_line.HasSwitch(switches::kChromeFrame))
1463345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    bindings_ |= BindingsPolicy::EXTERNAL_HOST;
147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // For chrome:// overrides, some of the defaults are a little different.
1483345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  GURL effective_url = tab_contents->GetURL();
1493345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (effective_url.SchemeIs(chrome::kChromeUIScheme) &&
1503345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      effective_url.host() == chrome::kChromeUINewTabHost) {
151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    focus_location_bar_by_default_ = true;
152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
15572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenExtensionWebUI::~ExtensionWebUI() {}
156731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
15772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid ExtensionWebUI::ResetExtensionFunctionDispatcher(
158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    RenderViewHost* render_view_host) {
159513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  // TODO(jcivelli): http://crbug.com/60608 we should get the URL out of the
160513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  //                 active entry of the navigation controller.
161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  extension_function_dispatcher_.reset(
162513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch      ExtensionFunctionDispatcher::Create(render_view_host, this, url_));
163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(extension_function_dispatcher_.get());
164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
16672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid ExtensionWebUI::ResetExtensionBookmarkManagerEventRouter() {
1673345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Hack: A few things we specialize just for the bookmark manager.
1683345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (extension_function_dispatcher_->extension_id() ==
1693345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      extension_misc::kBookmarkManagerId) {
1703345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    extension_bookmark_manager_event_router_.reset(
1713345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        new ExtensionBookmarkManagerEventRouter(GetProfile(), tab_contents()));
1723345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1733345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    link_transition_type_ = PageTransition::AUTO_BOOKMARK;
1743345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
17772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid ExtensionWebUI::RenderViewCreated(RenderViewHost* render_view_host) {
178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ResetExtensionFunctionDispatcher(render_view_host);
179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ResetExtensionBookmarkManagerEventRouter();
180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
18272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid ExtensionWebUI::RenderViewReused(RenderViewHost* render_view_host) {
183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ResetExtensionFunctionDispatcher(render_view_host);
184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ResetExtensionBookmarkManagerEventRouter();
185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
18772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid ExtensionWebUI::ProcessWebUIMessage(
188ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    const ExtensionHostMsg_DomMessage_Params& params) {
1893345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  extension_function_dispatcher_->HandleRequest(params);
190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
19272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenBrowser* ExtensionWebUI::GetBrowser() {
1934a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  TabContents* contents = tab_contents();
1944a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  TabContentsIterator tab_iterator;
1954a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  for (; !tab_iterator.done(); ++tab_iterator) {
196ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (contents == (*tab_iterator)->tab_contents())
1974a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      return tab_iterator.browser();
1984a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  }
1994a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
2004a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  return NULL;
201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
20372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenTabContents* ExtensionWebUI::associated_tab_contents() const {
204731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  return tab_contents();
205731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
206731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
207731df977c0511bca2206b5f333555b1205ff1f43Iain MerrickExtensionBookmarkManagerEventRouter*
20872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenExtensionWebUI::extension_bookmark_manager_event_router() {
209731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  return extension_bookmark_manager_event_router_.get();
210731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
211731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
21272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsengfx::NativeWindow ExtensionWebUI::GetCustomFrameNativeWindow() {
213c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (GetBrowser())
214c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return NULL;
215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
216c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If there was no browser associated with the function dispatcher delegate,
21772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // then this WebUI may be hosted in an ExternalTabContainer, and a framing
218c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // window will be accessible through the tab_contents.
219c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  TabContentsDelegate* tab_contents_delegate = tab_contents()->delegate();
220c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (tab_contents_delegate)
221c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return tab_contents_delegate->GetFrameNativeWindow();
222c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  else
223c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return NULL;
224c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
225c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
22672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsengfx::NativeView ExtensionWebUI::GetNativeViewOfHost() {
227ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  RenderWidgetHostView* rwhv = tab_contents()->GetRenderWidgetHostView();
228ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  return rwhv ? rwhv->GetNativeView() : NULL;
229c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
230c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
231c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch////////////////////////////////////////////////////////////////////////////////
232c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// chrome:// URL overrides
233c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
234c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static
23572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid ExtensionWebUI::RegisterUserPrefs(PrefService* prefs) {
236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  prefs->RegisterDictionaryPref(kExtensionURLOverrides);
237c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
239c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static
24072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenbool ExtensionWebUI::HandleChromeURLOverride(GURL* url, Profile* profile) {
241c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!url->SchemeIs(chrome::kChromeUIScheme))
242c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const DictionaryValue* overrides =
245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      profile->GetPrefs()->GetDictionary(kExtensionURLOverrides);
246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::string page = url->host();
247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ListValue* url_list;
2483345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (!overrides || !overrides->GetList(page, &url_list))
249c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
25121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  ExtensionService* service = profile->GetExtensionService();
252c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
2533345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  size_t i = 0;
2543345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  while (i < url_list->GetSize()) {
2553345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    Value* val = NULL;
2563345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    url_list->Get(i, &val);
257c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Verify that the override value is good.  If not, unregister it and find
259c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // the next one.
260c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    std::string override;
261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!val->GetAsString(&override)) {
262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NOTREACHED();
263c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      UnregisterChromeURLOverride(page, profile, val);
264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      continue;
265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    GURL extension_url(override);
267c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!extension_url.is_valid()) {
268c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NOTREACHED();
269c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      UnregisterChromeURLOverride(page, profile, val);
270c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      continue;
271c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
272c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
273c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Verify that the extension that's being referred to actually exists.
274513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    const Extension* extension = service->GetExtensionByURL(extension_url);
275c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!extension) {
276c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // This can currently happen if you use --load-extension one run, and
277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // then don't use it the next.  It could also happen if an extension
278c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // were deleted directly from the filesystem, etc.
279c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      LOG(WARNING) << "chrome URL override present for non-existant extension";
280c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      UnregisterChromeURLOverride(page, profile, val);
281c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      continue;
282c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
283c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
2843345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    // We can't handle chrome-extension URLs in incognito mode unless the
2853345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    // extension uses split mode.
2863345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    bool incognito_override_allowed =
2873345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        extension->incognito_split_mode() &&
288ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        service->IsIncognitoEnabled(extension->id());
2893345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    if (profile->IsOffTheRecord() && !incognito_override_allowed) {
2903345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      ++i;
2913345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      continue;
2923345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    }
2933345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
294c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    *url = extension_url;
295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return true;
296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return false;
298c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
299c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
300c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static
30172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid ExtensionWebUI::RegisterChromeURLOverrides(
302c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Profile* profile, const Extension::URLOverrideMap& overrides) {
303c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (overrides.empty())
304c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
305c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
306c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  PrefService* prefs = profile->GetPrefs();
307ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DictionaryPrefUpdate update(prefs, kExtensionURLOverrides);
308ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DictionaryValue* all_overrides = update.Get();
309c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
310c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // For each override provided by the extension, add it to the front of
311c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // the override list if it's not already in the list.
312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Extension::URLOverrideMap::const_iterator iter = overrides.begin();
3133345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  for (; iter != overrides.end(); ++iter) {
3143345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    const std::string& key = iter->first;
315c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    ListValue* page_overrides;
316c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!all_overrides->GetList(key, &page_overrides)) {
317c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      page_overrides = new ListValue();
318c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      all_overrides->Set(key, page_overrides);
319c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else {
320c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      CleanUpDuplicates(page_overrides);
321c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
322c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Verify that the override isn't already in the list.
323c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      ListValue::iterator i = page_overrides->begin();
324c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      for (; i != page_overrides->end(); ++i) {
325c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        std::string override_val;
326c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        if (!(*i)->GetAsString(&override_val)) {
327c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          NOTREACHED();
328c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          continue;
329c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        }
3303345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        if (override_val == iter->second.spec())
331c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          break;
332c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
333c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // This value is already in the list, leave it alone.
334c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (i != page_overrides->end())
335c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        continue;
336c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
337c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Insert the override at the front of the list.  Last registered override
338c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // wins.
3393345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    page_overrides->Insert(0, new StringValue(iter->second.spec()));
340c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
341c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
342c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
343c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static
34472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid ExtensionWebUI::UnregisterAndReplaceOverride(const std::string& page,
345c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Profile* profile, ListValue* list, Value* override) {
346c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int index = list->Remove(*override);
347c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (index == 0) {
348c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // This is the active override, so we need to find all existing
349c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // tabs for this override and get them to reload the original URL.
350c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    for (TabContentsIterator iterator; !iterator.done(); ++iterator) {
351ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      TabContents* tab = (*iterator)->tab_contents();
352c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (tab->profile() != profile)
353c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        continue;
354c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
355c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      GURL url = tab->GetURL();
356c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (!url.SchemeIs(chrome::kChromeUIScheme) || url.host() != page)
357c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        continue;
358c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
359c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Don't use Reload() since |url| isn't the same as the internal URL
360c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // that NavigationController has.
361c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      tab->controller().LoadURL(url, url, PageTransition::RELOAD);
362c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
363c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
364c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
365c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
366c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static
36772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid ExtensionWebUI::UnregisterChromeURLOverride(const std::string& page,
368c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Profile* profile, Value* override) {
369c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!override)
370c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
371c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  PrefService* prefs = profile->GetPrefs();
372ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DictionaryPrefUpdate update(prefs, kExtensionURLOverrides);
373ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DictionaryValue* all_overrides = update.Get();
374c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ListValue* page_overrides;
3753345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (!all_overrides->GetList(page, &page_overrides)) {
376c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // If it's being unregistered, it should already be in the list.
377c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    NOTREACHED();
378c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
379c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
380c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    UnregisterAndReplaceOverride(page, profile, page_overrides, override);
381c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
382c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
383c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
384c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static
38572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid ExtensionWebUI::UnregisterChromeURLOverrides(
386c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Profile* profile, const Extension::URLOverrideMap& overrides) {
387c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (overrides.empty())
388c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
389c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  PrefService* prefs = profile->GetPrefs();
390ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DictionaryPrefUpdate update(prefs, kExtensionURLOverrides);
391ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DictionaryValue* all_overrides = update.Get();
392c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Extension::URLOverrideMap::const_iterator iter = overrides.begin();
3933345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  for (; iter != overrides.end(); ++iter) {
3943345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    const std::string& page = iter->first;
395c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    ListValue* page_overrides;
396c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!all_overrides->GetList(page, &page_overrides)) {
397c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // If it's being unregistered, it should already be in the list.
398c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NOTREACHED();
399c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      continue;
400c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else {
4013345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      StringValue override(iter->second.spec());
4023345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      UnregisterAndReplaceOverride(iter->first, profile,
403c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                   page_overrides, &override);
404c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
405c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
406c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
407c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
408c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static
40972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid ExtensionWebUI::GetFaviconForURL(Profile* profile,
410c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    FaviconService::GetFaviconRequest* request, const GURL& page_url) {
411c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // tracker deletes itself when done.
41272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  ExtensionWebUIImageLoadingTracker* tracker =
41372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      new ExtensionWebUIImageLoadingTracker(profile, request, page_url);
414c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  tracker->Init();
415c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
416