• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium Embedded Framework Authors.
2 // Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 
6 #include "libcef/browser/net/chrome_scheme_handler.h"
7 
8 #include <algorithm>
9 #include <map>
10 #include <string>
11 #include <utility>
12 
13 #include "include/cef_version.h"
14 #include "include/cef_web_plugin.h"
15 #include "libcef/browser/extensions/chrome_api_registration.h"
16 #include "libcef/browser/frame_host_impl.h"
17 #include "libcef/browser/net/internal_scheme_handler.h"
18 #include "libcef/browser/thread_util.h"
19 #include "libcef/common/app_manager.h"
20 #include "libcef/features/runtime.h"
21 
22 #include "base/command_line.h"
23 #include "base/files/file_util.h"
24 #include "base/lazy_instance.h"
25 #include "base/logging.h"
26 #include "base/memory/ptr_util.h"
27 #include "base/path_service.h"
28 #include "base/strings/string_util.h"
29 #include "base/strings/stringprintf.h"
30 #include "base/strings/utf_string_conversions.h"
31 #include "base/values.h"
32 #include "cef/grit/cef_resources.h"
33 #include "chrome/browser/browser_about_handler.h"
34 #include "chrome/browser/profiles/profile.h"
35 #include "chrome/browser/ui/webui/chrome_untrusted_web_ui_controller_factory.h"
36 #include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h"
37 #include "chrome/browser/ui/webui/theme_source.h"
38 #include "chrome/common/url_constants.h"
39 #include "content/browser/renderer_host/debug_urls.h"
40 #include "content/browser/webui/content_web_ui_controller_factory.h"
41 #include "content/public/browser/browser_url_handler.h"
42 #include "content/public/browser/content_browser_client.h"
43 #include "content/public/browser/url_data_source.h"
44 #include "content/public/browser/web_ui_controller.h"
45 #include "content/public/common/url_constants.h"
46 #include "content/public/common/url_utils.h"
47 #include "content/public/common/user_agent.h"
48 #include "ipc/ipc_channel.h"
49 #include "third_party/blink/public/common/chrome_debug_urls.h"
50 #include "ui/base/resource/resource_bundle.h"
51 #include "v8/include/v8.h"
52 
53 using extensions::api::cef::kSupportedAPIs;
54 
55 namespace scheme {
56 
57 const char kChromeURL[] = "chrome://";
58 
59 namespace {
60 
61 const char kChromeUIExtensionsSupportHost[] = "extensions-support";
62 const char kChromeUILicenseHost[] = "license";
63 const char kChromeUIWebUIHostsHost[] = "webui-hosts";
64 
65 // TODO(network): Consider handling content::kChromeDevToolsScheme via WebUI
66 // (DevToolsUI class) with the following changes:
67 // 1. Add an entry for content::kChromeDevToolsScheme in
68 //    AlloyContentBrowserClient::GetAdditionalWebUISchemes.
69 // 2. Allow the scheme in CefWebUIControllerFactory::AllowWebUIForURL.
70 // 3. Add an entry for chrome::kChromeUIDevToolsHost in kAllowedWebUIHosts and
71 //    kUnlistedHosts.
72 // 4. Remove scheme::RegisterInternalHandlers and related plumbing.
73 
74 // Chrome hosts implemented by WebUI.
75 // Some WebUI handlers have Chrome dependencies that may fail in CEF without
76 // additional changes. Do not add new hosts to this list without also manually
77 // testing all related functionality in CEF.
78 const char* kAllowedWebUIHosts[] = {
79     chrome::kChromeUIAccessibilityHost,
80     content::kChromeUIBlobInternalsHost,
81     chrome::kChromeUIChromeURLsHost,
82     chrome::kChromeUICreditsHost,
83     kChromeUIExtensionsSupportHost,
84     content::kChromeUIGpuHost,
85     content::kChromeUIHistogramHost,
86     content::kChromeUIIndexedDBInternalsHost,
87     kChromeUILicenseHost,
88     content::kChromeUIMediaInternalsHost,
89     chrome::kChromeUINetExportHost,
90     chrome::kChromeUINetInternalsHost,
91     content::kChromeUINetworkErrorHost,
92     content::kChromeUINetworkErrorsListingHost,
93     chrome::kChromeUIPrintHost,
94     content::kChromeUIProcessInternalsHost,
95     content::kChromeUIResourcesHost,
96 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
97     chrome::kChromeUISandboxHost,
98 #endif
99     content::kChromeUIServiceWorkerInternalsHost,
100     chrome::kChromeUISystemInfoHost,
101     chrome::kChromeUIThemeHost,
102     content::kChromeUITracingHost,
103     chrome::kChromeUIVersionHost,
104     content::kChromeUIWebRTCInternalsHost,
105     kChromeUIWebUIHostsHost,
106 };
107 
108 // Hosts that don't have useful output when linked directly. They'll be excluded
109 // from the "chrome://webui-hosts" listing.
110 const char* kUnlistedHosts[] = {
111     content::kChromeUINetworkErrorHost,
112     content::kChromeUIResourcesHost,
113     chrome::kChromeUIThemeHost,
114 };
115 
116 enum ChromeHostId {
117   CHROME_UNKNOWN = 0,
118   CHROME_EXTENSIONS_SUPPORT,
119   CHROME_LICENSE,
120   CHROME_VERSION,
121   CHROME_WEBUI_HOSTS,
122 };
123 
124 // Chrome hosts implemented by CEF.
125 const struct {
126   const char* host;
127   ChromeHostId host_id;
128 } kAllowedCefHosts[] = {
129     {chrome::kChromeUIChromeURLsHost, CHROME_WEBUI_HOSTS},
130     {kChromeUIExtensionsSupportHost, CHROME_EXTENSIONS_SUPPORT},
131     {kChromeUILicenseHost, CHROME_LICENSE},
132     {chrome::kChromeUIVersionHost, CHROME_VERSION},
133     {kChromeUIWebUIHostsHost, CHROME_WEBUI_HOSTS},
134 };
135 
GetChromeHostId(const std::string & host)136 ChromeHostId GetChromeHostId(const std::string& host) {
137   for (size_t i = 0; i < sizeof(kAllowedCefHosts) / sizeof(kAllowedCefHosts[0]);
138        ++i) {
139     if (base::EqualsCaseInsensitiveASCII(kAllowedCefHosts[i].host,
140                                          host.c_str())) {
141       return kAllowedCefHosts[i].host_id;
142     }
143   }
144 
145   return CHROME_UNKNOWN;
146 }
147 
148 // Returns WebUI hosts. Does not include chrome debug hosts (for crashing, etc).
GetAllowedHosts(std::vector<std::string> * hosts)149 void GetAllowedHosts(std::vector<std::string>* hosts) {
150   // Explicitly whitelisted WebUI hosts.
151   for (size_t i = 0;
152        i < sizeof(kAllowedWebUIHosts) / sizeof(kAllowedWebUIHosts[0]); ++i) {
153     hosts->push_back(kAllowedWebUIHosts[i]);
154   }
155 }
156 
157 // Returns true if a host should not be listed on "chrome://webui-hosts".
IsUnlistedHost(const std::string & host)158 bool IsUnlistedHost(const std::string& host) {
159   for (size_t i = 0; i < sizeof(kUnlistedHosts) / sizeof(kUnlistedHosts[0]);
160        ++i) {
161     if (host == kUnlistedHosts[i])
162       return true;
163   }
164   return false;
165 }
166 
167 // Returns true if a host is WebUI and should be allowed to load.
IsAllowedWebUIHost(const std::string & host)168 bool IsAllowedWebUIHost(const std::string& host) {
169   // Chrome runtime allows all WebUI hosts.
170   if (cef::IsChromeRuntimeEnabled())
171     return true;
172 
173   // Explicitly whitelisted WebUI hosts.
174   for (size_t i = 0;
175        i < sizeof(kAllowedWebUIHosts) / sizeof(kAllowedWebUIHosts[0]); ++i) {
176     if (base::EqualsCaseInsensitiveASCII(kAllowedWebUIHosts[i], host.c_str())) {
177       return true;
178     }
179   }
180 
181   return false;
182 }
183 
184 // Additional debug URLs that are not included in chrome::kChromeDebugURLs.
185 const char* kAllowedDebugURLs[] = {
186     blink::kChromeUIBrowserCrashURL,
187 };
188 
GetDebugURLs(std::vector<std::string> * urls)189 void GetDebugURLs(std::vector<std::string>* urls) {
190   for (size_t i = 0; i < chrome::kNumberOfChromeDebugURLs; ++i) {
191     urls->push_back(chrome::kChromeDebugURLs[i]);
192   }
193 
194   for (size_t i = 0;
195        i < sizeof(kAllowedDebugURLs) / sizeof(kAllowedDebugURLs[0]); ++i) {
196     urls->push_back(kAllowedDebugURLs[i]);
197   }
198 }
199 
GetOSType()200 std::string GetOSType() {
201 #if BUILDFLAG(IS_WIN)
202   return "Windows";
203 #elif BUILDFLAG(IS_MAC)
204   return "Mac OS X";
205 #elif BUILDFLAG(IS_LINUX)
206   return "Linux";
207 #else
208   return "Unknown";
209 #endif
210 }
211 
GetCommandLine()212 std::string GetCommandLine() {
213 #if BUILDFLAG(IS_WIN)
214   return base::WideToUTF8(
215       base::CommandLine::ForCurrentProcess()->GetCommandLineString());
216 #elif BUILDFLAG(IS_POSIX)
217   std::string command_line = "";
218   using ArgvList = std::vector<std::string>;
219   const ArgvList& argv = base::CommandLine::ForCurrentProcess()->argv();
220   for (ArgvList::const_iterator iter = argv.begin(); iter != argv.end(); iter++)
221     command_line += " " + *iter;
222   // TODO(viettrungluu): |command_line| could really have any encoding, whereas
223   // below we assumes it's UTF-8.
224   return command_line;
225 #endif
226 }
227 
GetModulePath()228 std::string GetModulePath() {
229   base::FilePath path;
230   if (base::PathService::Get(base::FILE_MODULE, &path))
231     return CefString(path.value());
232   return std::string();
233 }
234 
235 class TemplateParser {
236  public:
TemplateParser()237   TemplateParser() : ident_start_("$$"), ident_end_("$$") {}
238 
TemplateParser(const std::string & ident_start,const std::string & ident_end)239   TemplateParser(const std::string& ident_start, const std::string& ident_end)
240       : ident_start_(ident_start), ident_end_(ident_end) {}
241 
Add(const std::string & key,const std::string & value)242   void Add(const std::string& key, const std::string& value) {
243     values_.insert(std::make_pair(key, value));
244   }
245 
Parse(std::string * tmpl)246   void Parse(std::string* tmpl) {
247     int start_pos, end_pos = 0;
248     int ident_start_len = ident_start_.length();
249     int ident_end_len = ident_end_.length();
250 
251     while (true) {
252       start_pos = tmpl->find(ident_start_, end_pos);
253       if (start_pos >= 0) {
254         end_pos = tmpl->find(ident_end_, start_pos + ident_start_len);
255         if (end_pos >= 0) {
256           // Found an identifier. Check if a substitution exists.
257           std::string key = tmpl->substr(start_pos + ident_start_len,
258                                          end_pos - start_pos - ident_start_len);
259           KeyMap::const_iterator it = values_.find(key);
260           if (it != values_.end()) {
261             // Peform the substitution.
262             tmpl->replace(start_pos, end_pos + ident_end_len - start_pos,
263                           it->second);
264             end_pos = start_pos + it->second.length();
265           } else {
266             // Leave the unknown identifier in place.
267             end_pos += ident_end_len;
268           }
269 
270           if (end_pos >= static_cast<int>(tmpl->length()) - ident_start_len -
271                              ident_end_len) {
272             // Not enough room remaining for more identifiers.
273             break;
274           }
275         } else {
276           // No end identifier found.
277           break;
278         }
279       } else {
280         // No start identifier found.
281         break;
282       }
283     }
284   }
285 
286  private:
287   using KeyMap = std::map<std::string, std::string>;
288   KeyMap values_;
289   std::string ident_start_;
290   std::string ident_end_;
291 };
292 
OnExtensionsSupportUI(std::string * mime_type,std::string * output)293 bool OnExtensionsSupportUI(std::string* mime_type, std::string* output) {
294   *mime_type = "text/html";
295 
296   if (cef::IsChromeRuntimeEnabled()) {
297     // Redirect to the Chrome documentation.
298     *output =
299         "<html><head>\n"
300         "<meta http-equiv=\"refresh\" "
301         "content=\"0;URL='https://developer.chrome.com/docs/extensions/'\"/>\n"
302         "</head></html>\n";
303     return true;
304   }
305 
306   static const char kDevURL[] = "https://developer.chrome.com/extensions/";
307 
308   std::string html =
309       "<html>\n<head><title>Extensions Support</title></head>\n"
310       "<body bgcolor=\"white\"><h3>Supported Chrome Extensions "
311       "APIs</h3>\nFollow <a "
312       "href=\"https://bitbucket.org/chromiumembedded/cef/issues/1947\" "
313       "target=\"new\">issue #1947</a> for development progress.\n<ul>\n";
314 
315   bool has_top_level_name = false;
316   for (size_t i = 0; kSupportedAPIs[i] != nullptr; ++i) {
317     const std::string& api_name = kSupportedAPIs[i];
318     if (api_name.find("Private") != std::string::npos) {
319       // Don't list private APIs.
320       continue;
321     }
322 
323     const size_t dot_pos = api_name.find('.');
324     if (dot_pos == std::string::npos) {
325       if (has_top_level_name) {
326         // End the previous top-level API entry.
327         html += "</ul></li>\n";
328       } else {
329         has_top_level_name = true;
330       }
331 
332       // Start a new top-level API entry.
333       html += "<li><a href=\"" + std::string(kDevURL) + api_name +
334               "\" target=\"new\">" + api_name + "</a><ul>\n";
335     } else {
336       // Function name.
337       const std::string& group_name = api_name.substr(0, dot_pos);
338       const std::string& function_name = api_name.substr(dot_pos + 1);
339       html += "\t<li><a href=\"" + std::string(kDevURL) + group_name +
340               "#method-" + function_name + "\" target=\"new\">" + api_name +
341               "</a></li>\n";
342     }
343   }
344 
345   if (has_top_level_name) {
346     // End the last top-level API entry.
347     html += "</ul></li>\n";
348   }
349 
350   html += "</ul>\n</body>\n</html>";
351 
352   *output = html;
353   return true;
354 }
355 
OnLicenseUI(std::string * mime_type,std::string * output)356 bool OnLicenseUI(std::string* mime_type, std::string* output) {
357   std::string piece =
358       ui::ResourceBundle::GetSharedInstance().LoadDataResourceString(
359           IDR_CEF_LICENSE_TXT);
360   if (piece.empty()) {
361     NOTREACHED() << "Failed to load license txt resource.";
362     return false;
363   }
364 
365   *mime_type = "text/html";
366   *output = "<html><head><title>License</title></head><body><pre>" + piece +
367             "</pre></body></html>";
368 
369   return true;
370 }
371 
OnVersionUI(Profile * profile,std::string * mime_type,std::string * output)372 bool OnVersionUI(Profile* profile,
373                  std::string* mime_type,
374                  std::string* output) {
375   std::string tmpl =
376       ui::ResourceBundle::GetSharedInstance().LoadDataResourceString(
377           IDR_CEF_VERSION_HTML);
378   if (tmpl.empty()) {
379     NOTREACHED() << "Failed to load version html resource.";
380     return false;
381   }
382 
383   TemplateParser parser;
384   parser.Add("YEAR", MAKE_STRING(COPYRIGHT_YEAR));
385   parser.Add("CEF", CEF_VERSION);
386   parser.Add("CHROMIUM",
387              base::StringPrintf("%d.%d.%d.%d", CHROME_VERSION_MAJOR,
388                                 CHROME_VERSION_MINOR, CHROME_VERSION_BUILD,
389                                 CHROME_VERSION_PATCH));
390   parser.Add("OS", GetOSType());
391   parser.Add("WEBKIT", content::GetWebKitVersion());
392   parser.Add("JAVASCRIPT", v8::V8::GetVersion());
393   parser.Add(
394       "USERAGENT",
395       CefAppManager::Get()->GetContentClient()->browser()->GetUserAgent());
396   parser.Add("COMMANDLINE", GetCommandLine());
397   parser.Add("MODULEPATH", GetModulePath());
398   parser.Add("CACHEPATH", CefString(profile->GetPath().value()));
399 
400   parser.Parse(&tmpl);
401 
402   *mime_type = "text/html";
403   *output = tmpl;
404 
405   return true;
406 }
407 
OnWebUIHostsUI(std::string * mime_type,std::string * output)408 bool OnWebUIHostsUI(std::string* mime_type, std::string* output) {
409   std::string html =
410       "<html>\n<head><title>Chrome URLs</title></head>\n"
411       "<body bgcolor=\"white\"><h3>List of Chrome URLs</h3>\n<ul>\n";
412 
413   std::vector<std::string> list;
414   GetAllowedHosts(&list);
415   std::sort(list.begin(), list.end());
416 
417   for (size_t i = 0U; i < list.size(); ++i) {
418     if (IsUnlistedHost(list[i]))
419       continue;
420 
421     html += "<li><a href=\"chrome://" + list[i] + "\">chrome://" + list[i] +
422             "</a></li>\n";
423   }
424 
425   list.clear();
426   GetDebugURLs(&list);
427   std::sort(list.begin(), list.end());
428 
429   html +=
430       "</ul>\n<h3>For Debug</h3>\n"
431       "<p>The following pages are for debugging purposes only. Because they "
432       "crash or hang the renderer, they're not linked directly; you can type "
433       "them into the address bar if you need them.</p>\n<ul>\n";
434   for (size_t i = 0U; i < list.size(); ++i) {
435     html += "<li>" + std::string(list[i]) + "</li>\n";
436   }
437   html += "</ul>\n";
438 
439   html += "</body>\n</html>";
440 
441   *mime_type = "text/html";
442   *output = html;
443 
444   return true;
445 }
446 
447 const content::WebUI::TypeID kCefWebUITypeID = &kCefWebUITypeID;
448 
449 class CefURLDataSource : public content::URLDataSource {
450  public:
CefURLDataSource(const std::string & host,ChromeHostId host_id,Profile * profile)451   CefURLDataSource(const std::string& host,
452                    ChromeHostId host_id,
453                    Profile* profile)
454       : host_(host), host_id_(host_id), profile_(profile) {
455     CEF_REQUIRE_UIT();
456     output_ = new base::RefCountedString();
457     bool handled = false;
458     switch (host_id_) {
459       case CHROME_EXTENSIONS_SUPPORT:
460         handled = OnExtensionsSupportUI(&mime_type_, &output_->data());
461         break;
462       case CHROME_LICENSE:
463         handled = OnLicenseUI(&mime_type_, &output_->data());
464         break;
465       case CHROME_VERSION:
466         handled = OnVersionUI(profile_, &mime_type_, &output_->data());
467         break;
468       case CHROME_WEBUI_HOSTS:
469         handled = OnWebUIHostsUI(&mime_type_, &output_->data());
470         break;
471       default:
472         break;
473     }
474     DCHECK(handled) << "Unhandled WebUI host: " << host;
475   }
476 
477   CefURLDataSource(const CefURLDataSource&) = delete;
478   CefURLDataSource& operator=(const CefURLDataSource&) = delete;
479 
480   ~CefURLDataSource() override = default;
481 
482   // content::URLDataSource implementation.
GetSource()483   std::string GetSource() override { return host_; }
484 
StartDataRequest(const GURL & path,const content::WebContents::Getter & wc_getter,content::URLDataSource::GotDataCallback callback)485   void StartDataRequest(
486       const GURL& path,
487       const content::WebContents::Getter& wc_getter,
488       content::URLDataSource::GotDataCallback callback) override {
489     std::move(callback).Run(output_);
490   }
491 
GetMimeType(const std::string & path)492   std::string GetMimeType(const std::string& path) override {
493     return mime_type_;
494   }
495 
AllowCaching()496   bool AllowCaching() override { return false; }
497 
498  private:
499   const std::string host_;
500   const ChromeHostId host_id_;
501   Profile* const profile_;
502 
503   std::string mime_type_;
504   scoped_refptr<base::RefCountedString> output_;
505 };
506 
507 class CefWebUIController : public content::WebUIController {
508  public:
CefWebUIController(content::WebUI * web_ui,const std::string & host,ChromeHostId host_id)509   CefWebUIController(content::WebUI* web_ui,
510                      const std::string& host,
511                      ChromeHostId host_id)
512       : content::WebUIController(web_ui) {
513     Profile* profile = Profile::FromWebUI(web_ui);
514     content::URLDataSource::Add(
515         profile, std::make_unique<CefURLDataSource>(host, host_id, profile));
516   }
517 
518   CefWebUIController(const CefWebUIController&) = delete;
519   CefWebUIController& operator=(const CefWebUIController&) = delete;
520 
521   ~CefWebUIController() override = default;
522 };
523 
524 // Intercepts all WebUI calls and either blocks them or forwards them to the
525 // Content or Chrome WebUI factory as appropriate.
526 class CefWebUIControllerFactory : public content::WebUIControllerFactory {
527  public:
528   CefWebUIControllerFactory(const CefWebUIControllerFactory&) = delete;
529   CefWebUIControllerFactory& operator=(const CefWebUIControllerFactory&) =
530       delete;
531 
532   // Returns true if WebUI is allowed to handle the specified |url|.
AllowWebUIForURL(const GURL & url)533   static bool AllowWebUIForURL(const GURL& url) {
534     if (!url.SchemeIs(content::kChromeUIScheme) &&
535         !url.SchemeIs(content::kChromeUIUntrustedScheme)) {
536       return false;
537     }
538 
539     if (IsAllowedWebUIHost(url.host()))
540       return true;
541 
542     return false;
543   }
544 
545   // Returns true if WebUI is allowed to make network requests.
IsWebUIAllowedToMakeNetworkRequests(const url::Origin & origin)546   static bool IsWebUIAllowedToMakeNetworkRequests(const url::Origin& origin) {
547     if (!AllowWebUIForURL(origin.GetURL()))
548       return false;
549 
550     if (ChromeWebUIControllerFactory::IsWebUIAllowedToMakeNetworkRequests(
551             origin)) {
552       return true;
553     }
554 
555     return false;
556   }
557 
CreateWebUIControllerForURL(content::WebUI * web_ui,const GURL & url)558   std::unique_ptr<content::WebUIController> CreateWebUIControllerForURL(
559       content::WebUI* web_ui,
560       const GURL& url) override {
561     std::unique_ptr<content::WebUIController> controller;
562     if (!AllowWebUIForURL(url))
563       return controller;
564 
565     // Set up the chrome://theme/ source. These URLs are referenced from many
566     // places (WebUI and chrome://resources which live in //ui). WebUI code
567     // can live in both //content and //chrome. Since ThemeSource lives in
568     // //chrome the WebUI from //content is not performing this setup despite
569     // the fact that it's needed for proper handling of theme resource requests.
570     // See https://crbug.com/1011280.
571     Profile* profile = Profile::FromWebUI(web_ui);
572     content::URLDataSource::Add(profile,
573                                 std::make_unique<ThemeSource>(profile));
574 
575     const auto host_id = GetChromeHostId(url.host());
576     if (host_id != CHROME_UNKNOWN) {
577       return std::make_unique<CefWebUIController>(web_ui, url.host(), host_id);
578     }
579 
580     controller = content::ContentWebUIControllerFactory::GetInstance()
581                      ->CreateWebUIControllerForURL(web_ui, url);
582     if (controller.get())
583       return controller;
584 
585     controller = ChromeUntrustedWebUIControllerFactory::GetInstance()
586                      ->CreateWebUIControllerForURL(web_ui, url);
587     if (controller.get())
588       return controller;
589 
590     return ChromeWebUIControllerFactory::GetInstance()
591         ->CreateWebUIControllerForURL(web_ui, url);
592   }
593 
GetWebUIType(content::BrowserContext * browser_context,const GURL & url)594   content::WebUI::TypeID GetWebUIType(content::BrowserContext* browser_context,
595                                       const GURL& url) override {
596     content::WebUI::TypeID type = content::WebUI::kNoWebUI;
597     if (!AllowWebUIForURL(url))
598       return type;
599 
600     const auto host_id = GetChromeHostId(url.host());
601     if (host_id != CHROME_UNKNOWN) {
602       return kCefWebUITypeID;
603     }
604 
605     type = content::ContentWebUIControllerFactory::GetInstance()->GetWebUIType(
606         browser_context, url);
607     if (type != content::WebUI::kNoWebUI)
608       return type;
609 
610     type = ChromeUntrustedWebUIControllerFactory::GetInstance()->GetWebUIType(
611         browser_context, url);
612     if (type != content::WebUI::kNoWebUI)
613       return type;
614 
615     type = ChromeWebUIControllerFactory::GetInstance()->GetWebUIType(
616         browser_context, url);
617     if (type != content::WebUI::kNoWebUI)
618       return type;
619 
620     return content::WebUI::kNoWebUI;
621   }
622 
UseWebUIForURL(content::BrowserContext * browser_context,const GURL & url)623   bool UseWebUIForURL(content::BrowserContext* browser_context,
624                       const GURL& url) override {
625     if (!AllowWebUIForURL(url))
626       return false;
627 
628     const auto host_id = GetChromeHostId(url.host());
629     if (host_id != CHROME_UNKNOWN) {
630       return true;
631     }
632 
633     if (content::ContentWebUIControllerFactory::GetInstance()->UseWebUIForURL(
634             browser_context, url) ||
635         ChromeUntrustedWebUIControllerFactory::GetInstance()->UseWebUIForURL(
636             browser_context, url) ||
637         ChromeWebUIControllerFactory::GetInstance()->UseWebUIForURL(
638             browser_context, url)) {
639       return true;
640     }
641 
642     return false;
643   }
644 
BrowserURLHandlerCreated(content::BrowserURLHandler * handler)645   static void BrowserURLHandlerCreated(content::BrowserURLHandler* handler) {
646     // For Chrome runtime this is registered in
647     // ChromeContentBrowserClient::BrowserURLHandlerCreated().
648     if (cef::IsAlloyRuntimeEnabled()) {
649       // Handler to rewrite chrome://about and chrome://sync URLs.
650       handler->AddHandlerPair(&HandleChromeAboutAndChromeSyncRewrite,
651                               content::BrowserURLHandler::null_handler());
652     }
653 
654     // chrome: & friends. For Chrome runtime the default registration is
655     // disabled is ChromeContentBrowserClient::BrowserURLHandlerCreated().
656     handler->AddHandlerPair(&HandleWebUI, &HandleWebUIReverse);
657   }
658 
659   static CefWebUIControllerFactory* GetInstance();
660 
661  protected:
662   CefWebUIControllerFactory() = default;
663   ~CefWebUIControllerFactory() override = default;
664 
665  private:
666   friend struct base::LazyInstanceTraitsBase<CefWebUIControllerFactory>;
667 
668   // From chrome/browser/chrome_content_browser_client.cc
669 
670   // Handles rewriting Web UI URLs.
HandleWebUI(GURL * url,content::BrowserContext * browser_context)671   static bool HandleWebUI(GURL* url, content::BrowserContext* browser_context) {
672     if (!GetInstance()->UseWebUIForURL(browser_context, *url))
673       return false;
674 
675     return true;
676   }
677 
678   // Reverse URL handler for Web UI.
HandleWebUIReverse(GURL * url,content::BrowserContext * browser_context)679   static bool HandleWebUIReverse(GURL* url,
680                                  content::BrowserContext* browser_context) {
681     // No need to actually reverse-rewrite the URL.
682     return false;
683   }
684 };
685 
686 base::LazyInstance<CefWebUIControllerFactory>::Leaky
687     g_web_ui_controller_factory = LAZY_INSTANCE_INITIALIZER;
688 
689 // static
GetInstance()690 CefWebUIControllerFactory* CefWebUIControllerFactory::GetInstance() {
691   return &g_web_ui_controller_factory.Get();
692 }
693 
694 }  // namespace
695 
RegisterWebUIControllerFactory()696 void RegisterWebUIControllerFactory() {
697   // Channel all WebUI handling through CefWebUIControllerFactory.
698   content::WebUIControllerFactory::UnregisterFactoryForTesting(
699       content::ContentWebUIControllerFactory::GetInstance());
700 
701   content::WebUIControllerFactory::RegisterFactory(
702       CefWebUIControllerFactory::GetInstance());
703 }
704 
BrowserURLHandlerCreated(content::BrowserURLHandler * handler)705 void BrowserURLHandlerCreated(content::BrowserURLHandler* handler) {
706   CefWebUIControllerFactory::BrowserURLHandlerCreated(handler);
707 }
708 
IsWebUIAllowedToMakeNetworkRequests(const url::Origin & origin)709 bool IsWebUIAllowedToMakeNetworkRequests(const url::Origin& origin) {
710   return CefWebUIControllerFactory::IsWebUIAllowedToMakeNetworkRequests(origin);
711 }
712 
713 }  // namespace scheme
714