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:
Note that custom help bubble UI will be focused when shown, so take that into account when designing them, especially for screen reader users.
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.
The simplest way to create a custom UI for your promo is by making your own bubble dialog.
Here are the steps:
BubbleDialogDelegateView and also inherit from CustomHelpBubbleUi.CustomHelpBubbleUi::NotifyUserAction() with one of the appropriate values (depending on the type of action the user took).NotifyUserAction() then an “aborted” result will be registered instead of the correct result.DialogDelegate::SetCancelCallback() and DialogDelegate::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.
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.
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:
TopChromeWebUIController and CustomWebUIHelpBubbleController.enable_chrome_send = true as well.UserEducation. histogram bucket for Top Chrome WebUI performance metrics.webui::SetupWebUIDataSource() in the constructor with the appropriate parameters.WEB_UI_CONTROLLER_TYPE_DECL() in the header and WEB_UI_CONTROLLER_TYPE_IMPL(ClassName) in the source file.DECLARE_TOP_CHROME_WEBUI_CONFIG(ClassName) to automatically create a config for your WebUI..AddWebUIConfig(ClassNameConfig) in this file.RegisterWebUIControllerInterfaceBinder() in this file.CustomHelpBubbleHandlerFactory and any additional mojo bindings you need.On the WebUI/Typescript side:
CustomHelpBubbleHandlerInterface by calling CustomHelpBubbleProxyImpl.getInstance().getHandler().handler.notifyUserAction() with the appropriate value.Unlike Views, CustomWebUIHelpBubbleController is already a convenience class that handles a lot of the boilerplate (yes, there‘s more than what’s listed above).
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.
There are three ways you can add additional behavior for after your bubble closes:
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.
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.
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!