1 // Copyright 2018 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 "chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.h"
6 
7 #include <memory>
8 #include <string>
9 #include <utility>
10 
11 #include "base/bind.h"
12 #include "base/macros.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "build/buildflag.h"
15 #include "chrome/browser/chromeos/assistant/assistant_util.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/profiles/profile_manager.h"
18 #include "chrome/browser/ui/ash/ash_util.h"
19 #include "chrome/browser/ui/views/chrome_web_dialog_view.h"
20 #include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
21 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
22 #include "chrome/common/url_constants.h"
23 #include "chrome/grit/browser_resources.h"
24 #include "chromeos/assistant/buildflags.h"
25 #include "chromeos/services/assistant/public/cpp/assistant_prefs.h"
26 #include "chromeos/services/assistant/public/cpp/features.h"
27 #include "components/prefs/pref_service.h"
28 #include "components/session_manager/core/session_manager.h"
29 #include "content/public/browser/host_zoom_map.h"
30 #include "content/public/browser/url_data_source.h"
31 #include "content/public/browser/web_ui.h"
32 #include "content/public/browser/web_ui_data_source.h"
33 #include "content/public/common/content_features.h"
34 #include "net/base/url_util.h"
35 #include "services/network/public/mojom/content_security_policy.mojom.h"
36 #include "ui/chromeos/resources/grit/ui_chromeos_resources.h"
37 #include "ui/display/display.h"
38 #include "ui/display/screen.h"
39 #include "ui/gfx/geometry/insets.h"
40 #include "ui/views/widget/widget.h"
41 #include "ui/wm/core/window_animations.h"
42 
43 namespace chromeos {
44 
45 namespace {
46 
47 AssistantOptInDialog* g_dialog = nullptr;
48 
49 constexpr int kCaptionBarHeight = 32;
50 constexpr int kDialogMargin = 48;
51 constexpr gfx::Size kDialogMaxSize = gfx::Size(768, 768);
52 constexpr gfx::Size kDialogMinSize = gfx::Size(544, 464);
53 constexpr gfx::Insets kDialogInsets =
54     gfx::Insets(kDialogMargin + kCaptionBarHeight,
55                 kDialogMargin,
56                 kDialogMargin,
57                 kDialogMargin);
58 
59 constexpr char kFlowTypeParamKey[] = "flow-type";
60 constexpr char kCaptionBarHeightParamKey[] = "caption-bar-height";
61 
CreateAssistantOptInURL(ash::FlowType type)62 GURL CreateAssistantOptInURL(ash::FlowType type) {
63   GURL gurl(chrome::kChromeUIAssistantOptInURL);
64   gurl = net::AppendQueryParameter(
65       gurl, kFlowTypeParamKey, base::NumberToString(static_cast<int>(type)));
66   gurl = net::AppendQueryParameter(gurl, kCaptionBarHeightParamKey,
67                                    base::NumberToString(kCaptionBarHeight));
68   return gurl;
69 }
70 
71 }  // namespace
72 
AssistantOptInUI(content::WebUI * web_ui)73 AssistantOptInUI::AssistantOptInUI(content::WebUI* web_ui)
74     : ui::WebDialogUI(web_ui) {
75   // Set up the chrome://assistant-optin source.
76   content::WebUIDataSource* source =
77       content::WebUIDataSource::Create(chrome::kChromeUIAssistantOptInHost);
78 
79   auto assistant_handler =
80       std::make_unique<AssistantOptInFlowScreenHandler>(&js_calls_container_);
81   assistant_handler_ptr_ = assistant_handler.get();
82   web_ui->AddMessageHandler(std::move(assistant_handler));
83   assistant_handler_ptr_->set_on_initialized(base::BindOnce(
84       &AssistantOptInUI::Initialize, weak_factory_.GetWeakPtr()));
85   assistant_handler_ptr_->SetupAssistantConnection();
86 
87   base::DictionaryValue localized_strings;
88   assistant_handler_ptr_->GetLocalizedStrings(&localized_strings);
89 
90   OobeUI::AddOobeComponents(source, localized_strings);
91 
92   source->AddLocalizedStrings(localized_strings);
93   source->UseStringsJs();
94   source->AddResourcePath("assistant_optin.js", IDR_ASSISTANT_OPTIN_JS);
95   source->AddResourcePath("assistant_logo.png", IDR_ASSISTANT_LOGO_PNG);
96   source->SetDefaultResource(IDR_ASSISTANT_OPTIN_HTML);
97   source->AddResourcePath("voice_match_animation.json",
98                           IDR_ASSISTANT_VOICE_MATCH_ANIMATION);
99   source->AddResourcePath("voice_match_already_setup_animation.json",
100                           IDR_ASSISTANT_VOICE_MATCH_ALREADY_SETUP_ANIMATION);
101   source->OverrideContentSecurityPolicy(
102       network::mojom::CSPDirectiveName::WorkerSrc, "worker-src blob: 'self';");
103   source->DisableTrustedTypesCSP();
104   content::WebUIDataSource::Add(Profile::FromWebUI(web_ui), source);
105 
106   // Do not zoom for Assistant opt-in web contents.
107   content::HostZoomMap* zoom_map =
108       content::HostZoomMap::GetForWebContents(web_ui->GetWebContents());
109   DCHECK(zoom_map);
110   zoom_map->SetZoomLevelForHost(web_ui->GetWebContents()->GetURL().host(), 0);
111 }
112 
113 AssistantOptInUI::~AssistantOptInUI() = default;
114 
OnDialogClosed()115 void AssistantOptInUI::OnDialogClosed() {
116   if (assistant_handler_ptr_) {
117     assistant_handler_ptr_->OnDialogClosed();
118   }
119 }
120 
Initialize()121 void AssistantOptInUI::Initialize() {
122   js_calls_container_.ExecuteDeferredJSCalls(web_ui());
123 }
124 
125 // AssistantOptInDialog
126 
127 // static
Show(ash::FlowType type,ash::AssistantSetup::StartAssistantOptInFlowCallback callback)128 void AssistantOptInDialog::Show(
129     ash::FlowType type,
130     ash::AssistantSetup::StartAssistantOptInFlowCallback callback) {
131 #if !BUILDFLAG(ENABLE_CROS_LIBASSISTANT)
132   std::move(callback).Run(false);
133 #else
134   // Check Assistant allowed state.
135   if (::assistant::IsAssistantAllowedForProfile(
136           ProfileManager::GetActiveUserProfile()) !=
137       chromeos::assistant::AssistantAllowedState::ALLOWED) {
138     std::move(callback).Run(false);
139     return;
140   }
141 
142   // Check session state here to prevent timing issue -- session state might
143   // have changed during the mojom calls to launch the opt-in dalog.
144   if (session_manager::SessionManager::Get()->session_state() !=
145       session_manager::SessionState::ACTIVE) {
146     std::move(callback).Run(false);
147     return;
148   }
149   if (g_dialog) {
150     g_dialog->Focus();
151     std::move(callback).Run(false);
152     return;
153   }
154   g_dialog = new AssistantOptInDialog(type, std::move(callback));
155 
156   g_dialog->ShowSystemDialog();
157 #endif
158 }
159 
160 // static
BounceIfActive()161 bool AssistantOptInDialog::BounceIfActive() {
162   if (!g_dialog)
163     return false;
164 
165   g_dialog->Focus();
166   wm::AnimateWindow(g_dialog->dialog_window(),
167                     wm::WINDOW_ANIMATION_TYPE_BOUNCE);
168   return true;
169 }
170 
AssistantOptInDialog(ash::FlowType type,ash::AssistantSetup::StartAssistantOptInFlowCallback callback)171 AssistantOptInDialog::AssistantOptInDialog(
172     ash::FlowType type,
173     ash::AssistantSetup::StartAssistantOptInFlowCallback callback)
174     : SystemWebDialogDelegate(CreateAssistantOptInURL(type), base::string16()),
175       callback_(std::move(callback)) {}
176 
~AssistantOptInDialog()177 AssistantOptInDialog::~AssistantOptInDialog() {
178   DCHECK_EQ(this, g_dialog);
179   g_dialog = nullptr;
180 }
181 
AdjustWidgetInitParams(views::Widget::InitParams * params)182 void AssistantOptInDialog::AdjustWidgetInitParams(
183     views::Widget::InitParams* params) {
184   params->z_order = ui::ZOrderLevel::kNormal;
185 }
186 
GetDialogSize(gfx::Size * size) const187 void AssistantOptInDialog::GetDialogSize(gfx::Size* size) const {
188   auto bounds = display::Screen::GetScreen()->GetPrimaryDisplay().work_area();
189   bounds.Inset(kDialogInsets);
190   auto dialog_size = bounds.size();
191   dialog_size.SetToMin(kDialogMaxSize);
192   dialog_size.SetToMax(kDialogMinSize);
193   size->SetSize(dialog_size.width(), dialog_size.height());
194 }
195 
GetDialogArgs() const196 std::string AssistantOptInDialog::GetDialogArgs() const {
197   return std::string();
198 }
199 
OnDialogShown(content::WebUI * webui)200 void AssistantOptInDialog::OnDialogShown(content::WebUI* webui) {
201   assistant_ui_ = static_cast<AssistantOptInUI*>(webui->GetController());
202 }
203 
OnDialogClosed(const std::string & json_retval)204 void AssistantOptInDialog::OnDialogClosed(const std::string& json_retval) {
205   if (assistant_ui_)
206     assistant_ui_->OnDialogClosed();
207 
208   PrefService* prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
209   const bool completed =
210       prefs->GetBoolean(chromeos::assistant::prefs::kAssistantEnabled) &&
211       (prefs->GetInteger(assistant::prefs::kAssistantConsentStatus) ==
212        assistant::prefs::ConsentStatus::kActivityControlAccepted);
213   std::move(callback_).Run(completed);
214   SystemWebDialogDelegate::OnDialogClosed(json_retval);
215 }
216 
217 }  // namespace chromeos
218