android_webview
apps
ash
base
build
build_overrides
buildtools
cc
chrome
chromecast
chromeos
cloud_print
codelabs
components
content
courgette
crypto
dbus
device
docs
accessibility
autofill
design
enterprise
fuchsia
gpu
images
infra
ios
linux
login
mac
media
memory
memory-infra
patterns
privacy
privacy_budget
process
security
speed
speed_metrics
sync
testing
ui
updater
workflow
DIR_METADATA
OWNERS
README.md
accessibility.md
ad_tagging.md
adding_to_third_party.md
android_accessing_cpp_enums_in_java.md
android_accessing_cpp_features_in_java.md
android_accessing_cpp_switches_in_java.md
android_build_instructions.md
android_cast_build_instructions.md
android_debugging_instructions.md
android_dynamic_feature_modules.md
android_emulator.md
android_logging.md
android_native_libraries.md
android_studio.md
angle_in_chromium.md
asan.md
atom.md
bitmap_pipeline.md
branch_sheriff.md
building_old_revisions.md
callback.md
ccache_mac.md
chrome_os_logging.md
chrome_settings.md
chrome_untrusted.md
chromedriver_status.md
chromeos_build_instructions.md
chromeos_glossary.md
chromium_browser_vs_google_chrome.md
chromoting_android_hacking.md
cipd_and_3pp.md
cl_respect.md
clang.md
clang_code_coverage_wrapper.md
clang_format.md
clang_sheriffing.md
clang_static_analyzer.md
clang_tidy.md
clang_tool_refactoring.md
clangd.md
clion.md
closure_compilation.md
cocoa_tips_and_tricks.md
code_review_owners.md
code_reviews.md
commit_checklist.md
component_build.md
configuration.md
contributing.md
cr_respect.md
cr_user_manual.md
cross_platform_ui.md
cygwin_dll_remapping_failure.md
dbus_mojo_connection_service.md
debugging_with_crash_keys.md
deterministic_builds.md
disassemble_code.md
documentation_best_practices.md
documentation_guidelines.md
early-hints.md
eclipse.md
emacs.md
erc_irc.md
flag_expiry.md
flag_ownership.md
gdbinit.md
get_the_code.md
git_cookbook.md
git_tips.md
google_chrome_branded_builds.md
google_play_services.md
graphical_debugging_aid_chromium_views.md
gwp_asan.md
how_cc_works.md
how_to_add_your_feature_flag.md
how_to_extend_web_test_framework.md
idn.md
initialize_blink_features.md
inlined_stack_traces.md
installation_at_vmware.md
ios_build_instructions.md
ios_infra.md
ios_voiceover.md
kiosk_mode.md
lacros.md
lldbinit.md
luci_migration_faq.md
mac_arm64.md
mac_build_instructions.md
mac_lld.md
mojo_and_services.md
mojo_ipc_conversion.md
native_relocations.md
navigation-request-navigation-state.gv
navigation-request-navigation-state.png
navigation.md
navigation_concepts.md
network_traffic_annotations.md
new_port_policy.md
no_sources_assignment_filter.md
old_chromoting_build_instructions.md
optimizing_web_uis.md
optional.md
origin_trials_integration.md
ozone_overview.md
parsing_test_results.md
pgo.md
piranha_plant.md
profiling.md
profiling_content_shell_on_android.md
proxy_auto_config.md
qtcreator.md
release_branch_guidance.md
render-frame-host-lifecycle-state.gv
render-frame-host-lifecycle-state.png
render_document.md
seccomp_sandbox_crash_dumping.md
servicification.md
session_history.md
sheriff.md
shutdown.md
special_case_urls.md
static_initializers.md
sublime_ide.md
system_hardening_features.md
tab_helpers.md
threading_and_tasks.md
threading_and_tasks_faq.md
threading_and_tasks_testing.md
toolchain_support.md
tour_of_luci_ui.md
tpm_quick_ref.md
translation_screenshots.md
trusted_types_on_webui.md
updating_clang.md
updating_clang_format_binaries.md
use_counter_wiki.md
useful_urls.md
user_data_dir.md
user_data_storage.md
user_handle_mapping.md
using_build_runner.md
vanilla_msysgit_workflow.md
vscode.md
vscode_python.md
webui_explainer.md
webui_in_chrome.md
webui_in_components.md
webview_policies.md
win_cross.md
win_order_files.md
windows_build_instructions.md
windows_native_window_occlusion_tracking.md
windows_pwa_integration.md
windows_shortcut_and_taskbar_handling.md
windows_split_dll.md
windows_virtual_desktop_handling.md
wmax_tokens.md
working_remotely_with_android.md
writing_clang_plugins.md
extensions
fuchsia
gin
google_apis
google_update
gpu
headless
infra
ios
ipc
jingle
media
mojo
native_client_sdk
net
pdf
ppapi
printing
remoting
rlz
sandbox
services
skia
sql
storage
styleguide
testing
third_party
tools
ui
url
weblayer
.clang-format
.clang-tidy
.eslintrc.js
.git-blame-ignore-revs
.gitattributes
.gitignore
.gn
.vpython
.vpython3
.yapfignore
AUTHORS
BUILD.gn
CODE_OF_CONDUCT.md
DEPS
DIR_METADATA
ENG_REVIEW_OWNERS
LICENSE
LICENSE.chromium_os
OWNERS
PRESUBMIT.py
PRESUBMIT_test.py
PRESUBMIT_test_mocks.py
README.md
WATCHLISTS
codereview.settings

