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