Custom Help Bubbles
A custom help bubble is a UI used specifically for feature promos instead of a normal blue help bubble. You can use a custom help bubble when you want all of the normal rate-limiting and contention management of the feature promo system, but you need something other than a blue bubble.
Note that custom help bubble UI are subject to allowlisting and UI review; custom help bubble UI must conform to the normal requirements of help bubbles:
- Must have an (X) button in the upper right or a prominent dismiss button
- Must respond to ESC with the same effect as (X)
- Must meet all other accessibility requirements
Note that custom help bubble UI will be focused when shown, so take that into account when designing them, especially for screen reader users.
Registering Custom Help Bubble IPH
When registering browser_user_education_service.cc use
FeaturePromoSpecification::CreateForCustomUi(). You'll note that this takes a
CustomHelpBubbleFactoryCallback which returns a CustomHelpBubble. Most of
the time you won't be directly creating your own CustomHelpBubble (though you
can!) but instead using a utility method to directly wrap your UI.
Which utility method you use to create your CustomHelpBubbleFactoryCallback
will depend on whether you are creating a Views BubbleDialog or a WebUI dialog.
If you are not creating one of these supported UI types, you will have to both
create your own UI and your own CustomHelpBubble class to wrap it.
Custom Promo UI Using Views Bubble Dialogs
The simplest way to create a custom UI for your promo is by making your own bubble dialog.
Here are the steps:
- Derive your class from
BubbleDialogDelegateViewand also inherit fromCustomHelpBubbleUi. - Ensure that any input that should close the dialog instead calls
CustomHelpBubbleUi::NotifyUserAction()with one of the appropriate values (depending on the type of action the user took).- If you cannot prevent your bubble from closing (e.g. you are using default dismiss-on-escape behavior) make sure to call before closing the bubble.
- If the bubble closes for any reason before
NotifyUserAction()then an "aborted" result will be registered instead of the correct result. - Don't be afraid to call
DialogDelegate::SetCancelCallback()andDialogDelegate::SetAcceptCallback()to set up some default behavior handling.
That's it! There's a ready (if very simple) example in TestCustomHelpBubbleView. For layout examples (including how to get the close button positioned) check out HelpBubbleView.
Registering
To register a Custom UI feature promo with a Views-based custom bubble, first
follow all of the normal steps for feature promos, then
bind a CustomHelpBubbleViewFactoryCallback and pass it to
CreateCustomHelpBubbleViewFactoryCallback().
Example:
// This could go inline in the call below, but is separated to make the
// example clearer.
auto my_custom_help_bubble_view_factory_callback =
base::BindRepeating(
[](ui::ElementContext from_context,
HelpBubbleArrow arrow,
FeaturePromoSpecification::BuildHelpBubbleParams build_params) {
return std::make_unique<MyCustomUiBubbleDialogView>(
build_params.anchor_element->AsA<views::TrackedElementViews>()->view(),
arrow);
});
feature_promo_registry.Register(
user_education::FeaturePromoSpecification::CreateForCustomUi(
kIPHMyCustomUIPromoFeature,
kAnchorElementId,
CreateCustomHelpBubbleViewFactoryCallback(
my_custom_help_bubble_view_factory_callback));
)
Note: you can use the from_context parameter to locate the browser that the
promo is being launched from, even if build_params.anchor_element is in a
different window.
This example assumes that the anchor is a View, however you can get creative
if you want to; see
FloatingWebUIHelpBubbleFactory
for one of the more sophisticated examples.
For handling of "custom action" IPH-like dialogs, see below.
Custom Promo UI Using WebUI Bubble Dialogs
You can create dialogs without the limitations of Views by using WebUI (though there is significantly more boilerplate involved). When you create a WebUI in this way, it must be a "Top Chrome" WebUI, which has a few extra requirements so it can be easily wrapped in a bubble dialog.
On the browser/C++ side:
- Your controller class must extend
TopChromeWebUIControllerandCustomWebUIHelpBubbleController.- You probably want
enable_chrome_send = trueas well. - The latter also puts this into the
UserEducation.histogram bucket for Top Chrome WebUI performance metrics.
- You probably want
- Call
webui::SetupWebUIDataSource()in the constructor with the appropriate parameters. - Add
WEB_UI_CONTROLLER_TYPE_DECL()in the header andWEB_UI_CONTROLLER_TYPE_IMPL(ClassName)in the source file. - Use
DECLARE_TOP_CHROME_WEBUI_CONFIG(ClassName)to automatically create a config for your WebUI. - Call
.AddWebUIConfig(ClassNameConfig)in this file. - Call or add to the appropriate
RegisterWebUIControllerInterfaceBinder()in this file.- This should include
CustomHelpBubbleHandlerFactoryand any additional mojo bindings you need.
- This should include
On the WebUI/Typescript side:
- Fetch the
CustomHelpBubbleHandlerInterfaceby callingCustomHelpBubbleProxyImpl.getInstance().getHandler(). - When the user interacts with a button or link, call
handler.notifyUserAction()with the appropriate value.- Ensure you have a proper (X) close button as well as at least one link or action button the user can interact with.
Unlike Views, CustomWebUIHelpBubbleController is already a convenience class
that handles a lot of the boilerplate (yes, there's more than what's listed
above).
Registering
To register a Custom UI feature promo with a WebUI-based custom bubble, first
follow all of the normal steps for feature promos, then
pass your WebUI's URL to MakeCustomWebUIHelpBubbleFactoryCallback<T>().
Example:
feature_promo_registry.Register(
user_education::FeaturePromoSpecification::CreateForCustomUi(
kIPHMyCustomUIPromoFeature,
kAnchorElementId,
MakeCustomWebUIHelpBubbleFactoryCallback<
MyCustomHelpBubbleWebUIController>(kMyWebUIUrl)));
For handling of "custom action" IPH-like dialogs, see below.
Custom and Follow-Up Actions
There are three ways you can add additional behavior for after your bubble closes:
- Bubble triggers some additional action in the browser via a button, link, etc. in response to user action.
- Bubble reports that the user selected "action" as their response, with logic provided when the IPH is registered, just as with "custom action"-style IPH.
- External code decides the bubble should be closed due to user action outside the help bubble, and some further action should be taken.
Option 1: Bubble Takes Action
An example of this would be the user clicking on a link in a WebUI dialog that opens, say, a settings page.
Alternatively, a button might call some function directly (over a different mojo interface if in a WebUI) that performs some action in the browser outside the help bubble.
In all these cases, the bubble code should send UserAction::kAction, which
will cause the bubble to close and record the correct IPH result. No further
action is needed.
Option 2: Custom Action-like Behavior
An example of this would be the bubble having a "Show Me" button that you want to have open settings, start a tutorial, or something else.
In this case, the bubble can just send UserAction::kAction. There is an
optional fourth parameter to CreateForCustomUi() that is identical to the
callback in CreateForCustomAction(). If the bubble returns kAction then
this callback will be called as if this were a custom action IPH.
Option 3: Close Promo and Continue
As with many other promos, sometimes the user will engage the feature being promoted outside the promo - for example, the user presses the toolbar or menu button the IPH is anchored to rather than interacting with the IPH.
In this case, at the very least, as with other IPH, you should call
BrowserUserEducationInterface::NotifyFeaturePromoFeatureUsed() (available on
BrowserWindow/BrowserView as well). However if you want to take additional
action (like highlighting menu elements in the app menu), you can instead call
CloseFeaturePromoAndContinue().
Both of these can close the promo and mark it as dismissed. The latter allows
you to continue the promo indefinitely while the user is having some other
experience (which could be anything). Just remember to release the
FeaturePromoHandle when you're done!