Specifically: - Update remaining usages of js/util.js to explicitly pull-in js/assert.js. - Update webui_resources.grd to not use flattenhtml=true for JS files. - Remove "// <include src=..." directives from affected files This is in preparation of moving such files over to the auto-generated webui_generated_resources.grd, which (purposefully) no longer supports flattenhtml. Bug: 1132403 Change-Id: Idb9e4df54348c3eac3cbe7152893e3d5c5bd82a6 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2503833 Commit-Queue: Guido Urdaneta <guidou@chromium.org> Auto-Submit: dpapad <dpapad@chromium.org> Reviewed-by: Guido Urdaneta <guidou@chromium.org> Reviewed-by: Rebekah Potter <rbpotter@chromium.org> Cr-Commit-Position: refs/heads/master@{#823308}
329 lines
11 KiB
Markdown
329 lines
11 KiB
Markdown
<style>
|
|
.note::before {
|
|
content: 'Note: ';
|
|
font-variant: small-caps;
|
|
font-style: italic;
|
|
}
|
|
|
|
.doc h1 {
|
|
margin: 0;
|
|
}
|
|
</style>
|
|
|
|
# Creating WebUI Interfaces in `components/`
|
|
|
|
To create a WebUI interface in `components/` you need to follow different steps from [Creating WebUI Interfaces in `chrome/`](https://www.chromium.org/developers/webui). This guide is specific to creating a WebUI interface in `src/components/`. It is based on the steps I went through to create the WebUI infrastructure for chrome://safe-browsing in 'src/components/safe_browsing/content/web_ui/'.
|
|
|
|
[TOC]
|
|
|
|
<a name="creating_webui_page"></a>
|
|
## Creating the WebUI page
|
|
|
|
WebUI resources in `components/` will be added in your specific project folder. Create a project folder `src/components/hello_world/`. When creating WebUI resources, follow the [Web Development Style Guide](https://chromium.googlesource.com/chromium/src/+/master/styleguide/web/web.md). For a sample WebUI page you could start with the following files:
|
|
|
|
`src/components/hello_world/hello_world.html:`
|
|
```html
|
|
<!DOCTYPE HTML>
|
|
<html dir="$i18n{textdirection}">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>$i18n{helloWorldTitle}</title>
|
|
<link rel="stylesheet" href="hello_world.css">
|
|
<script src="chrome://resources/js/cr.js"></script>
|
|
<script src="chrome://resources/js/load_time_data.js"></script>
|
|
<script src="chrome://resources/js/assert.js"></script>
|
|
<script src="chrome://resources/js/util.js"></script>
|
|
<script src="strings.js"></script>
|
|
<script src="hello_world.js"></script>
|
|
</head>
|
|
<body style="font-family:$i18n{fontfamily};font-size:$i18n{fontSize}">
|
|
<h1>$i18n{helloWorldTitle}</h1>
|
|
<p id="welcome-message"></p>
|
|
</body>
|
|
</html>
|
|
```
|
|
|
|
`src/components/hello_world/hello_world.css:`
|
|
```css
|
|
p {
|
|
white-space: pre-wrap;
|
|
}
|
|
```
|
|
|
|
`src/components/hello_world/hello_world.js:`
|
|
```js
|
|
cr.define('hello_world', function() {
|
|
'use strict';
|
|
|
|
/**
|
|
* Be polite and insert translated hello world strings for the user on loading.
|
|
*/
|
|
function initialize() {
|
|
$('welcome-message').textContent = loadTimeData.getStringF('welcomeMessage',
|
|
loadTimeData.getString('userName'));
|
|
}
|
|
|
|
// Return an object with all of the exports.
|
|
return {
|
|
initialize: initialize,
|
|
};
|
|
});
|
|
|
|
document.addEventListener('DOMContentLoaded', hello_world.initialize);
|
|
```
|
|
|
|
## Adding the resources
|
|
|
|
Resource files are specified in a `.grdp` file. Here's our
|
|
`components/resources/hello_world_resources.grdp`:
|
|
|
|
```xml
|
|
<?xml version="1.0" encoding="utf-8"?>
|
|
<grit-part>
|
|
<include name="IDR_HELLO_WORLD_HTML" file="../../components/hello_world/hello_world.html" type="BINDATA" />
|
|
<include name="IDR_HELLO_WORLD_CSS" file="../../components/hello_world/hello_world.css" type="BINDATA" />
|
|
<include name="IDR_HELLO_WORLD_JS" file="../../components/hello_world/hello_world.js" type="BINDATA" />
|
|
</grit-part>
|
|
```
|
|
|
|
Add the created file in `components/resources/dev_ui_components_resources.grd`:
|
|
|
|
```xml
|
|
+<part file="hello_world_resources.grdp" />
|
|
```
|
|
|
|
## Adding URL constants for the new chrome URL
|
|
|
|
Create the `constants.cc` and `constants.h` files to add the URL constants. This is where you will add the URL or URL's which will be directed to your new resources.
|
|
|
|
`src/components/hello_world/constants.cc:`
|
|
```c++
|
|
const char kChromeUIHelloWorldURL[] = "chrome://hello-world/";
|
|
const char kChromeUIHelloWorldHost[] = "hello-world";
|
|
```
|
|
|
|
`src/components/hello_world/constants.h:`
|
|
```c++
|
|
extern const char kChromeUIHelloWorldURL[];
|
|
extern const char kChromeUIHelloWorldHost[];
|
|
```
|
|
|
|
## Adding localized strings
|
|
|
|
We need a few string resources for translated strings to work on the new resource. The welcome message contains a variable with a sample value so that it can be accurately translated. Create a new file `components/hello_world_strings.grdp`. You can add the messages as follow:
|
|
|
|
```xml
|
|
<message name="IDS_HELLO_WORLD_TITLE" desc="A happy message saying hello to the world">
|
|
Hello World!
|
|
</message>
|
|
<message name="IDS_HELLO_WORLD_WELCOME_TEXT" desc="Message welcoming the user to the hello world page">
|
|
Welcome to this fancy Hello World page <ph name="WELCOME_NAME">$1<ex>Chromium User</ex></ph>!
|
|
</message>
|
|
```
|
|
Add the created file in `components/components_strings.grd`:
|
|
|
|
```xml
|
|
+<part file="hello_world_strings.grdp" />
|
|
```
|
|
|
|
## Adding a WebUI class for handling requests to the chrome://hello-world/ URL
|
|
|
|
Next we need a class to handle requests to this new resource URL. Typically this will subclass `ChromeWebUI` (but WebUI dialogs should subclass `HtmlDialogUI` instead).
|
|
|
|
`src/components/hello_world/hello_world_ui.h:`
|
|
```c++
|
|
#ifndef COMPONENTS_HELLO_WORLD_HELLO_WORLD_UI_H_
|
|
#define COMPONENTS_HELLO_WORLD_HELLO_WORLD_UI_H_
|
|
#pragma once
|
|
|
|
#include "base/macros.h"
|
|
#include "content/public/browser/web_ui_controller.h"
|
|
|
|
// The WebUI for chrome://hello-world
|
|
class HelloWorldUI : public content::WebUIController {
|
|
public:
|
|
explicit HelloWorldUI(content::WebUI* web_ui);
|
|
HelloWorldUI(const HelloWorldUI&) = delete;
|
|
HelloWorldUI& operator=(const HelloWorldUI&) = delete;
|
|
~HelloWorldUI() override;
|
|
private:
|
|
};
|
|
|
|
#endif // COMPONENTS_HELLO_WORLD_HELLO_WORLD_UI_H_
|
|
```
|
|
|
|
`src/components/hello_world/hello_world_ui.cc:`
|
|
```c++
|
|
#include "components/hello_world/hello_world_ui.h"
|
|
|
|
#include "components/grit/components_scaled_resources.h"
|
|
#include "components/grit/dev_ui_components_resources.h"
|
|
#include "components/hello_world/constants.h"
|
|
#include "components/strings/grit/components_strings.h"
|
|
#include "content/public/browser/browser_context.h"
|
|
#include "content/public/browser/web_contents.h"
|
|
#include "content/public/browser/web_ui.h"
|
|
#include "content/public/browser/web_ui_data_source.h"
|
|
|
|
HelloWorldUI::HelloWorldUI(content::WebUI* web_ui)
|
|
: content::WebUIController(web_ui) {
|
|
// Set up the chrome://hello-world source.
|
|
content::WebUIDataSource* html_source =
|
|
content::WebUIDataSource::Create(chrome::kChromeUIHelloWorldHost);
|
|
|
|
// Localized strings.
|
|
html_source->AddLocalizedString("helloWorldTitle", IDS_HELLO_WORLD_TITLE);
|
|
html_source->AddLocalizedString("welcomeMessage", IDS_HELLO_WORLD_WELCOME_TEXT);
|
|
|
|
// As a demonstration of passing a variable for JS to use we pass in the name "Bob".
|
|
html_source->AddString("userName", "Bob");
|
|
html_source->UseStringsJs();
|
|
|
|
// Add required resources.
|
|
html_source->AddResourcePath("hello_world.css", IDR_HELLO_WORLD_CSS);
|
|
html_source->AddResourcePath("hello_world.js", IDR_HELLO_WORLD_JS);
|
|
html_source->SetDefaultResource(IDR_HELLO_WORLD_HTML);
|
|
|
|
content::BrowserContext* browser_context =
|
|
web_ui->GetWebContents()->GetBrowserContext();
|
|
content::WebUIDataSource::Add(browser_context, html_source);
|
|
}
|
|
|
|
HelloWorldUI::~HelloWorldUI() {
|
|
}
|
|
```
|
|
|
|
## Adding new sources to Chrome
|
|
|
|
In order for your new class to be built and linked, you need to update the `BUILD.gn` and DEPS files. Create
|
|
|
|
`src/components/hello_world/BUILD.gn:`
|
|
```
|
|
sources = [
|
|
"hello_world_ui.cc",
|
|
"hello_world_ui.h",
|
|
...
|
|
```
|
|
and `src/components/hello_world/DEPS:`
|
|
```
|
|
include_rules = [
|
|
"+components/strings/grit/components_strings.h",
|
|
"+components/grit/components_scaled_resources.h"
|
|
"+components/grit/dev_ui_components_resources.h",
|
|
]
|
|
```
|
|
|
|
## Adding your WebUI request handler to the Chrome WebUI factory
|
|
|
|
The Chrome WebUI factory is where you setup your new request handler.
|
|
|
|
`src/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc:`
|
|
```c++
|
|
+ #include "components/hello_world/hello_world_ui.h"
|
|
+ #include "components/hello_world/constants.h"
|
|
...
|
|
+ if (url.host() == chrome::kChromeUIHelloWorldHost)
|
|
+ return &NewWebUI<HelloWorldUI>;
|
|
```
|
|
|
|
## Testing
|
|
|
|
You're done! Assuming no errors (because everyone gets their code perfect the first time) you should be able to compile and run chrome and navigate to `chrome://hello-world/` and see your nifty welcome text!
|
|
|
|
## Adding a callback handler
|
|
|
|
You probably want your new WebUI page to be able to do something or get information from the C++ world. For this, we use message callback handlers. Let's say that we don't trust the Javascript engine to be able to add two integers together (since we know that it uses floating point values internally). We could add a callback handler to perform integer arithmetic for us.
|
|
|
|
`src/components/hello_world/hello_world_ui.h:`
|
|
```c++
|
|
#include "content/public/browser/web_ui.h"
|
|
+
|
|
+ namespace base {
|
|
+ class ListValue;
|
|
+ } // namespace base
|
|
|
|
// The WebUI for chrome://hello-world
|
|
...
|
|
// Set up the chrome://hello-world source.
|
|
content::WebUIDataSource* html_source = content::WebUIDataSource::Create(hello_world::kChromeUIHelloWorldHost);
|
|
+
|
|
+ // Register callback handler.
|
|
+ RegisterMessageCallback("addNumbers",
|
|
+ base::BindRepeating(&HelloWorldUI::AddNumbers,
|
|
+ base::Unretained(this)));
|
|
|
|
// Localized strings.
|
|
...
|
|
virtual ~HelloWorldUI();
|
|
+
|
|
+ private:
|
|
+ // Add two numbers together using integer arithmetic.
|
|
+ void AddNumbers(const base::ListValue* args);
|
|
};
|
|
```
|
|
|
|
`src/components/hello_world/hello_world_ui.cc:`
|
|
```c++
|
|
#include "components/hello_world/hello_world_ui.h"
|
|
+
|
|
+ #include "base/values.h"
|
|
#include "content/public/browser/browser_context.h"
|
|
...
|
|
HelloWorldUI::~HelloWorldUI() {
|
|
}
|
|
+
|
|
+ void HelloWorldUI::AddNumbers(const base::ListValue* args) {
|
|
+ int term1, term2;
|
|
+ if (!args->GetInteger(0, &term1) || !args->GetInteger(1, &term2))
|
|
+ return;
|
|
+ base::FundamentalValue result(term1 + term2);
|
|
+ AllowJavascript();
|
|
+ std::string callback_id;
|
|
+ args->GetString(0, &callback_id);
|
|
+ ResolveJavascriptCallback(base::Value(callback_id), result);
|
|
+ }
|
|
```
|
|
|
|
`src/components/hello_world/hello_world.js:`
|
|
```c++
|
|
function initialize() {
|
|
+ cr.sendWithPromise('addNumbers', [2, 2]).then((result) =>
|
|
+ addResult(result));
|
|
}
|
|
+
|
|
+ function addResult(result) {
|
|
+ alert('The result of our C++ arithmetic: 2 + 2 = ' + result);
|
|
+ }
|
|
|
|
return {
|
|
initialize: initialize,
|
|
};
|
|
```
|
|
|
|
You'll notice that the call is asynchronous. We must wait for the C++ side to call our Javascript function to get the result.
|
|
|
|
## Creating a WebUI Dialog
|
|
|
|
Some pages have many messages or share code that sends messages. To make possible message handling and/or to create a WebUI dialogue `c++->js` and `js->c++`, follow the guide in [WebUI Explainer](https://chromium.googlesource.com/chromium/src/+/master/docs/webui_explainer.md).
|
|
|
|
## DevUI Pages
|
|
|
|
DevUI pages are WebUI pages intended for developers, and unlikely used by most users. An example is `chrome://bluetooth-internals`. On Android Chrome, these pages are moved to a separate [Dynamic Feature Module (DFM)](https://chromium.googlesource.com/chromium/src/+/master/docs/android_dynamic_feature_modules.md) to reduce binary size. Most WebUI pages are DevUI. This is why in this doc uses `dev_ui_components_resources.{grd, h}` in its examples.
|
|
|
|
`components/` resources that are intended for end users are associated with `components_resources.{grd, h}` and `components_scaled_resorces.{grd, h}`. Use these in place of or inadditional to `dev_ui_components_resources.{grd, h}` if needed.
|
|
|
|
<script>
|
|
let nameEls = Array.from(document.querySelectorAll('[id], a[name]'));
|
|
let names = nameEls.map(nameEl => nameEl.name || nameEl.id);
|
|
|
|
let localLinks = Array.from(document.querySelectorAll('a[href^="#"]'));
|
|
let hrefs = localLinks.map(a => a.href.split('#')[1]);
|
|
|
|
hrefs.forEach(href => {
|
|
if (names.includes(href))
|
|
console.info('found: ' + href);
|
|
else
|
|
console.error('broken href: ' + href);
|
|
})
|
|
</script>
|