0

WebUI: Migrate more UIs to WebUIConfig, update documentation

Migrate downloads and bookmarks to use WebUIConfig and add information
about WebUIConfigs and how to register them to WebUI documentation. This
is to reduce developer confusion about how to add UIs with Configs
rather than just WebUIControllerFactory. WebUIControllerFactory does not
support untrusted UIs.

Bug: 1317510
Change-Id: I9332180a306923814abdd6e0dbcb58a1f31e1a72
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4990269
Reviewed-by: Demetrios Papadopoulos <dpapad@chromium.org>
Code-Coverage: findit-for-me@appspot.gserviceaccount.com <findit-for-me@appspot.gserviceaccount.com>
Commit-Queue: Rebekah Potter <rbpotter@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1217858}
This commit is contained in:
Rebekah Potter
2023-10-31 20:36:47 +00:00
committed by Chromium LUCI CQ
parent 182b763e8d
commit 26b32f461c
8 changed files with 174 additions and 19 deletions

@ -16,6 +16,7 @@
#include "chrome/browser/ui/webui/favicon_source.h"
#include "chrome/browser/ui/webui/managed_ui_handler.h"
#include "chrome/browser/ui/webui/metrics_handler.h"
#include "chrome/browser/ui/webui/page_not_available_for_guest/page_not_available_for_guest_ui.h"
#include "chrome/browser/ui/webui/plural_string_handler.h"
#include "chrome/browser/ui/webui/webui_util.h"
#include "chrome/common/url_constants.h"
@ -134,6 +135,22 @@ content::WebUIDataSource* CreateAndAddBookmarksUIHTMLSource(Profile* profile) {
} // namespace
BookmarksUIConfig::BookmarksUIConfig()
: WebUIConfig(content::kChromeUIScheme, chrome::kChromeUIBookmarksHost) {}
BookmarksUIConfig::~BookmarksUIConfig() = default;
std::unique_ptr<content::WebUIController>
BookmarksUIConfig::CreateWebUIController(content::WebUI* web_ui,
const GURL& url) {
Profile* profile = Profile::FromWebUI(web_ui);
if (profile->IsGuestSession()) {
return std::make_unique<PageNotAvailableForGuestUI>(
web_ui, chrome::kChromeUIBookmarksHost);
}
return std::make_unique<BookmarksUI>(web_ui);
}
BookmarksUI::BookmarksUI(content::WebUI* web_ui) : WebUIController(web_ui) {
// Set up the chrome://bookmarks/ source.
Profile* profile = Profile::FromWebUI(web_ui);

@ -6,12 +6,24 @@
#define CHROME_BROWSER_UI_WEBUI_BOOKMARKS_BOOKMARKS_UI_H_
#include "content/public/browser/web_ui_controller.h"
#include "content/public/browser/webui_config.h"
#include "ui/base/resource/resource_scale_factor.h"
namespace base {
class RefCountedMemory;
}
class BookmarksUIConfig : public content::WebUIConfig {
public:
BookmarksUIConfig();
~BookmarksUIConfig() override;
// content::WebUIConfig:
std::unique_ptr<content::WebUIController> CreateWebUIController(
content::WebUI* web_ui,
const GURL& url) override;
};
class BookmarksUI : public content::WebUIController {
public:
explicit BookmarksUI(content::WebUI* web_ui);

@ -9,6 +9,11 @@
#include "content/public/browser/webui_config_map.h"
#include "printing/buildflags/buildflags.h"
#if !BUILDFLAG(IS_ANDROID)
#include "chrome/browser/ui/webui/bookmarks/bookmarks_ui.h"
#include "chrome/browser/ui/webui/downloads/downloads_ui.h"
#endif // !BUILDFLAG(IS_ANDROID)
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
#include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
#endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)
@ -23,8 +28,17 @@ void RegisterChromeWebUIConfigs() {
#if BUILDFLAG(IS_CHROMEOS_ASH)
ash::RegisterAshChromeWebUIConfigs();
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
#if !BUILDFLAG(IS_ANDROID) || BUILDFLAG(ENABLE_PRINT_PREVIEW)
auto& map = content::WebUIConfigMap::GetInstance();
#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(ENABLE_PRINT_PREVIEW)
#if !BUILDFLAG(IS_ANDROID)
map.AddWebUIConfig(std::make_unique<BookmarksUIConfig>());
map.AddWebUIConfig(std::make_unique<DownloadsUIConfig>());
#endif // !BUILDFLAG(IS_ANDROID)
#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
map.AddWebUIConfig(std::make_unique<printing::PrintPreviewUIConfig>());
#endif // BUILDFLAG(ENABLE_PRINT_PREVIEW)
}

@ -545,7 +545,6 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui,
#endif // !BUILDFLAG(IS_CHROMEOS)
if (profile->IsGuestSession() &&
(url.host_piece() == chrome::kChromeUIAppLauncherPageHost ||
url.host_piece() == chrome::kChromeUIBookmarksHost ||
url.host_piece() == chrome::kChromeUIHistoryHost ||
url.host_piece() == chrome::kChromeUIExtensionsHost ||
url.host_piece() == chrome::kChromeUINewTabPageHost ||
@ -555,17 +554,11 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui,
}
if (url.host_piece() == chrome::kChromeUIAppServiceInternalsHost)
return &NewWebUI<AppServiceInternalsUI>;
// Bookmarks are part of NTP on Android.
if (url.host_piece() == chrome::kChromeUIBookmarksHost)
return &NewWebUI<BookmarksUI>;
if (url.host_piece() == password_manager::kChromeUIPasswordManagerHost) {
return &NewWebUI<PasswordManagerUI>;
}
if (url.host_piece() == chrome::kChromeUICommanderHost)
return &NewWebUI<CommanderUI>;
// Downloads list on Android uses the built-in download manager.
if (url.host_piece() == chrome::kChromeUIDownloadsHost)
return &NewWebUI<DownloadsUI>;
// Identity API is not available on Android.
if (url.host_piece() == chrome::kChromeUIIdentityInternalsHost)
return &NewWebUI<IdentityInternalsUI>;

@ -199,6 +199,23 @@ content::WebUIDataSource* CreateAndAddDownloadsUIHTMLSource(Profile* profile) {
} // namespace
///////////////////////////////////////////////////////////////////////////////
//
// DownloadsUIConfig
//
///////////////////////////////////////////////////////////////////////////////
DownloadsUIConfig::DownloadsUIConfig()
: WebUIConfig(content::kChromeUIScheme, chrome::kChromeUIDownloadsHost) {}
DownloadsUIConfig::~DownloadsUIConfig() = default;
std::unique_ptr<content::WebUIController>
DownloadsUIConfig::CreateWebUIController(content::WebUI* web_ui,
const GURL& url) {
return std::make_unique<DownloadsUI>(web_ui);
}
///////////////////////////////////////////////////////////////////////////////
//
// DownloadsUI

@ -8,6 +8,7 @@
#include <memory>
#include "chrome/browser/ui/webui/downloads/downloads.mojom.h"
#include "content/public/browser/webui_config.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
@ -20,6 +21,17 @@ class RefCountedMemory;
class DownloadsDOMHandler;
class DownloadsUIConfig : public content::WebUIConfig {
public:
DownloadsUIConfig();
~DownloadsUIConfig() override;
// content::WebUIConfig:
std::unique_ptr<content::WebUIController> CreateWebUIController(
content::WebUI* web_ui,
const GURL& url) override;
};
class DownloadsUI : public ui::MojoWebUIController,
public downloads::mojom::PageHandlerFactory {
public:

@ -100,15 +100,27 @@ When a URL is requested, a new renderer is created to load the URL, and a
corresponding class in the browser is set up to handle messages from the
renderer to the browser (a `RenderFrameHost`).
The URL of the request is inspected:
One factory that serves WebUI URLs is `WebUIConfigMapWebUIControllerFactory`.
This factory looks at a global map from hosts to `WebUIConfig`s to see if any
of the configs handle the requested URL, and calls a method to create the
corresponding controller if so.
```c++
if (url.SchemeIs("chrome") && url.host_piece() == "donuts") // chrome://donuts
return &NewWebUI<DonutsUI>;
return nullptr; // Not a known host; no special access.
auto* config = config_map_->GetConfig(browser_context, url);
if (!config)
return nullptr; // Not a known host; no special access.
return config->CreateWebUIController(web_ui, url);
```
and if a factory knows how to handle a host (returns a `WebUIFactoryFunction`),
Configs can be registered with the map by calling `map.AddWebUIConfig()` in
`chrome_web_ui_configs.cc`:
```c++
map.AddWebUIConfig(std::make_unique<donuts::DonutsUIConfig>());
```
If a factory knows how to handle a host (returns a `WebUIFactoryFunction`),
the navigation machinery [grants the renderer process WebUI
bindings](#bindings) via the child security policy.
@ -120,10 +132,24 @@ if (bindings_flags & BINDINGS_POLICY_WEB_UI) {
}
```
The factory creates a [`WebUIController`](#WebUIController) for a tab.
The factory creates a [`WebUIController`](#WebUIController) for a tab using
the WebUIConfig.
Here's an example:
```c++
// Config for chrome://donuts
DonutsUIConfig::DonutsUIConfig()
: WebUIConfig(content::kChromeUIScheme, chrome::kChromeUIDonutsHost) {}
DonutsUIConfig::~DonutsUIConfig() = default;
std::unique_ptr<content::WebUIController>
DonutsUIConfig::CreateWebUIController(content::WebUI* web_ui,
const GURL& url) {
return std::make_unique<DonutsUI>(web_ui);
}
// Controller for chrome://donuts.
class DonutsUI : public content::WebUIController {
public:
@ -190,6 +216,24 @@ Because they run in a separate process and can exist before a corresponding
renderer process has been created, special care is required to communicate with
the renderer if reliable message passing is required.
### WebUIConfig
A `WebUIConfig` contains minimal possible logic and information for determining
whether a certain subclass of `WebUIController` should be created for a given
URL.
A `WebUIConfig` holds information about the host and scheme (`chrome://` or
`chrome-untrusted://`) that the controller serves.
A `WebUIConfig` may contain logic to check if the WebUI is enabled for a given
`BrowserContext` and url (e.g., if relevant feature flags are enabled/disabled,
if the url path is valid, etc).
A `WebUIConfig` can invoke the `WebUIController`'s constructor in its
`CreateWebUIControllerForURL` method.
`WebUIConfig`s are created at startup when factories are registered, so should
be lightweight.
### WebUIController
A `WebUIController` is the brains of the operation, and is responsible for
@ -200,9 +244,10 @@ pages, logic is often split across multiple
controller for organizational benefits.
A `WebUIController` is owned by a [`WebUI`](#WebUI), and is created and set on
an existing [`WebUI`](#WebUI) when the correct one is determined via URL
inspection (i.e. chrome://settings creates a generic [`WebUI`](#WebUI) with a
settings-specific `WebUIController`).
an existing [`WebUI`](#WebUI) when the corresponding `WebUIConfig` is found in
the map matching the URL, or when the correct controller is determined via URL
inspection in `ChromeWebUIControllerFactory`. (i.e. chrome://settings creates
a generic [`WebUI`](#WebUI) with a settings-specific `WebUIController`).
### WebUIDataSource
@ -428,6 +473,9 @@ alongside the C++ code in chrome/browser/ui/webui. For example:
```
module donuts.mojom;
// Factory ensures that the Page and PageHandler interfaces are always created
// together without requiring an initialization call from the WebUI to the
// handler.
interface PageHandlerFactory {
CreatePageHandler(pending_remote<Page> page,
pending_receiver<PageHandler> handler);

@ -274,9 +274,51 @@ static_library("ui") {
}
```
### Adding your WebUI request handler to the Chrome WebUI factory
### Option 1: Add a WebUIConfig class and put it in the WebUIConfigMap
`WebUIConfig`s contain minimal information about the host and scheme served
by the `WebUIController` subclass. You can create a `WebUIConfig` subclass
and register it in the `WebUIConfigMap` to ensure your request handler is
instantiated and used to handle any requests to the desired scheme + host.
The Chrome WebUI factory is where you setup your new request handler.
`chrome/browser/ui/webui/hello_world/hello_world_ui.h`
```c++
class HelloWorldUIConfig : public content::WebUIConfig {
public:
HelloWorldUIConfig();
~HelloWorldUIConfig() override;
// content::WebUIConfig:
std::unique_ptr<content::WebUIController> CreateWebUIController(
content::WebUI* web_ui,
const GURL& url) override;
};
```
`chrome/browser/ui/webui/hello_world/hello_world_ui.cc`
```c++
HelloWorldUIConfig::HelloWorldUIConfig()
: WebUIConfig(content::kChromeUIScheme, chrome::kChromeUIHelloWorldHost) {}
HelloWorldUIConfig::~HelloWorldUIConfig() = default;
std::unique_ptr<content::WebUIController>
HelloWorldUIConfig::CreateWebUIController(content::WebUI* web_ui,
const GURL& url) {
return std::make_unique<HelloWorldUI>(web_ui);
}
```
`chrome/browser/ui/webui/chrome_web_ui_configs.cc`
```c++
+ #include "chrome/browser/ui/webui/hello_world/hello_world_ui.h"
...
+map.AddWebUIConfig(std::make_unique<hello_world::HelloWorldUIConfig>());
```
### Option 2: Add your WebUI request handler to the Chrome WebUI factory
The Chrome WebUI factory is another way to setup your new request handler.
`chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc:`
```c++