1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/autofill_assistant/browser/generic_ui_replace_placeholders.h"
6 #include "base/logging.h"
7 #include "components/autofill_assistant/browser/field_formatter.h"
8
9 namespace autofill_assistant {
10 // Forward declaration to allow recursive calls.
11 void ReplacePlaceholdersInGenericUi(
12 GenericUserInterfaceProto* in_out_proto,
13 const std::map<std::string, std::string>& placeholders);
14
15 namespace {
16
ReplaceInPlace(std::string * in_out,const std::map<std::string,std::string> & placeholders)17 void ReplaceInPlace(std::string* in_out,
18 const std::map<std::string, std::string>& placeholders) {
19 auto formatted_string = field_formatter::FormatString(*in_out, placeholders,
20 /*strict = */ false);
21 if (!formatted_string.has_value()) {
22 VLOG(1) << "lenient placeholder replacement failed";
23 return;
24 }
25 in_out->assign(*formatted_string);
26 }
27
ReplacePlaceholdersInView(ViewProto * in_out_proto,const std::map<std::string,std::string> & placeholders)28 void ReplacePlaceholdersInView(
29 ViewProto* in_out_proto,
30 const std::map<std::string, std::string>& placeholders) {
31 if (in_out_proto->has_identifier()) {
32 ReplaceInPlace(in_out_proto->mutable_identifier(), placeholders);
33 }
34
35 switch (in_out_proto->view_case()) {
36 case ViewProto::kViewContainer:
37 for (auto& child :
38 *in_out_proto->mutable_view_container()->mutable_views()) {
39 ReplacePlaceholdersInView(&child, placeholders);
40 }
41 return;
42 case ViewProto::kTextView:
43 switch (in_out_proto->text_view().kind_case()) {
44 case TextViewProto::kModelIdentifier:
45 ReplaceInPlace(
46 in_out_proto->mutable_text_view()->mutable_model_identifier(),
47 placeholders);
48 return;
49 case TextViewProto::kText:
50 case TextViewProto::KIND_NOT_SET:
51 return;
52 }
53 return;
54 case ViewProto::kVerticalExpanderView: {
55 if (in_out_proto->vertical_expander_view().has_title_view()) {
56 ReplacePlaceholdersInView(in_out_proto->mutable_vertical_expander_view()
57 ->mutable_title_view(),
58 placeholders);
59 }
60 if (in_out_proto->vertical_expander_view().has_collapsed_view()) {
61 ReplacePlaceholdersInView(in_out_proto->mutable_vertical_expander_view()
62 ->mutable_collapsed_view(),
63 placeholders);
64 }
65 if (in_out_proto->vertical_expander_view().has_expanded_view()) {
66 ReplacePlaceholdersInView(in_out_proto->mutable_vertical_expander_view()
67 ->mutable_expanded_view(),
68 placeholders);
69 }
70 return;
71 }
72 case ViewProto::kTextInputView: {
73 if (in_out_proto->text_input_view().has_model_identifier()) {
74 ReplaceInPlace(
75 in_out_proto->mutable_text_input_view()->mutable_model_identifier(),
76 placeholders);
77 }
78 return;
79 }
80 case ViewProto::kToggleButtonView:
81 if (in_out_proto->toggle_button_view().has_model_identifier()) {
82 ReplaceInPlace(in_out_proto->mutable_toggle_button_view()
83 ->mutable_model_identifier(),
84 placeholders);
85 }
86 if (in_out_proto->toggle_button_view().has_left_content_view()) {
87 ReplacePlaceholdersInView(in_out_proto->mutable_toggle_button_view()
88 ->mutable_left_content_view(),
89 placeholders);
90 }
91 if (in_out_proto->toggle_button_view().has_right_content_view()) {
92 ReplacePlaceholdersInView(in_out_proto->mutable_toggle_button_view()
93 ->mutable_right_content_view(),
94 placeholders);
95 }
96 return;
97 case ViewProto::kDividerView:
98 case ViewProto::kImageView:
99 case ViewProto::VIEW_NOT_SET:
100 return;
101 }
102 }
103
ReplacePlaceholdersInEvent(EventProto * in_out_proto,const std::map<std::string,std::string> & placeholders)104 void ReplacePlaceholdersInEvent(
105 EventProto* in_out_proto,
106 const std::map<std::string, std::string>& placeholders) {
107 switch (in_out_proto->kind_case()) {
108 case EventProto::kOnValueChanged:
109 if (in_out_proto->on_value_changed().has_model_identifier()) {
110 ReplaceInPlace(in_out_proto->mutable_on_value_changed()
111 ->mutable_model_identifier(),
112 placeholders);
113 }
114 return;
115 case EventProto::kOnViewClicked:
116 if (in_out_proto->on_view_clicked().has_view_identifier()) {
117 ReplaceInPlace(
118 in_out_proto->mutable_on_view_clicked()->mutable_view_identifier(),
119 placeholders);
120 }
121 return;
122 case EventProto::kOnViewContainerCleared:
123 if (in_out_proto->on_view_container_cleared().has_view_identifier()) {
124 ReplaceInPlace(in_out_proto->mutable_on_view_container_cleared()
125 ->mutable_view_identifier(),
126 placeholders);
127 }
128 return;
129 case EventProto::kOnPopupDismissed:
130 if (in_out_proto->on_popup_dismissed().has_popup_identifier()) {
131 ReplaceInPlace(in_out_proto->mutable_on_popup_dismissed()
132 ->mutable_popup_identifier(),
133 placeholders);
134 }
135 return;
136 case EventProto::kOnUserActionCalled:
137 case EventProto::kOnTextLinkClicked:
138 case EventProto::KIND_NOT_SET:
139 return;
140 }
141 return;
142 }
143
ReplacePlaceholdersInValue(ValueReferenceProto * in_out_proto,const std::map<std::string,std::string> & placeholders)144 void ReplacePlaceholdersInValue(
145 ValueReferenceProto* in_out_proto,
146 const std::map<std::string, std::string>& placeholders) {
147 switch (in_out_proto->kind_case()) {
148 case ValueReferenceProto::kModelIdentifier:
149 ReplaceInPlace(in_out_proto->mutable_model_identifier(), placeholders);
150 return;
151 case ValueReferenceProto::kValue:
152 case ValueReferenceProto::KIND_NOT_SET:
153 return;
154 }
155 }
156
ReplacePlaceholdersInInteraction(InteractionProto * in_out_proto,const std::map<std::string,std::string> & placeholders)157 void ReplacePlaceholdersInInteraction(
158 InteractionProto* in_out_proto,
159 const std::map<std::string, std::string>& placeholders) {
160 for (auto& trigger_event : *in_out_proto->mutable_trigger_event()) {
161 ReplacePlaceholdersInEvent(&trigger_event, placeholders);
162 }
163
164 for (auto& callback : *in_out_proto->mutable_callbacks()) {
165 ReplacePlaceholdersInCallback(&callback, placeholders);
166 }
167 }
168
ReplacePlaceholdersInModel(ModelProto * in_out_proto,const std::map<std::string,std::string> & placeholders)169 void ReplacePlaceholdersInModel(
170 ModelProto* in_out_proto,
171 const std::map<std::string, std::string>& placeholders) {
172 for (auto& value : *in_out_proto->mutable_values()) {
173 if (value.has_identifier()) {
174 ReplaceInPlace(value.mutable_identifier(), placeholders);
175 }
176 }
177 }
178
179 } // namespace
180
ReplacePlaceholdersInGenericUi(GenericUserInterfaceProto * in_out_proto,const std::map<std::string,std::string> & placeholders)181 void ReplacePlaceholdersInGenericUi(
182 GenericUserInterfaceProto* in_out_proto,
183 const std::map<std::string, std::string>& placeholders) {
184 if (placeholders.empty()) {
185 return;
186 }
187
188 if (in_out_proto->has_root_view()) {
189 ReplacePlaceholdersInView(in_out_proto->mutable_root_view(), placeholders);
190 }
191
192 for (auto& interaction :
193 *in_out_proto->mutable_interactions()->mutable_interactions()) {
194 ReplacePlaceholdersInInteraction(&interaction, placeholders);
195 }
196
197 if (in_out_proto->has_model()) {
198 ReplacePlaceholdersInModel(in_out_proto->mutable_model(), placeholders);
199 }
200 }
201
ReplacePlaceholdersInCallback(CallbackProto * in_out_proto,const std::map<std::string,std::string> & placeholders)202 void ReplacePlaceholdersInCallback(
203 CallbackProto* in_out_proto,
204 const std::map<std::string, std::string>& placeholders) {
205 if (in_out_proto->has_condition_model_identifier()) {
206 ReplaceInPlace(in_out_proto->mutable_condition_model_identifier(),
207 placeholders);
208 }
209
210 switch (in_out_proto->kind_case()) {
211 case CallbackProto::kSetValue:
212 if (in_out_proto->set_value().has_model_identifier()) {
213 ReplaceInPlace(
214 in_out_proto->mutable_set_value()->mutable_model_identifier(),
215 placeholders);
216 }
217 if (in_out_proto->set_value().has_value()) {
218 ReplacePlaceholdersInValue(
219 in_out_proto->mutable_set_value()->mutable_value(), placeholders);
220 }
221 return;
222 case CallbackProto::kShowListPopup:
223 if (in_out_proto->show_list_popup().has_item_names()) {
224 ReplacePlaceholdersInValue(
225 in_out_proto->mutable_show_list_popup()->mutable_item_names(),
226 placeholders);
227 }
228 if (in_out_proto->show_list_popup().has_item_types()) {
229 ReplacePlaceholdersInValue(
230 in_out_proto->mutable_show_list_popup()->mutable_item_types(),
231 placeholders);
232 }
233 if (in_out_proto->show_list_popup()
234 .has_selected_item_indices_model_identifier()) {
235 ReplaceInPlace(in_out_proto->mutable_show_list_popup()
236 ->mutable_selected_item_indices_model_identifier(),
237 placeholders);
238 }
239 if (in_out_proto->show_list_popup()
240 .has_selected_item_names_model_identifier()) {
241 ReplaceInPlace(in_out_proto->mutable_show_list_popup()
242 ->mutable_selected_item_names_model_identifier(),
243 placeholders);
244 }
245 return;
246 case CallbackProto::kComputeValue:
247 if (in_out_proto->compute_value().has_result_model_identifier()) {
248 ReplaceInPlace(in_out_proto->mutable_compute_value()
249 ->mutable_result_model_identifier(),
250 placeholders);
251 }
252 switch (in_out_proto->compute_value().kind_case()) {
253 case ComputeValueProto::kBooleanAnd:
254 for (auto& value : *in_out_proto->mutable_compute_value()
255 ->mutable_boolean_and()
256 ->mutable_values()) {
257 ReplacePlaceholdersInValue(&value, placeholders);
258 }
259 return;
260 case ComputeValueProto::kBooleanOr:
261 for (auto& value : *in_out_proto->mutable_compute_value()
262 ->mutable_boolean_or()
263 ->mutable_values()) {
264 ReplacePlaceholdersInValue(&value, placeholders);
265 }
266 return;
267 case ComputeValueProto::kBooleanNot:
268 if (in_out_proto->compute_value().boolean_not().has_value()) {
269 ReplacePlaceholdersInValue(in_out_proto->mutable_compute_value()
270 ->mutable_boolean_not()
271 ->mutable_value(),
272 placeholders);
273 }
274 return;
275 case ComputeValueProto::kToString:
276 if (in_out_proto->compute_value().to_string().has_value()) {
277 ReplacePlaceholdersInValue(in_out_proto->mutable_compute_value()
278 ->mutable_to_string()
279 ->mutable_value(),
280 placeholders);
281 }
282 return;
283 case ComputeValueProto::kComparison:
284 if (in_out_proto->compute_value().comparison().has_value_a()) {
285 ReplacePlaceholdersInValue(in_out_proto->mutable_compute_value()
286 ->mutable_comparison()
287 ->mutable_value_a(),
288 placeholders);
289 }
290 if (in_out_proto->compute_value().comparison().has_value_b()) {
291 ReplacePlaceholdersInValue(in_out_proto->mutable_compute_value()
292 ->mutable_comparison()
293 ->mutable_value_b(),
294 placeholders);
295 }
296 return;
297 case ComputeValueProto::kIntegerSum:
298 for (auto& value : *in_out_proto->mutable_compute_value()
299 ->mutable_integer_sum()
300 ->mutable_values()) {
301 ReplacePlaceholdersInValue(&value, placeholders);
302 }
303 return;
304 case ComputeValueProto::kCreateCreditCardResponse:
305 if (in_out_proto->compute_value()
306 .create_credit_card_response()
307 .has_value()) {
308 ReplacePlaceholdersInValue(
309 in_out_proto->mutable_compute_value()
310 ->mutable_create_credit_card_response()
311 ->mutable_value(),
312 placeholders);
313 }
314 return;
315 case ComputeValueProto::kCreateLoginOptionResponse:
316 if (in_out_proto->compute_value()
317 .create_login_option_response()
318 .has_value()) {
319 ReplacePlaceholdersInValue(
320 in_out_proto->mutable_compute_value()
321 ->mutable_create_login_option_response()
322 ->mutable_value(),
323 placeholders);
324 }
325 return;
326 case ComputeValueProto::KIND_NOT_SET:
327 return;
328 }
329 case CallbackProto::kSetUserActions:
330 if (in_out_proto->set_user_actions().has_user_actions()) {
331 ReplacePlaceholdersInValue(
332 in_out_proto->mutable_set_user_actions()->mutable_user_actions(),
333 placeholders);
334 }
335 return;
336 case CallbackProto::kShowCalendarPopup:
337 if (in_out_proto->show_calendar_popup().has_date_model_identifier()) {
338 ReplaceInPlace(in_out_proto->mutable_show_calendar_popup()
339 ->mutable_date_model_identifier(),
340 placeholders);
341 }
342 if (in_out_proto->show_calendar_popup().has_min_date()) {
343 ReplacePlaceholdersInValue(
344 in_out_proto->mutable_show_calendar_popup()->mutable_min_date(),
345 placeholders);
346 }
347 if (in_out_proto->show_calendar_popup().has_max_date()) {
348 ReplacePlaceholdersInValue(
349 in_out_proto->mutable_show_calendar_popup()->mutable_max_date(),
350 placeholders);
351 }
352 return;
353 case CallbackProto::kSetText:
354 if (in_out_proto->set_text().has_text()) {
355 ReplacePlaceholdersInValue(
356 in_out_proto->mutable_set_text()->mutable_text(), placeholders);
357 }
358 if (in_out_proto->set_text().has_view_identifier()) {
359 ReplaceInPlace(
360 in_out_proto->mutable_set_text()->mutable_view_identifier(),
361 placeholders);
362 }
363 return;
364 case CallbackProto::kToggleUserAction:
365 if (in_out_proto->toggle_user_action()
366 .has_user_actions_model_identifier()) {
367 ReplaceInPlace(in_out_proto->mutable_toggle_user_action()
368 ->mutable_user_actions_model_identifier(),
369 placeholders);
370 }
371 if (in_out_proto->toggle_user_action().has_enabled()) {
372 ReplacePlaceholdersInValue(
373 in_out_proto->mutable_toggle_user_action()->mutable_enabled(),
374 placeholders);
375 }
376 return;
377 case CallbackProto::kSetViewVisibility:
378 if (in_out_proto->set_view_visibility().has_view_identifier()) {
379 ReplaceInPlace(in_out_proto->mutable_set_view_visibility()
380 ->mutable_view_identifier(),
381 placeholders);
382 }
383 if (in_out_proto->set_view_visibility().has_visible()) {
384 ReplacePlaceholdersInValue(
385 in_out_proto->mutable_set_view_visibility()->mutable_visible(),
386 placeholders);
387 }
388 return;
389 case CallbackProto::kSetViewEnabled:
390 if (in_out_proto->set_view_enabled().has_view_identifier()) {
391 ReplaceInPlace(
392 in_out_proto->mutable_set_view_enabled()->mutable_view_identifier(),
393 placeholders);
394 }
395 if (in_out_proto->set_view_enabled().has_enabled()) {
396 ReplacePlaceholdersInValue(
397 in_out_proto->mutable_set_view_enabled()->mutable_enabled(),
398 placeholders);
399 }
400 return;
401 case CallbackProto::kShowGenericPopup:
402 if (in_out_proto->show_generic_popup().has_popup_identifier()) {
403 ReplaceInPlace(in_out_proto->mutable_show_generic_popup()
404 ->mutable_popup_identifier(),
405 placeholders);
406 }
407 if (in_out_proto->show_generic_popup().has_generic_ui()) {
408 ReplacePlaceholdersInGenericUi(
409 in_out_proto->mutable_show_generic_popup()->mutable_generic_ui(),
410 placeholders);
411 }
412 return;
413 case CallbackProto::kCreateNestedUi:
414 if (in_out_proto->create_nested_ui().has_generic_ui_identifier()) {
415 ReplaceInPlace(in_out_proto->mutable_create_nested_ui()
416 ->mutable_generic_ui_identifier(),
417 placeholders);
418 }
419 if (in_out_proto->create_nested_ui().has_generic_ui()) {
420 ReplacePlaceholdersInGenericUi(
421 in_out_proto->mutable_create_nested_ui()->mutable_generic_ui(),
422 placeholders);
423 }
424 if (in_out_proto->create_nested_ui().has_parent_view_identifier()) {
425 ReplaceInPlace(in_out_proto->mutable_create_nested_ui()
426 ->mutable_parent_view_identifier(),
427 placeholders);
428 }
429 return;
430 case CallbackProto::kClearViewContainer:
431 if (in_out_proto->clear_view_container().has_view_identifier()) {
432 ReplaceInPlace(in_out_proto->mutable_clear_view_container()
433 ->mutable_view_identifier(),
434 placeholders);
435 }
436 return;
437 case CallbackProto::kForEach:
438 if (in_out_proto->for_each().has_loop_value_model_identifier()) {
439 ReplaceInPlace(in_out_proto->mutable_for_each()
440 ->mutable_loop_value_model_identifier(),
441 placeholders);
442 }
443 for (auto& callback :
444 *in_out_proto->mutable_for_each()->mutable_callbacks()) {
445 ReplacePlaceholdersInCallback(&callback, placeholders);
446 }
447 return;
448 case CallbackProto::kShowInfoPopup:
449 case CallbackProto::kEndAction:
450 case CallbackProto::KIND_NOT_SET:
451 return;
452 }
453 }
454
455 } // namespace autofill_assistant
456