John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 1 | <style> |
| 2 | .note::before { |
| 3 | content: 'Note: '; |
| 4 | font-variant: small-caps; |
| 5 | font-style: italic; |
| 6 | } |
| 7 | |
| 8 | .doc h1 { |
| 9 | margin: 0; |
| 10 | } |
| 11 | </style> |
| 12 | |
| 13 | # Creating WebUI Interfaces outside components/ |
Toby Huang | d7752f9 | 2021-05-06 16:37:34 | [diff] [blame] | 14 | This guide is based on [Creating WebUI Interfaces in components](webui_in_components.md), and comments from reviewers when creating the ChromeOS emoji picker. |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 15 | |
| 16 | [TOC] |
| 17 | |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 18 | WebUI pages live in `chrome/browser/resources`. You should create a folder for your project `chrome/browser/resources/hello_world`. |
John Palmer | 046f987 | 2021-05-24 01:24:56 | [diff] [blame] | 19 | When creating WebUI resources, follow the [Web Development Style Guide](https://chromium.googlesource.com/chromium/src/+/main/styleguide/web/web.md). For a sample WebUI page you could start with the following files: |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 20 | |
Ryan Lester | bb99260 | 2021-07-15 15:36:03 | [diff] [blame] | 21 | `chrome/browser/resources/hello_world/hello_world_container.html` |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 22 | ```html |
| 23 | <!DOCTYPE HTML> |
| 24 | <html> |
Ryan Lester | bb99260 | 2021-07-15 15:36:03 | [diff] [blame] | 25 | <meta charset="utf-8"> |
| 26 | <link rel="stylesheet" href="hello_world.css"> |
| 27 | <hello-world></hello-world> |
| 28 | <script type="module" src="hello_world.js"></script> |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 29 | </html> |
| 30 | ``` |
| 31 | |
| 32 | `chrome/browser/resources/hello_world/hello_world.css` |
| 33 | ```css |
| 34 | body { |
| 35 | margin: 0; |
| 36 | } |
| 37 | ``` |
| 38 | |
Ryan Lester | bb99260 | 2021-07-15 15:36:03 | [diff] [blame] | 39 | `chrome/browser/resources/hello_world/hello_world.html` |
| 40 | ```html |
| 41 | <h1>Hello World</h1> |
| 42 | <div id="example-div">[[message_]]</div> |
| 43 | ``` |
| 44 | |
rbpotter | acc480cd | 2022-03-04 08:42:19 | [diff] [blame] | 45 | `chrome/browser/resources/hello_world/hello_world.ts` |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 46 | ```js |
| 47 | import './strings.m.js'; |
| 48 | |
| 49 | import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js'; |
rbpotter | acc480cd | 2022-03-04 08:42:19 | [diff] [blame] | 50 | import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; |
| 51 | import {getTemplate} from './hello_world.html.js'; |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 52 | |
Ryan Lester | bb99260 | 2021-07-15 15:36:03 | [diff] [blame] | 53 | export class HelloWorldElement extends PolymerElement { |
| 54 | static get is() { |
| 55 | return 'hello-world'; |
| 56 | } |
| 57 | |
| 58 | static get template() { |
rbpotter | acc480cd | 2022-03-04 08:42:19 | [diff] [blame] | 59 | return getTemplate(); |
Ryan Lester | bb99260 | 2021-07-15 15:36:03 | [diff] [blame] | 60 | } |
| 61 | |
| 62 | static get properties() { |
| 63 | return { |
| 64 | message_: { |
| 65 | type: String, |
| 66 | value: () => loadTimeData.getString('message'), |
| 67 | }, |
| 68 | }; |
| 69 | } |
rbpotter | acc480cd | 2022-03-04 08:42:19 | [diff] [blame] | 70 | |
| 71 | private message_: string; |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 72 | } |
| 73 | |
Ryan Lester | bb99260 | 2021-07-15 15:36:03 | [diff] [blame] | 74 | customElements.define(HelloWorldElement.is, HelloWorldElement); |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 75 | ``` |
| 76 | |
rbpotter | acc480cd | 2022-03-04 08:42:19 | [diff] [blame] | 77 | Add a 'tsconfig_base.json' file to configure TypeScript options. Typical options |
| 78 | needed by Polymer UIs include noUncheckedIndexAccess, noUnusedLocals, and |
| 79 | strictPropertyInitialization, all set to false. |
| 80 | |
| 81 | `chrome/browser/resources/hello_world/tsconfig_base.gn` |
| 82 | ``` |
| 83 | { |
| 84 | "extends": "../../../../tools/typescript/tsconfig_base.json", |
| 85 | "compilerOptions": { |
| 86 | "noUncheckedIndexedAccess": false, |
| 87 | "noUnusedLocals": false, |
| 88 | "strictPropertyInitialization": false |
| 89 | } |
| 90 | } |
| 91 | ``` |
| 92 | |
| 93 | Add a `BUILD.gn` file to get TypeScript compilation and to generate the JS file |
| 94 | from which the template will be imported. |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 95 | |
| 96 | `chrome/browser/resources/hello_world/BUILD.gn` |
| 97 | ``` |
rbpotter | acc480cd | 2022-03-04 08:42:19 | [diff] [blame] | 98 | import("//tools/polymer/html_to_wrapper.gni") |
| 99 | import("//tools/grit/preprocess_if_expr.gni") |
| 100 | import("//tools/typescript/ts_library.gni") |
Ryan Lester | bb99260 | 2021-07-15 15:36:03 | [diff] [blame] | 101 | |
rbpotter | acc480cd | 2022-03-04 08:42:19 | [diff] [blame] | 102 | html_to_wrapper("html_wrapper_files") { |
| 103 | in_files = [ "hello_world.html" ] |
Ryan Lester | bb99260 | 2021-07-15 15:36:03 | [diff] [blame] | 104 | } |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 105 | |
rbpotter | acc480cd | 2022-03-04 08:42:19 | [diff] [blame] | 106 | # Move everything to one folder using preprocess_if_expr. |
| 107 | preprocess_folder = "preprocessed" |
| 108 | |
| 109 | preprocess_if_expr("preprocess_generated") { |
| 110 | # This file is generated by html_to_wrapper(). |
| 111 | in_files = [ "hello_world.html.ts" ] |
| 112 | in_folder = target_gen_dir |
| 113 | out_folder = "$target_gen_dir/$preprocess_folder" |
| 114 | deps = [ ":html_wrapper_files" ] |
| 115 | } |
| 116 | |
| 117 | preprocess_if_expr("preprocess") { |
| 118 | in_files = [ "hello_world.ts" ] |
| 119 | in_folder = "." |
| 120 | out_folder = "$target_gen_dir/$preprocess_folder" |
| 121 | } |
| 122 | |
| 123 | ts_library("build_ts") { |
| 124 | root_dir = "$target_gen_dir/$preprocess_folder" |
| 125 | out_dir = "$target_gen_dir/tsc" |
| 126 | tsconfig_base = "tsconfig_base.json" |
| 127 | in_files = [ |
| 128 | "hello_world.ts", |
| 129 | "hello_world.html.ts" |
| 130 | ] |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 131 | deps = [ |
rbpotter | acc480cd | 2022-03-04 08:42:19 | [diff] [blame] | 132 | "//third_party/polymer/v3_0:library," |
| 133 | "//ui/webui/resources:library", |
| 134 | ] |
| 135 | extra_deps = [ |
| 136 | ":preprocess", |
| 137 | ":preprocess_generated", |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 138 | ] |
| 139 | } |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 140 | ``` |
| 141 | |
| 142 | Finally, create an `OWNERS` file for the new folder. |
| 143 | |
| 144 | ## Adding the resources |
Ryan Lester | bb99260 | 2021-07-15 15:36:03 | [diff] [blame] | 145 | Resources for the browser are stored in `grd` files. Current best practice is to autogenerate a grd file for your |
rbpotter | acc480cd | 2022-03-04 08:42:19 | [diff] [blame] | 146 | component in the `BUILD` file we created earlier. See new content below: |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 147 | |
rbpotter | acc480cd | 2022-03-04 08:42:19 | [diff] [blame] | 148 | `chrome/browser/resources/hello_world/BUILD.gn new additions` |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 149 | ``` |
| 150 | import("//tools/grit/grit_rule.gni") |
| 151 | import("//ui/webui/resources/tools/generate_grd.gni") |
| 152 | |
| 153 | resources_grd_file = "$target_gen_dir/resources.grd" |
Ryan Lester | bb99260 | 2021-07-15 15:36:03 | [diff] [blame] | 154 | |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 155 | generate_grd("build_grd") { |
| 156 | grd_prefix = "hello_world" |
| 157 | out_grd = resources_grd_file |
| 158 | input_files = [ |
| 159 | "hello_world.css", |
Ryan Lester | bb99260 | 2021-07-15 15:36:03 | [diff] [blame] | 160 | "hello_world_container.html", |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 161 | ] |
| 162 | input_files_base_dir = rebase_path(".", "//") |
rbpotter | acc480cd | 2022-03-04 08:42:19 | [diff] [blame] | 163 | deps = [ ":build_ts" ] |
| 164 | manifest_files = [ "$target_gen_dir/$tsconfig.manifest" ] |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 165 | } |
| 166 | |
| 167 | grit("resources") { |
| 168 | enable_input_discovery_for_gn_analyze = false |
| 169 | source = resources_grd_file |
| 170 | deps = [ ":build_grd" ] |
| 171 | outputs = [ |
| 172 | "grit/hello_world_resources.h", |
| 173 | "grit/hello_world_resources_map.cc", |
| 174 | "grit/hello_world_resources_map.h", |
| 175 | "hello_world_resources.pak", |
| 176 | ] |
| 177 | output_dir = "$root_gen_dir/chrome" |
| 178 | } |
| 179 | ``` |
| 180 | |
| 181 | Then add the new resource target to `chrome/browser/resources/BUILD.gn` |
| 182 | ``` |
| 183 | group("resources") { |
| 184 | public_deps += [ |
| 185 | ... |
| 186 | "hello_world:resources" |
| 187 | ... |
| 188 | ] |
| 189 | } |
| 190 | ``` |
| 191 | |
Toby Huang | d7752f9 | 2021-05-06 16:37:34 | [diff] [blame] | 192 | Also add to `chrome/chrome_paks.gni` |
| 193 | |
| 194 | ``` |
| 195 | template("chrome_extra_paks") { |
| 196 | ... (lots) |
| 197 | sources += [ |
| 198 | ... |
| 199 | "$root_gen_dir/chrome/hello_world_resources.pak", |
| 200 | ... |
| 201 | ] |
| 202 | deps += [ |
| 203 | ... |
| 204 | "//chrome/browser/resources/hello_world:resources", |
| 205 | ... |
| 206 | ] |
| 207 | } |
| 208 | ``` |
| 209 | |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 210 | ## Adding URL constants for the new chrome URL |
| 211 | |
| 212 | `chrome/common/webui_url_constants.cc:` |
| 213 | ```c++ |
| 214 | const char kChromeUIHelloWorldURL[] = "chrome://hello-world/"; |
| 215 | const char kChromeUIHelloWorldHost[] = "hello-world"; |
| 216 | ``` |
| 217 | |
| 218 | `chrome/common/webui_url_constants.h:` |
| 219 | ```c++ |
| 220 | extern const char kChromeUIHelloWorldURL[]; |
| 221 | extern const char kChromeUIHelloWorldHost[]; |
| 222 | ``` |
| 223 | |
| 224 | ## Adding a WebUI class for handling requests to the chrome://hello-world/ URL |
| 225 | Next we need a class to handle requests to this new resource URL. Typically this will subclass `WebUIController` (WebUI |
| 226 | dialogs will also need another class which will subclass `WebDialogDelegate`, this is shown later). |
| 227 | |
| 228 | `chrome/browser/ui/webui/hello_world_ui.h` |
| 229 | ```c++ |
| 230 | #ifndef CHROME_BROWSER_UI_WEBUI_HELLO_WORLD_HELLO_WORLD_H_ |
| 231 | #define CHROME_BROWSER_UI_WEBUI_HELLO_WORLD_HELLO_WORLD_H_ |
| 232 | |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 233 | #include "content/public/browser/web_ui_controller.h" |
| 234 | |
| 235 | // The WebUI for chrome://hello-world |
| 236 | class HelloWorldUI : public content::WebUIController { |
| 237 | public: |
| 238 | explicit HelloWorldUI(content::WebUI* web_ui); |
| 239 | ~HelloWorldUI() override; |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 240 | }; |
| 241 | |
| 242 | #endif // CHROME_BROWSER_UI_WEBUI_HELLO_WORLD_HELLO_WORLD_H_ |
| 243 | ``` |
| 244 | |
| 245 | `chrome/browser/ui/webui/hello_world_ui.cc` |
| 246 | ```c++ |
| 247 | #include "chrome/browser/ui/webui/hello_world_ui.h" |
| 248 | |
| 249 | #include "chrome/browser/ui/webui/webui_util.h" |
Toby Huang | d7752f9 | 2021-05-06 16:37:34 | [diff] [blame] | 250 | #include "chrome/common/webui_url_constants.h" |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 251 | #include "content/public/browser/browser_context.h" |
| 252 | #include "content/public/browser/web_contents.h" |
Toby Huang | d7752f9 | 2021-05-06 16:37:34 | [diff] [blame] | 253 | #include "chrome/grit/hello_world_resources.h" |
| 254 | #include "chrome/grit/hello_world_resources_map.h" |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 255 | #include "content/public/browser/web_ui.h" |
| 256 | #include "content/public/browser/web_ui_data_source.h" |
| 257 | |
| 258 | |
| 259 | HelloWorldUI::HelloWorldUI(content::WebUI* web_ui) |
| 260 | : content::WebUIController(web_ui) { |
| 261 | // Set up the chrome://hello-world source. |
| 262 | content::WebUIDataSource* html_source = |
| 263 | content::WebUIDataSource::Create(chrome::kChromeUIHelloWorldHost); |
| 264 | |
| 265 | // As a demonstration of passing a variable for JS to use we pass in some |
| 266 | // a simple message. |
| 267 | html_source->AddString("message", "Hello World!"); |
| 268 | html_source->UseStringsJs(); |
| 269 | |
| 270 | // Add required resources. |
Lei Zhang | 6b01c7c | 2021-11-16 16:39:41 | [diff] [blame] | 271 | webui::SetupWebUIDataSource( |
| 272 | html_source, |
| 273 | base::make_span(kHelloWorldResources, kHelloWorldResourcesSize), |
| 274 | IDR_HELLO_WORLD_HELLO_WORLD_CONTAINER_HTML); |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 275 | |
| 276 | content::BrowserContext* browser_context = |
| 277 | web_ui->GetWebContents()->GetBrowserContext(); |
| 278 | content::WebUIDataSource::Add(browser_context, html_source); |
| 279 | } |
| 280 | |
Toby Huang | d7752f9 | 2021-05-06 16:37:34 | [diff] [blame] | 281 | HelloWorldUI::~HelloWorldUI() = default; |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 282 | ``` |
| 283 | |
| 284 | To ensure that your code actually gets compiled, you need to add it to `chrome/browser/ui/BUILD.gn`: |
| 285 | |
| 286 | ``` |
| 287 | static_library("ui") { |
| 288 | sources = [ |
| 289 | ... (lots) |
Toby Huang | d7752f9 | 2021-05-06 16:37:34 | [diff] [blame] | 290 | "webui/hello_world_ui.cc", |
| 291 | "webui/hello_world_ui.h", |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 292 | ``` |
| 293 | |
| 294 | ## Adding your WebUI request handler to the Chrome WebUI factory |
| 295 | |
| 296 | The Chrome WebUI factory is where you setup your new request handler. |
| 297 | |
| 298 | `chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc:` |
| 299 | ```c++ |
| 300 | + #include "chrome/browser/ui/webui/hello_world_ui.h" |
| 301 | ... |
| 302 | + if (url.host() == chrome::kChromeUIHelloWorldHost) |
| 303 | + return &NewWebUI<HelloWorldUI>; |
| 304 | ``` |
| 305 | |
Toby Huang | d7752f9 | 2021-05-06 16:37:34 | [diff] [blame] | 306 | ## Add an entry to resource_ids.spec |
Ryan Lester | dcf8f42 | 2021-07-07 15:36:18 | [diff] [blame] | 307 | This file is for automatically generating resource ids. Ensure that your entry |
| 308 | has a unique ID and preserves numerical ordering. |
Toby Huang | d7752f9 | 2021-05-06 16:37:34 | [diff] [blame] | 309 | |
| 310 | `tools/gritsettings/resource_ids.spec` |
| 311 | |
| 312 | ``` |
| 313 | # START chrome/ WebUI resources section |
| 314 | ... (lots) |
| 315 | "<(SHARED_INTERMEDIATE_DIR)/chrome/browser/resources/hello_world/resources.grd": { |
| 316 | "META": {"sizes": {"includes": [5]}}, |
| 317 | "includes": [2085], |
| 318 | }, |
| 319 | ``` |
| 320 | |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 321 | ## Check everything works |
| 322 | |
| 323 | You're done! Assuming no errors (because everyone gets their code perfect the first time) you should be able to compile |
| 324 | and run chrome and navigate to `chrome://hello-world/` and see your nifty welcome text! |
| 325 | |
| 326 | |
| 327 | ## Making a WebUI Dialog |
| 328 | |
| 329 | Instead of having a full page for your WebUI, you might want a dialog in order to have a fully independent window. To |
| 330 | do that, some small changes are needed to your code. First, we need to add a new class which inherits from |
| 331 | `ui::WebDialogDelegate`. The easiest way to do that is to edit the `hello_world_ui.*` files |
| 332 | |
| 333 | |
| 334 | `chrome/browser/ui/webui/hello_world_ui.h` |
| 335 | ```c++ |
| 336 | // Leave the old content, but add this new code |
| 337 | class HelloWorldDialog : public ui::WebDialogDelegate { |
| 338 | public: |
| 339 | static void Show(); |
| 340 | ~HelloWorldDialog() override; |
Toby Huang | d7752f9 | 2021-05-06 16:37:34 | [diff] [blame] | 341 | HelloWorldDialog(const HelloWorldDialog&) = delete; |
| 342 | HelloWorldDialog& operator=(const HelloWorldDialog&) = delete; |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 343 | |
| 344 | private: |
| 345 | HelloWorldDialog(); |
| 346 | // ui::WebDialogDelegate: |
| 347 | ui::ModalType GetDialogModalType() const override; |
Jan Wilken Dörrie | 85285b0 | 2021-03-11 23:38:47 | [diff] [blame] | 348 | std::u16string GetDialogTitle() const override; |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 349 | GURL GetDialogContentURL() const override; |
| 350 | void GetWebUIMessageHandlers( |
| 351 | std::vector<content::WebUIMessageHandler*>* handlers) const override; |
| 352 | void GetDialogSize(gfx::Size* size) const override; |
| 353 | std::string GetDialogArgs() const override; |
| 354 | void OnDialogShown(content::WebUI* webui) override; |
| 355 | void OnDialogClosed(const std::string& json_retval) override; |
| 356 | void OnCloseContents(content::WebContents* source, |
| 357 | bool* out_close_dialog) override; |
| 358 | bool ShouldShowDialogTitle() const override; |
| 359 | |
| 360 | content::WebUI* webui_ = nullptr; |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 361 | }; |
| 362 | ``` |
| 363 | |
| 364 | `chrome/browser/ui/webui/hello_world_ui.cc` |
| 365 | ```c++ |
| 366 | // Leave the old content, but add this new stuff |
| 367 | |
Toby Huang | d7752f9 | 2021-05-06 16:37:34 | [diff] [blame] | 368 | HelloWorldDialog::HelloWorldDialog() = default; |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 369 | |
| 370 | void HelloWorldDialog::Show() { |
Lei Zhang | 6b01c7c | 2021-11-16 16:39:41 | [diff] [blame] | 371 | // HelloWorldDialog is self-deleting via OnDialogClosed(). |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 372 | chrome::ShowWebDialog(nullptr, ProfileManager::GetActiveUserProfile(), |
| 373 | new HelloWorldDialog()); |
| 374 | } |
| 375 | |
| 376 | ui::ModalType HelloWorldDialog::GetDialogModalType() const { |
| 377 | return ui::MODAL_TYPE_NONE; |
| 378 | } |
| 379 | |
Jan Wilken Dörrie | 85285b0 | 2021-03-11 23:38:47 | [diff] [blame] | 380 | std::u16string HelloWorldDialog::GetDialogTitle() const { |
Jan Wilken Dörrie | 2c470ea | 2021-03-22 22:26:24 | [diff] [blame] | 381 | return u"Hello world"; |
John Palmer | d3225c2 | 2020-12-17 00:33:17 | [diff] [blame] | 382 | } |
| 383 | |
| 384 | GURL HelloWorldDialog::GetDialogContentURL() const { |
| 385 | return GURL(chrome::kChromeUIHelloWorldURL[); |
| 386 | } |
| 387 | |
| 388 | void HelloWorldDialog::GetWebUIMessageHandlers( |
| 389 | std::vector<content::WebUIMessageHandler*>* handlers) const {} |
| 390 | |
| 391 | void HelloWorldDialog::GetDialogSize(gfx::Size* size) const { |
| 392 | const int kDefaultWidth = 544; |
| 393 | const int kDefaultHeight = 628; |
| 394 | size->SetSize(kDefaultWidth, kDefaultHeight); |
| 395 | } |
| 396 | |
| 397 | std::string HelloWorldDialog::GetDialogArgs() const { |
| 398 | return ""; |
| 399 | } |
| 400 | |
| 401 | void HelloWorldDialog::OnDialogShown(content::WebUI* webui) { |
| 402 | webui_ = webui; |
| 403 | } |
| 404 | |
| 405 | void HelloWorldDialog::OnDialogClosed(const std::string& json_retval) { |
| 406 | delete this; |
| 407 | } |
| 408 | |
| 409 | void HelloWorldDialog::OnCloseContents(content::WebContents* source, |
| 410 | bool* out_close_dialog) { |
| 411 | *out_close_dialog = true; |
| 412 | } |
| 413 | |
| 414 | bool HelloWorldDialog::ShouldShowDialogTitle() const { |
| 415 | return true; |
| 416 | } |
| 417 | |
| 418 | HelloWorldDialog::~HelloWorldDialog() = default; |
| 419 | ``` |
| 420 | |
| 421 | Finally, you will need to do something to actually show your dialog, which can be done by calling `HelloWorldDialog::Show()`. |