0

Update WebUI explainer doc

Using this doc as a reference for creating a new WebUI, there are a
couple minor friction points.

* Fix syntax errors and missing variables/members
* Use DefaultWebUIConfig - simplest method and best starting point
* Export Browser Proxy interface - otherwise need to refactor when it
comes time to write browser tests

Ideally, there would be a doc to literally copy/paste from, but this
adds a maintenance burden and the files would get long with includes
and such.

Change-Id: I06243596985f89d571827e67e2e469364154bfcd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5668492
Reviewed-by: Demetrios Papadopoulos <dpapad@chromium.org>
Commit-Queue: Mickey Burks <mickeyburks@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1322107}
This commit is contained in:
Mickey Burks
2024-07-02 11:45:05 +00:00
committed by Chromium LUCI CQ
parent 112cf3db90
commit 463e6f5b87

@ -142,20 +142,23 @@ if (bindings_flags & BINDINGS_POLICY_WEB_UI) {
The factory creates a [`WebUIController`](#WebUIController) for a tab using The factory creates a [`WebUIController`](#WebUIController) for a tab using
the WebUIConfig. the WebUIConfig.
Here's an example: Here's an example using the DefaultWebUIConfig:
```c++ ```c++
class DonutsUI;
// This would go in chrome/common/webui_url_constants.cc
namespace chrome {
const char kChromeUIDonutsHost[] = "donuts";
} // namespace chrome
// Config for chrome://donuts // Config for chrome://donuts
DonutsUIConfig::DonutsUIConfig() class DonutsUIConfig : public content::DefaultWebUIConfig<DonutsUI> {
: WebUIConfig(content::kChromeUIScheme, chrome::kChromeUIDonutsHost) {} public:
DonutsUIConfig()
DonutsUIConfig::~DonutsUIConfig() = default; : DefaultWebUIConfig(content::kChromeUIScheme,
chrome::kChromeUIDonutsHost) {}
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. // Controller for chrome://donuts.
class DonutsUI : public content::WebUIController { class DonutsUI : public content::WebUIController {
@ -610,13 +613,17 @@ class DonutsPageHandler : public donuts::mojom::PageHandler {
~DonutsPageHandler() override; ~DonutsPageHandler() override;
// Triggered by some outside event // Triggered by some outside event
void DonutsPageHandler::OnBakingDonutsFinished(uint32_t num_donuts); void OnBakingDonutsFinished(uint32_t num_donuts);
// donuts::mojom::PageHandler: // donuts::mojom::PageHandler:
void StartPilotLight() override; void StartPilotLight() override;
void BakeDonuts(uint32_t num_donuts) override; void BakeDonuts(uint32_t num_donuts) override;
void GetNumberOfDonuts(GetNumberOfDonutsCallback callback) override; void GetNumberOfDonuts(GetNumberOfDonutsCallback callback) override;
}
private:
mojo::Receiver<donuts::mojom::PageHandler> receiver_;
mojo::Remote<donuts::mojom::Page> page_;
};
``` ```
The message handler needs to implement all the methods on the PageHandler The message handler needs to implement all the methods on the PageHandler
@ -646,7 +653,7 @@ void DonutsPageHandler::StartPilotLight() {
} }
// Triggered by bakeDonuts() call in TS. // Triggered by bakeDonuts() call in TS.
void DonutsPageHandler::BakeDonuts(int32_t num_donuts) { void DonutsPageHandler::BakeDonuts(uint32_t num_donuts) {
GetOven()->BakeDonuts(); GetOven()->BakeDonuts();
} }
@ -667,14 +674,22 @@ added to the build and served from the root (e.g.
**chrome/browser/resources/donuts/BUILD.gn** **chrome/browser/resources/donuts/BUILD.gn**
``` ```
import("//ui/webui/resources/tools/build_webui.gni")
build_webui("build") { build_webui("build") {
# ... Other arguments go here grd_prefix = "donuts"
# You will add these files in the next step:
non_web_component_files = [
"donuts.ts",
"browser_proxy.ts",
]
mojo_files_deps = mojo_files_deps =
[ "//chrome/browser/ui/webui/donuts:mojo_bindings_ts__generator" ] [ "//chrome/browser/ui/webui/donuts:mojo_bindings_ts__generator" ]
mojo_files = [ mojo_files = [
"$root_gen_dir/chrome/browser/ui/webui/donuts/donuts.mojom-webui.ts", "$root_gen_dir/chrome/browser/ui/webui/donuts/donuts.mojom-webui.ts",
] ]
# ... Other arguments can go here
} }
``` ```
@ -683,19 +698,23 @@ class:
**chrome/browser/resources/donuts/browser_proxy.ts** **chrome/browser/resources/donuts/browser_proxy.ts**
```js ```js
import {PageCallbackRouter, PageHandlerFactory, PageHandlerInterface, PageHandlerRemote} from './donuts.mojom-webui.js'; import {PageCallbackRouter, PageHandlerFactory, PageHandlerRemote} from './donuts.mojom-webui.js';
import type {PageHandlerInterface} from './donuts.mojom-webui.js';
class BrowserProxy { // Exporting the interface helps when creating a TestBrowserProxy wrapper.
export interface BrowserProxy {
callbackRouter: PageCallbackRouter;
handler: PageHandlerInterface;
}
export class BrowserProxyImpl implements BrowserProxy {
callbackRouter: PageCallbackRouter; callbackRouter: PageCallbackRouter;
handler: PageHandlerInterface; handler: PageHandlerInterface;
constructor() { private constructor() {
this.callbackRouter = new PageCallbackRouter(); this.callbackRouter = new PageCallbackRouter();
this.handler = new PageHandlerRemote(); this.handler = new PageHandlerRemote();
PageHandlerFactory.getRemote().createPageHandler(
const factory = PageHandlerFactory.getRemote();
factory.createPageHandler(
this.callbackRouter.$.bindNewPipeAndPassRemote(), this.callbackRouter.$.bindNewPipeAndPassRemote(),
(this.handler as PageHandlerRemote).$.bindNewPipeAndPassReceiver()); (this.handler as PageHandlerRemote).$.bindNewPipeAndPassReceiver());
} }
@ -704,8 +723,8 @@ class BrowserProxy {
return instance || (instance = new BrowserProxy()); return instance || (instance = new BrowserProxy());
} }
static setInstance(obj: BrowserProxy) { static setInstance(proxy: BrowserProxy) {
instance = obj; instance = proxy;
} }
} }
@ -723,13 +742,13 @@ response from the browser.
**chrome/browser/resources/donuts/donuts.ts** **chrome/browser/resources/donuts/donuts.ts**
```js ```js
import {BrowserProxy} from './browser_proxy.js'; import {BrowserProxyImpl} from './browser_proxy.js';
let numDonutsBaked: number = 0; let numDonutsBaked: number = 0;
window.onload = function() { window.onload = function() {
// Other page initialization steps go here // Other page initialization steps go here
const proxy = BrowserProxy.getInstance(); const proxy = BrowserProxyImpl.getInstance();
// Tells the browser to start the pilot light. // Tells the browser to start the pilot light.
proxy.handler.startPilotLight(); proxy.handler.startPilotLight();
// Adds a listener for the asynchronous "donutsBaked" event. // Adds a listener for the asynchronous "donutsBaked" event.
@ -742,7 +761,7 @@ window.onload = function() {
function CheckNumberOfDonuts() { function CheckNumberOfDonuts() {
// Requests the number of donuts from the browser, and alerts with the // Requests the number of donuts from the browser, and alerts with the
// response. // response.
BrowserProxy.getInstance().handler.getNumberOfDonuts().then( BrowserProxyImpl.getInstance().handler.getNumberOfDonuts().then(
(numDonuts: number) => { (numDonuts: number) => {
alert('Yay, there are ' + numDonuts + ' delicious donuts left!'); alert('Yay, there are ' + numDonuts + ' delicious donuts left!');
}); });
@ -750,7 +769,7 @@ function CheckNumberOfDonuts() {
function BakeDonuts(numDonuts: number) { function BakeDonuts(numDonuts: number) {
// Tells the browser to bake |numDonuts| donuts. // Tells the browser to bake |numDonuts| donuts.
BrowserProxy.getInstance().handler.bakeDonuts(numDonuts); BrowserProxyImpl.getInstance().handler.bakeDonuts(numDonuts);
} }
``` ```