text: // Copyright (c) 2012 The Chromium Embedded Framework Authors.
  // Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "libcef/browser/net/chrome_scheme_handler.h"
  
  #include <algorithm>
  #include <map>
  #include <string>
  #include <utility>
  
  #include "include/cef_version.h"
  #include "include/cef_web_plugin.h"
  #include "libcef/browser/extensions/chrome_api_registration.h"
  #include "libcef/browser/frame_host_impl.h"
  #include "libcef/browser/net/internal_scheme_handler.h"
  #include "libcef/browser/thread_util.h"
  #include "libcef/common/app_manager.h"
  
  #include "base/command_line.h"
  #include "base/files/file_util.h"
  #include "base/lazy_instance.h"
  #include "base/logging.h"
  #include "base/memory/ptr_util.h"
  #include "base/path_service.h"
  #include "base/strings/string_util.h"
  #include "base/strings/stringprintf.h"
  #include "base/strings/utf_string_conversions.h"
  #include "base/values.h"
  #include "cef/grit/cef_resources.h"
  #include "chrome/browser/browser_about_handler.h"
  #include "chrome/browser/profiles/profile.h"
  #include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h"
  #include "chrome/browser/ui/webui/theme_source.h"
  #include "chrome/common/url_constants.h"
  #include "content/browser/frame_host/debug_urls.h"
  #include "content/browser/webui/content_web_ui_controller_factory.h"
  #include "content/public/browser/browser_url_handler.h"
  #include "content/public/browser/content_browser_client.h"
  #include "content/public/browser/url_data_source.h"
  #include "content/public/browser/web_ui_controller.h"
  #include "content/public/common/url_constants.h"
  #include "content/public/common/url_utils.h"
  #include "content/public/common/user_agent.h"
  #include "ipc/ipc_channel.h"
  #include "ui/base/resource/resource_bundle.h"
  #include "v8/include/v8.h"
  
  using extensions::api::cef::kSupportedAPIs;
  
  namespace scheme {
  
  const char kChromeURL[] = "chrome://";
  
  namespace {
  
  const char kChromeUIExtensionsSupportHost[] = "extensions-support";
  const char kChromeUILicenseHost[] = "license";
  const char kChromeUIWebUIHostsHost[] = "webui-hosts";
  
  // TODO(network): Consider handling content::kChromeDevToolsScheme via WebUI
  // (DevToolsUI class) with the following changes:
  // 1. Add an entry for content::kChromeDevToolsScheme in
  //    AlloyContentBrowserClient::GetAdditionalWebUISchemes.
  // 2. Allow the scheme in CefWebUIControllerFactory::AllowWebUIForURL.
  // 3. Add an entry for chrome::kChromeUIDevToolsHost in kAllowedWebUIHosts and
  //    kUnlistedHosts.
  // 4. Remove scheme::RegisterInternalHandlers and related plumbing.
  
  // Chrome hosts implemented by WebUI.
  // Some WebUI handlers have Chrome dependencies that may fail in CEF without
  // additional changes. Do not add new hosts to this list without also manually
  // testing all related functionality in CEF.
  const char* kAllowedWebUIHosts[] = {
      content::kChromeUIAppCacheInternalsHost,
      chrome::kChromeUIAccessibilityHost,
      content::kChromeUIBlobInternalsHost,
      chrome::kChromeUIChromeURLsHost,
      chrome::kChromeUICreditsHost,
      kChromeUIExtensionsSupportHost,
      content::kChromeUIGpuHost,
      content::kChromeUIHistogramHost,
      content::kChromeUIIndexedDBInternalsHost,
      kChromeUILicenseHost,
      content::kChromeUIMediaInternalsHost,
      chrome::kChromeUINetExportHost,
      chrome::kChromeUINetInternalsHost,
      content::kChromeUINetworkErrorHost,
      content::kChromeUINetworkErrorsListingHost,
      chrome::kChromeUIPrintHost,
      content::kChromeUIProcessInternalsHost,
      content::kChromeUIResourcesHost,
      content::kChromeUIServiceWorkerInternalsHost,
      chrome::kChromeUISystemInfoHost,
      chrome::kChromeUIThemeHost,
      content::kChromeUITracingHost,
      chrome::kChromeUIVersionHost,
      content::kChromeUIWebRTCInternalsHost,
      kChromeUIWebUIHostsHost,
  };
  
  // Hosts that don't have useful output when linked directly. They'll be excluded
  // from the "chrome://webui-hosts" listing.
  const char* kUnlistedHosts[] = {
      content::kChromeUINetworkErrorHost,
      content::kChromeUIResourcesHost,
      chrome::kChromeUIThemeHost,
  };
  
  enum ChromeHostId {
    CHROME_UNKNOWN = 0,
    CHROME_EXTENSIONS_SUPPORT,
    CHROME_LICENSE,
    CHROME_VERSION,
    CHROME_WEBUI_HOSTS,
  };
  
  // Chrome hosts implemented by CEF.
  const struct {
    const char* host;
    ChromeHostId host_id;
  } kAllowedCefHosts[] = {
      {chrome::kChromeUIChromeURLsHost, CHROME_WEBUI_HOSTS},
      {kChromeUIExtensionsSupportHost, CHROME_EXTENSIONS_SUPPORT},
      {kChromeUILicenseHost, CHROME_LICENSE},
      {chrome::kChromeUIVersionHost, CHROME_VERSION},
      {kChromeUIWebUIHostsHost, CHROME_WEBUI_HOSTS},
  };
  
  ChromeHostId GetChromeHostId(const std::string& host) {
    for (size_t i = 0; i < sizeof(kAllowedCefHosts) / sizeof(kAllowedCefHosts[0]);
         ++i) {
      if (base::EqualsCaseInsensitiveASCII(kAllowedCefHosts[i].host,
                                           host.c_str())) {
        return kAllowedCefHosts[i].host_id;
      }
    }
  
    return CHROME_UNKNOWN;
  }
  
  // Returns WebUI hosts. Does not include chrome debug hosts (for crashing, etc).
  void GetAllowedHosts(std::vector<std::string>* hosts) {
    // Explicitly whitelisted WebUI hosts.
    for (size_t i = 0;
         i < sizeof(kAllowedWebUIHosts) / sizeof(kAllowedWebUIHosts[0]); ++i) {
      hosts->push_back(kAllowedWebUIHosts[i]);
    }
  }
  
  // Returns true if a host should not be listed on "chrome://webui-hosts".
  bool IsUnlistedHost(const std::string& host) {
    for (size_t i = 0; i < sizeof(kUnlistedHosts) / sizeof(kUnlistedHosts[0]);
         ++i) {
      if (host == kUnlistedHosts[i])
        return true;
    }
    return false;
  }
  
  // Returns true if a host is WebUI and should be allowed to load.
  bool IsAllowedWebUIHost(const std::string& host) {
    // Explicitly whitelisted WebUI hosts.
    for (size_t i = 0;
         i < sizeof(kAllowedWebUIHosts) / sizeof(kAllowedWebUIHosts[0]); ++i) {
      if (base::EqualsCaseInsensitiveASCII(kAllowedWebUIHosts[i], host.c_str())) {
        return true;
      }
    }
  
    return false;
  }
  
  // Additional debug URLs that are not included in chrome::kChromeDebugURLs.
  const char* kAllowedDebugURLs[] = {
      content::kChromeUIBrowserCrashURL,
  };
  
  void GetDebugURLs(std::vector<std::string>* urls) {
    for (size_t i = 0; i < chrome::kNumberOfChromeDebugURLs; ++i) {
      urls->push_back(chrome::kChromeDebugURLs[i]);
    }
  
    for (size_t i = 0;
         i < sizeof(kAllowedDebugURLs) / sizeof(kAllowedDebugURLs[0]); ++i) {
      urls->push_back(kAllowedDebugURLs[i]);
    }
  }
  
  std::string GetOSType() {
  #if defined(OS_WIN)
    return "Windows";
  #elif defined(OS_MACOSX)
    return "Mac OS X";
  #elif defined(OS_CHROMEOS)
    return "Chromium OS";
  #elif defined(OS_ANDROID)
    return "Android";
  #elif defined(OS_LINUX)
    return "Linux";
  #elif defined(OS_FREEBSD)
    return "FreeBSD";
  #elif defined(OS_OPENBSD)
    return "OpenBSD";
  #elif defined(OS_SOLARIS)
    return "Solaris";
  #else
    return "Unknown";
  #endif
  }
  
  std::string GetCommandLine() {
  #if defined(OS_WIN)
    return base::WideToUTF8(
        base::CommandLine::ForCurrentProcess()->GetCommandLineString());
  #elif defined(OS_POSIX)
    std::string command_line = "";
    typedef std::vector<std::string> ArgvList;
    const ArgvList& argv = base::CommandLine::ForCurrentProcess()->argv();
    for (ArgvList::const_iterator iter = argv.begin(); iter != argv.end(); iter++)
      command_line += " " + *iter;
    // TODO(viettrungluu): |command_line| could really have any encoding, whereas
    // below we assumes it's UTF-8.
    return command_line;
  #endif
  }
  
  std::string GetModulePath() {
    base::FilePath path;
    if (base::PathService::Get(base::FILE_MODULE, &path))
      return CefString(path.value());
    return std::string();
  }
  
  class TemplateParser {
   public:
    TemplateParser() : ident_start_("$$"), ident_end_("$$") {}
  
    TemplateParser(const std::string& ident_start, const std::string& ident_end)
        : ident_start_(ident_start), ident_end_(ident_end) {}
  
    void Add(const std::string& key, const std::string& value) {
      values_.insert(std::make_pair(key, value));
    }
  
    void Parse(std::string* tmpl) {
      int start_pos, end_pos = 0;
      int ident_start_len = ident_start_.length();
      int ident_end_len = ident_end_.length();
  
      while (true) {
        start_pos = tmpl->find(ident_start_, end_pos);
        if (start_pos >= 0) {
          end_pos = tmpl->find(ident_end_, start_pos + ident_start_len);
          if (end_pos >= 0) {
            // Found an identifier. Check if a substitution exists.
            std::string key = tmpl->substr(start_pos + ident_start_len,
                                           end_pos - start_pos - ident_start_len);
            KeyMap::const_iterator it = values_.find(key);
            if (it != values_.end()) {
              // Peform the substitution.
              tmpl->replace(start_pos, end_pos + ident_end_len - start_pos,
                            it->second);
              end_pos = start_pos + it->second.length();
            } else {
              // Leave the unknown identifier in place.
              end_pos += ident_end_len;
            }
  
            if (end_pos >= static_cast<int>(tmpl->length()) - ident_start_len -
                               ident_end_len) {
              // Not enough room remaining for more identifiers.
              break;
            }
          } else {
            // No end identifier found.
            break;
          }
        } else {
          // No start identifier found.
          break;
        }
      }
    }
  
   private:
    typedef std::map<std::string, std::string> KeyMap;
    KeyMap values_;
    std::string ident_start_;
    std::string ident_end_;
  };
  
  bool OnExtensionsSupportUI(std::string* mime_type, std::string* output) {
    static const char kDevURL[] = "https://developer.chrome.com/extensions/";
  
    std::string html =
        "<html>\n<head><title>Extensions Support</title></head>\n"
        "<body bgcolor=\"white\"><h3>Supported Chrome Extensions "
        "APIs</h3>\nFollow <a "
        "href=\"https://bitbucket.org/chromiumembedded/cef/issues/1947\" "
        "target=\"new\">issue #1947</a> for development progress.\n<ul>\n";
  
    bool has_top_level_name = false;
    for (size_t i = 0; kSupportedAPIs[i] != nullptr; ++i) {
      const std::string& api_name = kSupportedAPIs[i];
      if (api_name.find("Private") != std::string::npos) {
        // Don't list private APIs.
        continue;
      }
  
      const size_t dot_pos = api_name.find('.');
      if (dot_pos == std::string::npos) {
        if (has_top_level_name) {
          // End the previous top-level API entry.
          html += "</ul></li>\n";
        } else {
          has_top_level_name = true;
        }
  
        // Start a new top-level API entry.
        html += "<li><a href=\"" + std::string(kDevURL) + api_name +
                "\" target=\"new\">" + api_name + "</a><ul>\n";
      } else {
        // Function name.
        const std::string& group_name = api_name.substr(0, dot_pos);
        const std::string& function_name = api_name.substr(dot_pos + 1);
        html += "\t<li><a href=\"" + std::string(kDevURL) + group_name +
                "#method-" + function_name + "\" target=\"new\">" + api_name +
                "</a></li>\n";
      }
    }
  
    if (has_top_level_name) {
      // End the last top-level API entry.
      html += "</ul></li>\n";
    }
  
    html += "</ul>\n</body>\n</html>";
  
    *mime_type = "text/html";
    *output = html;
  
    return true;
  }
  
  bool OnLicenseUI(std::string* mime_type, std::string* output) {
    std::string piece =
        ui::ResourceBundle::GetSharedInstance().LoadDataResourceString(
            IDR_CEF_LICENSE_TXT);
    if (piece.empty()) {
      NOTREACHED() << "Failed to load license txt resource.";
      return false;
    }
  
    *mime_type = "text/html";
    *output = "<html><head><title>License</title></head><body><pre>" + piece +
              "</pre></body></html>";
  
    return true;
  }
  
  bool OnVersionUI(Profile* profile,
                   std::string* mime_type,
                   std::string* output) {
    std::string tmpl =
        ui::ResourceBundle::GetSharedInstance().LoadDataResourceString(
            IDR_CEF_VERSION_HTML);
    if (tmpl.empty()) {
      NOTREACHED() << "Failed to load version html resource.";
      return false;
    }
  
    TemplateParser parser;
    parser.Add("YEAR", MAKE_STRING(COPYRIGHT_YEAR));
    parser.Add("CEF", CEF_VERSION);
    parser.Add("CHROMIUM",
               base::StringPrintf("%d.%d.%d.%d", CHROME_VERSION_MAJOR,
                                  CHROME_VERSION_MINOR, CHROME_VERSION_BUILD,
                                  CHROME_VERSION_PATCH));
    parser.Add("OS", GetOSType());
    parser.Add("WEBKIT", content::GetWebKitVersion());
    parser.Add("JAVASCRIPT", v8::V8::GetVersion());
    parser.Add("FLASH", std::string());  // Value populated asynchronously.
    parser.Add(
        "USERAGENT",
        CefAppManager::Get()->GetContentClient()->browser()->GetUserAgent());
    parser.Add("COMMANDLINE", GetCommandLine());
    parser.Add("MODULEPATH", GetModulePath());
    parser.Add("CACHEPATH", CefString(profile->GetPath().value()));
  
    parser.Parse(&tmpl);
  
    *mime_type = "text/html";
    *output = tmpl;
  
    return true;
  }
  
  bool OnWebUIHostsUI(std::string* mime_type, std::string* output) {
    std::string html =
        "<html>\n<head><title>Chrome URLs</title></head>\n"
        "<body bgcolor=\"white\"><h3>List of Chrome URLs</h3>\n<ul>\n";
  
    std::vector<std::string> list;
    GetAllowedHosts(&list);
    std::sort(list.begin(), list.end());
  
    for (size_t i = 0U; i < list.size(); ++i) {
      if (IsUnlistedHost(list[i]))
        continue;
  
      html += "<li><a href=\"chrome://" + list[i] + "\">chrome://" + list[i] +
              "</a></li>\n";
    }
  
    list.clear();
    GetDebugURLs(&list);
    std::sort(list.begin(), list.end());
  
    html +=
        "</ul>\n<h3>For Debug</h3>\n"
        "<p>The following pages are for debugging purposes only. Because they "
        "crash or hang the renderer, they're not linked directly; you can type "
        "them into the address bar if you need them.</p>\n<ul>\n";
    for (size_t i = 0U; i < list.size(); ++i) {
      html += "<li>" + std::string(list[i]) + "</li>\n";
    }
    html += "</ul>\n";
  
    html += "</body>\n</html>";
  
    *mime_type = "text/html";
    *output = html;
  
    return true;
  }
  
  const content::WebUI::TypeID kCefWebUITypeID = &kCefWebUITypeID;
  
  class CefURLDataSource : public content::URLDataSource {
   public:
    CefURLDataSource(const std::string& host,
                     ChromeHostId host_id,
                     Profile* profile)
        : host_(host), host_id_(host_id), profile_(profile) {
      CEF_REQUIRE_UIT();
      output_ = new base::RefCountedString();
      bool handled = false;
      switch (host_id_) {
        case CHROME_EXTENSIONS_SUPPORT:
          handled = OnExtensionsSupportUI(&mime_type_, &output_->data());
          break;
        case CHROME_LICENSE:
          handled = OnLicenseUI(&mime_type_, &output_->data());
          break;
        case CHROME_VERSION:
          handled = OnVersionUI(profile_, &mime_type_, &output_->data());
          break;
        case CHROME_WEBUI_HOSTS:
          handled = OnWebUIHostsUI(&mime_type_, &output_->data());
          break;
        default:
          break;
      }
      DCHECK(handled) << "Unhandled WebUI host: " << host;
    }
  
    ~CefURLDataSource() override = default;
  
    // content::URLDataSource implementation.
    std::string GetSource() override { return host_; }
  
    void StartDataRequest(
        const GURL& path,
        const content::WebContents::Getter& wc_getter,
        content::URLDataSource::GotDataCallback callback) override {
      std::move(callback).Run(output_);
    }
  
    std::string GetMimeType(const std::string& path) override {
      return mime_type_;
    }
  
    bool AllowCaching() override { return false; }
  
   private:
    const std::string host_;
    const ChromeHostId host_id_;
    Profile* const profile_;
  
    std::string mime_type_;
    scoped_refptr<base::RefCountedString> output_;
  
    DISALLOW_COPY_AND_ASSIGN(CefURLDataSource);
  };
  
  class CefWebUIController : public content::WebUIController {
   public:
    CefWebUIController(content::WebUI* web_ui,
                       const std::string& host,
                       ChromeHostId host_id)
        : content::WebUIController(web_ui) {
      Profile* profile = Profile::FromWebUI(web_ui);
      content::URLDataSource::Add(
          profile, std::make_unique<CefURLDataSource>(host, host_id, profile));
    }
  
    ~CefWebUIController() override = default;
  
   private:
    DISALLOW_COPY_AND_ASSIGN(CefWebUIController);
  };
  
  // Intercepts all WebUI calls and either blocks them or forwards them to the
  // Content or Chrome WebUI factory as appropriate.
  class CefWebUIControllerFactory : public content::WebUIControllerFactory {
   public:
    // Returns true if WebUI is allowed to handle the specified |url|.
    static bool AllowWebUIForURL(const GURL& url) {
      if (!url.SchemeIs(content::kChromeUIScheme))
        return false;
  
      if (IsAllowedWebUIHost(url.host()))
        return true;
  
      return false;
    }
  
    // Returns true if WebUI is allowed to make network requests.
    static bool IsWebUIAllowedToMakeNetworkRequests(const url::Origin& origin) {
      if (!AllowWebUIForURL(origin.GetURL()))
        return false;
  
      if (ChromeWebUIControllerFactory::IsWebUIAllowedToMakeNetworkRequests(
              origin)) {
        return true;
      }
  
      return false;
    }
  
    std::unique_ptr<content::WebUIController> CreateWebUIControllerForURL(
        content::WebUI* web_ui,
        const GURL& url) override {
      std::unique_ptr<content::WebUIController> controller;
      if (!AllowWebUIForURL(url))
        return controller;
  
      // Set up the chrome://theme/ source. These URLs are referenced from many
      // places (WebUI and chrome://resources which live in //ui). WebUI code
      // can live in both //content and //chrome. Since ThemeSource lives in
      // //chrome the WebUI from //content is not performing this setup despite
      // the fact that it's needed for proper handling of theme resource requests.
      // See https://crbug.com/1011280.
      Profile* profile = Profile::FromWebUI(web_ui);
      content::URLDataSource::Add(profile,
                                  std::make_unique<ThemeSource>(profile));
  
      const auto host_id = GetChromeHostId(url.host());
      if (host_id != CHROME_UNKNOWN) {
        return std::make_unique<CefWebUIController>(web_ui, url.host(), host_id);
      }
  
      controller = content::ContentWebUIControllerFactory::GetInstance()
                       ->CreateWebUIControllerForURL(web_ui, url);
      if (controller.get())
        return controller;
  
      return ChromeWebUIControllerFactory::GetInstance()
          ->CreateWebUIControllerForURL(web_ui, url);
    }
  
    content::WebUI::TypeID GetWebUIType(content::BrowserContext* browser_context,
                                        const GURL& url) override {
      content::WebUI::TypeID type = content::WebUI::kNoWebUI;
      if (!AllowWebUIForURL(url))
        return type;
  
      const auto host_id = GetChromeHostId(url.host());
      if (host_id != CHROME_UNKNOWN) {
        return kCefWebUITypeID;
      }
  
      type = content::ContentWebUIControllerFactory::GetInstance()->GetWebUIType(
          browser_context, url);
      if (type != content::WebUI::kNoWebUI)
        return type;
  
      type = ChromeWebUIControllerFactory::GetInstance()->GetWebUIType(
          browser_context, url);
      if (type != content::WebUI::kNoWebUI)
        return type;
  
      return content::WebUI::kNoWebUI;
    }
  
    bool UseWebUIForURL(content::BrowserContext* browser_context,
                        const GURL& url) override {
      if (!AllowWebUIForURL(url))
        return false;
  
      const auto host_id = GetChromeHostId(url.host());
      if (host_id != CHROME_UNKNOWN) {
        return true;
      }
  
      if (content::ContentWebUIControllerFactory::GetInstance()->UseWebUIForURL(
              browser_context, url) ||
          ChromeWebUIControllerFactory::GetInstance()->UseWebUIForURL(
              browser_context, url)) {
        return true;
      }
  
      return false;
    }
  
    bool UseWebUIBindingsForURL(content::BrowserContext* browser_context,
                                const GURL& url) override {
      if (!AllowWebUIForURL(url))
        return false;
  
      const auto host_id = GetChromeHostId(url.host());
      if (host_id != CHROME_UNKNOWN) {
        // TODO(network): Use WebUI bindings to implement DidFinishChromeLoad.
        return false;
      }
  
      if (content::ContentWebUIControllerFactory::GetInstance()
              ->UseWebUIBindingsForURL(browser_context, url) ||
          ChromeWebUIControllerFactory::GetInstance()->UseWebUIBindingsForURL(
              browser_context, url)) {
        return true;
      }
  
      return false;
    }
  
    static void BrowserURLHandlerCreated(content::BrowserURLHandler* handler) {
      // about: handler. Must come before chrome: handler, since it will
      // rewrite about: urls to chrome: URLs and then expect chrome: to
      // actually handle them.  Also relies on a preliminary fixup phase.
      handler->SetFixupHandler(&FixupBrowserAboutURL);
      handler->AddHandlerPair(&WillHandleBrowserAboutURL,
                              content::BrowserURLHandler::null_handler());
  
      // chrome: & friends.
      handler->AddHandlerPair(&HandleWebUI, &HandleWebUIReverse);
    }
  
    static CefWebUIControllerFactory* GetInstance();
  
   protected:
    CefWebUIControllerFactory() {}
    ~CefWebUIControllerFactory() override {}
  
   private:
    friend struct base::LazyInstanceTraitsBase<CefWebUIControllerFactory>;
  
    // From chrome/browser/chrome_content_browser_client.cc
  
    // Handles rewriting Web UI URLs.
    static bool HandleWebUI(GURL* url, content::BrowserContext* browser_context) {
      if (!GetInstance()->UseWebUIForURL(browser_context, *url))
        return false;
  
      return true;
    }
  
    // Reverse URL handler for Web UI.
    static bool HandleWebUIReverse(GURL* url,
                                   content::BrowserContext* browser_context) {
      // No need to actually reverse-rewrite the URL.
      return false;
    }
  
    DISALLOW_COPY_AND_ASSIGN(CefWebUIControllerFactory);
  };
  
  base::LazyInstance<CefWebUIControllerFactory>::Leaky
      g_web_ui_controller_factory = LAZY_INSTANCE_INITIALIZER;
  
  // static
  CefWebUIControllerFactory* CefWebUIControllerFactory::GetInstance() {
    return &g_web_ui_controller_factory.Get();
  }
  
  void DidFinishChromeVersionLoad(CefRefPtr<CefFrame> frame) {
    // Retieve Flash version information and update asynchronously.
    class Visitor : public CefWebPluginInfoVisitor {
     public:
      Visitor(CefRefPtr<CefFrame> frame) : frame_(frame) {}
  
      bool Visit(CefRefPtr<CefWebPluginInfo> info,
                 int count,
                 int total) override {
        std::string name = info->GetName();
        if (name == "Shockwave Flash") {
          if (frame_->IsValid()) {
            std::string version = info->GetVersion();
            frame_->ExecuteJavaScript(
                "document.getElementById('flash').innerText = '" + version + "';",
                std::string(), 0);
          }
          return false;
        }
  
        return true;
      }
  
     private:
      CefRefPtr<CefFrame> frame_;
  
      IMPLEMENT_REFCOUNTING(Visitor);
    };
  
    CefVisitWebPluginInfo(new Visitor(frame));
  }
  
  }  // namespace
  
  void RegisterWebUIControllerFactory() {
    // Channel all WebUI handling through CefWebUIControllerFactory.
    content::WebUIControllerFactory::UnregisterFactoryForTesting(
        content::ContentWebUIControllerFactory::GetInstance());
  
    content::WebUIControllerFactory::RegisterFactory(
        CefWebUIControllerFactory::GetInstance());
  }
  
  void BrowserURLHandlerCreated(content::BrowserURLHandler* handler) {
    CefWebUIControllerFactory::BrowserURLHandlerCreated(handler);
  }
  
  void DidFinishChromeLoad(CefRefPtr<CefFrame> frame, const GURL& validated_url) {
    ChromeHostId host_id = GetChromeHostId(validated_url.host());
    switch (host_id) {
      case CHROME_VERSION:
        DidFinishChromeVersionLoad(frame);
        break;
      default:
        break;
    }
  }
  
  bool IsWebUIAllowedToMakeNetworkRequests(const url::Origin& origin) {
    return CefWebUIControllerFactory::IsWebUIAllowedToMakeNetworkRequests(origin);
  }
  
  }  // namespace scheme
  