1 // Copyright (c) 2011 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/printing/cloud_print/cloud_print_setup_flow.h"
6
7 #include "base/json/json_writer.h"
8 #include "base/memory/singleton.h"
9 #include "base/string_util.h"
10 #include "base/utf_string_conversions.h"
11 #include "base/values.h"
12 #include "chrome/browser/platform_util.h"
13 #include "chrome/browser/prefs/pref_service.h"
14 #include "chrome/browser/printing/cloud_print/cloud_print_proxy_service.h"
15 #include "chrome/browser/printing/cloud_print/cloud_print_setup_message_handler.h"
16 #include "chrome/browser/printing/cloud_print/cloud_print_setup_source.h"
17 #include "chrome/browser/printing/cloud_print/cloud_print_url.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/service/service_process_control.h"
20 #include "chrome/browser/service/service_process_control_manager.h"
21 #include "chrome/browser/ui/browser.h"
22 #include "chrome/browser/ui/browser_dialogs.h"
23 #include "chrome/browser/ui/browser_list.h"
24 #include "chrome/browser/ui/browser_window.h"
25 #include "chrome/browser/ui/webui/chrome_url_data_manager.h"
26 #include "chrome/common/net/gaia/gaia_auth_fetcher.h"
27 #include "chrome/common/net/gaia/gaia_constants.h"
28 #include "chrome/common/net/gaia/google_service_auth_error.h"
29 #include "chrome/common/pref_names.h"
30 #include "chrome/common/service_messages.h"
31 #include "content/browser/browser_thread.h"
32 #include "content/browser/renderer_host/render_view_host.h"
33 #include "content/browser/tab_contents/tab_contents.h"
34 #include "grit/chromium_strings.h"
35 #include "grit/locale_settings.h"
36 #include "ui/base/l10n/l10n_font_util.h"
37 #include "ui/gfx/font.h"
38
39 namespace {
40
SetupIframeXPath()41 string16& SetupIframeXPath() {
42 static string16 kSetupIframeXPath =
43 ASCIIToUTF16("//iframe[@id='cloudprintsetup']");
44 return kSetupIframeXPath;
45 }
46
DoneIframeXPath()47 string16& DoneIframeXPath() {
48 static string16 kDoneIframeXPath =
49 ASCIIToUTF16("//iframe[@id='setupdone']");
50 return kDoneIframeXPath;
51 }
52
53 } // end namespace
54
55 ////////////////////////////////////////////////////////////////////////////////
56 // CloudPrintSetupFlow implementation.
57
58 // static
OpenDialog(Profile * profile,const base::WeakPtr<Delegate> & delegate,gfx::NativeWindow parent_window)59 CloudPrintSetupFlow* CloudPrintSetupFlow::OpenDialog(
60 Profile* profile,
61 const base::WeakPtr<Delegate>& delegate,
62 gfx::NativeWindow parent_window) {
63 DCHECK(profile);
64 // Set the arguments for showing the gaia login page.
65 DictionaryValue args;
66 args.SetString("user", "");
67 args.SetInteger("error", 0);
68 args.SetBoolean("editable_user", true);
69
70 bool setup_done = false;
71 if (profile->GetPrefs()->HasPrefPath(prefs::kCloudPrintEmail) &&
72 !profile->GetPrefs()->GetString(prefs::kCloudPrintEmail).empty())
73 setup_done = true;
74 args.SetString("pageToShow", setup_done ? "setupdone" : "cloudprintsetup");
75
76 std::string json_args;
77 base::JSONWriter::Write(&args, false, &json_args);
78
79 CloudPrintSetupFlow* flow = new CloudPrintSetupFlow(json_args, profile,
80 delegate, setup_done);
81 // We may not always have a browser. This can happen when we are being
82 // invoked in the context of a "token expired" notfication. If we don't have
83 // a brower, use the underlying dialog system to show the dialog without
84 // using a browser.
85 if (!parent_window) {
86 Browser* browser = BrowserList::GetLastActive();
87 if (browser && browser->window())
88 parent_window = browser->window()->GetNativeHandle();
89 }
90 browser::ShowHtmlDialog(parent_window, profile, flow);
91 return flow;
92 }
93
CloudPrintSetupFlow(const std::string & args,Profile * profile,const base::WeakPtr<Delegate> & delegate,bool setup_done)94 CloudPrintSetupFlow::CloudPrintSetupFlow(
95 const std::string& args,
96 Profile* profile,
97 const base::WeakPtr<Delegate>& delegate,
98 bool setup_done)
99 : web_ui_(NULL),
100 dialog_start_args_(args),
101 last_auth_error_(GoogleServiceAuthError::None()),
102 setup_done_(setup_done),
103 process_control_(NULL),
104 delegate_(delegate) {
105 // TODO(hclam): The data source should be added once.
106 profile_ = profile;
107 profile->GetChromeURLDataManager()->AddDataSource(
108 new CloudPrintSetupSource());
109 }
110
~CloudPrintSetupFlow()111 CloudPrintSetupFlow::~CloudPrintSetupFlow() {
112 }
113
Focus()114 void CloudPrintSetupFlow::Focus() {
115 // TODO(pranavk): implement this method.
116 NOTIMPLEMENTED();
117 }
118
119 ///////////////////////////////////////////////////////////////////////////////
120 // HtmlDialogUIDelegate implementation.
GetDialogContentURL() const121 GURL CloudPrintSetupFlow::GetDialogContentURL() const {
122 return GURL("chrome://cloudprintsetup/setupflow");
123 }
124
GetWebUIMessageHandlers(std::vector<WebUIMessageHandler * > * handlers) const125 void CloudPrintSetupFlow::GetWebUIMessageHandlers(
126 std::vector<WebUIMessageHandler*>* handlers) const {
127 // Create the message handler only after we are asked, the caller is
128 // responsible for deleting the objects.
129 handlers->push_back(
130 new CloudPrintSetupMessageHandler(
131 const_cast<CloudPrintSetupFlow*>(this)));
132 }
133
GetDialogSize(gfx::Size * size) const134 void CloudPrintSetupFlow::GetDialogSize(gfx::Size* size) const {
135 PrefService* prefs = profile_->GetPrefs();
136 gfx::Font approximate_web_font(
137 UTF8ToUTF16(prefs->GetString(prefs::kWebKitSansSerifFontFamily)),
138 prefs->GetInteger(prefs::kWebKitDefaultFontSize));
139
140 if (setup_done_) {
141 *size = ui::GetLocalizedContentsSizeForFont(
142 IDS_CLOUD_PRINT_SETUP_WIZARD_DONE_WIDTH_CHARS,
143 IDS_CLOUD_PRINT_SETUP_WIZARD_DONE_HEIGHT_LINES,
144 approximate_web_font);
145 } else {
146 *size = ui::GetLocalizedContentsSizeForFont(
147 IDS_CLOUD_PRINT_SETUP_WIZARD_WIDTH_CHARS,
148 IDS_CLOUD_PRINT_SETUP_WIZARD_HEIGHT_LINES,
149 approximate_web_font);
150 }
151
152 #if !defined(OS_WIN)
153 // NOTE(scottbyer):The following comment comes from
154 // SyncSetupFlow::GetDialogSize, where this hack was copied from. By starting
155 // off development of the UI on Windows, the hack is seemingly backwards.
156
157 // NOTE(akalin): This is a hack to work around a problem with font height on
158 // windows. Basically font metrics are incorrectly returned in logical units
159 // instead of pixels on Windows. Logical units are very commonly 96 DPI
160 // so our localized char/line counts are too small by a factor of 96/72.
161 // So we compensate for this on non-windows platform.
162
163 // TODO(scottbyer): Fix the root cause, kill the hacks.
164 float scale_hack = 96.f/72.f;
165 size->set_width(size->width() * scale_hack);
166 size->set_height(size->height() * scale_hack);
167 #endif
168 }
169
170 // A callback to notify the delegate that the dialog closed.
OnDialogClosed(const std::string & json_retval)171 void CloudPrintSetupFlow::OnDialogClosed(const std::string& json_retval) {
172 // If we are fetching the token then cancel the request.
173 if (authenticator_.get())
174 authenticator_->CancelRequest();
175
176 if (delegate_)
177 delegate_->OnDialogClosed();
178 delete this;
179 }
180
GetDialogArgs() const181 std::string CloudPrintSetupFlow::GetDialogArgs() const {
182 return dialog_start_args_;
183 }
184
OnCloseContents(TabContents * source,bool * out_close_dialog)185 void CloudPrintSetupFlow::OnCloseContents(TabContents* source,
186 bool* out_close_dialog) {
187 }
188
GetDialogTitle() const189 std::wstring CloudPrintSetupFlow::GetDialogTitle() const {
190 return UTF16ToWideHack(
191 l10n_util::GetStringUTF16(IDS_CLOUD_PRINT_SETUP_DIALOG_TITLE));
192 }
193
IsDialogModal() const194 bool CloudPrintSetupFlow::IsDialogModal() const {
195 // We are always modeless.
196 return false;
197 }
198
ShouldShowDialogTitle() const199 bool CloudPrintSetupFlow::ShouldShowDialogTitle() const {
200 return true;
201 }
202
203 ///////////////////////////////////////////////////////////////////////////////
204 // GaiaAuthConsumer implementation.
OnClientLoginSuccess(const GaiaAuthConsumer::ClientLoginResult & credentials)205 void CloudPrintSetupFlow::OnClientLoginSuccess(
206 const GaiaAuthConsumer::ClientLoginResult& credentials) {
207 // Save the token for the cloud print proxy.
208 lsid_ = credentials.lsid;
209
210 // Show that Gaia login has succeeded.
211 ShowGaiaSuccessAndSettingUp();
212 authenticator_.reset();
213
214 profile_->GetCloudPrintProxyService()->EnableForUser(credentials.lsid,
215 login_);
216 // TODO(sanjeevr): Should we wait and verify that the enable succeeded?
217 ShowSetupDone();
218 }
219
OnClientLoginFailure(const GoogleServiceAuthError & error)220 void CloudPrintSetupFlow::OnClientLoginFailure(
221 const GoogleServiceAuthError& error) {
222 last_auth_error_ = error;
223 ShowGaiaFailed(error);
224 authenticator_.reset();
225 }
226
227 ///////////////////////////////////////////////////////////////////////////////
228 // Methods called by CloudPrintSetupMessageHandler
Attach(WebUI * web_ui)229 void CloudPrintSetupFlow::Attach(WebUI* web_ui) {
230 web_ui_ = web_ui;
231 }
232
OnUserSubmittedAuth(const std::string & user,const std::string & password,const std::string & captcha,const std::string & access_code)233 void CloudPrintSetupFlow::OnUserSubmittedAuth(const std::string& user,
234 const std::string& password,
235 const std::string& captcha,
236 const std::string& access_code) {
237 // Save the login name only.
238 login_ = user;
239
240 // Start the authenticator.
241 authenticator_.reset(
242 new GaiaAuthFetcher(this, GaiaConstants::kChromeSource,
243 profile_->GetRequestContext()));
244
245 if (!access_code.empty()) {
246 authenticator_->StartClientLogin(user, access_code,
247 GaiaConstants::kCloudPrintService,
248 std::string(), std::string(),
249 GaiaAuthFetcher::HostedAccountsAllowed);
250 } else {
251 authenticator_->StartClientLogin(user, password,
252 GaiaConstants::kCloudPrintService,
253 last_auth_error_.captcha().token, captcha,
254 GaiaAuthFetcher::HostedAccountsAllowed);
255 }
256 }
257
OnUserClickedLearnMore()258 void CloudPrintSetupFlow::OnUserClickedLearnMore() {
259 web_ui_->tab_contents()->OpenURL(CloudPrintURL::GetCloudPrintLearnMoreURL(),
260 GURL(), NEW_FOREGROUND_TAB,
261 PageTransition::LINK);
262 }
263
OnUserClickedPrintTestPage()264 void CloudPrintSetupFlow::OnUserClickedPrintTestPage() {
265 web_ui_->tab_contents()->OpenURL(CloudPrintURL::GetCloudPrintTestPageURL(),
266 GURL(), NEW_FOREGROUND_TAB,
267 PageTransition::LINK);
268 }
269
270 ///////////////////////////////////////////////////////////////////////////////
271 // Helper methods for showing contents of the Web UI
ShowGaiaLogin(const DictionaryValue & args)272 void CloudPrintSetupFlow::ShowGaiaLogin(const DictionaryValue& args) {
273 if (web_ui_)
274 web_ui_->CallJavascriptFunction("cloudprint.showSetupLogin");
275
276 std::string json;
277 base::JSONWriter::Write(&args, false, &json);
278 string16 javascript = UTF8ToUTF16("cloudprint.showGaiaLogin(" + json + ");");
279
280 ExecuteJavascriptInIFrame(SetupIframeXPath(), javascript);
281 }
282
ShowGaiaSuccessAndSettingUp()283 void CloudPrintSetupFlow::ShowGaiaSuccessAndSettingUp() {
284 ExecuteJavascriptInIFrame(
285 SetupIframeXPath(),
286 ASCIIToUTF16("cloudprint.showGaiaSuccessAndSettingUp();"));
287 }
288
ShowGaiaFailed(const GoogleServiceAuthError & error)289 void CloudPrintSetupFlow::ShowGaiaFailed(const GoogleServiceAuthError& error) {
290 DictionaryValue args;
291 args.SetString("pageToShow", "cloudprintsetup");
292 args.SetString("user", login_);
293 args.SetInteger("error", error.state());
294 args.SetBoolean("editable_user", true);
295 args.SetString("captchaUrl", error.captcha().image_url.spec());
296 ShowGaiaLogin(args);
297 }
298
ShowSetupDone()299 void CloudPrintSetupFlow::ShowSetupDone() {
300 setup_done_ = true;
301 string16 product_name = l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
302 string16 message = l10n_util::GetStringFUTF16(IDS_CLOUD_PRINT_SETUP_DONE,
303 product_name,
304 UTF8ToUTF16(login_));
305 string16 javascript(ASCIIToUTF16("cloudprint.setMessage('") + message +
306 ASCIIToUTF16("');"));
307 ExecuteJavascriptInIFrame(DoneIframeXPath(), javascript);
308
309 if (web_ui_) {
310 PrefService* prefs = profile_->GetPrefs();
311 gfx::Font approximate_web_font(
312 UTF8ToUTF16(prefs->GetString(prefs::kWebKitSansSerifFontFamily)),
313 prefs->GetInteger(prefs::kWebKitDefaultFontSize));
314 gfx::Size done_size = ui::GetLocalizedContentsSizeForFont(
315 IDS_CLOUD_PRINT_SETUP_WIZARD_DONE_WIDTH_CHARS,
316 IDS_CLOUD_PRINT_SETUP_WIZARD_DONE_HEIGHT_LINES,
317 approximate_web_font);
318
319 FundamentalValue new_width(done_size.width());
320 FundamentalValue new_height(done_size.height());
321 web_ui_->CallJavascriptFunction("cloudprint.showSetupDone",
322 new_width, new_height);
323 }
324
325 ExecuteJavascriptInIFrame(DoneIframeXPath(),
326 ASCIIToUTF16("cloudprint.onPageShown();"));
327 }
328
ExecuteJavascriptInIFrame(const string16 & iframe_xpath,const string16 & js)329 void CloudPrintSetupFlow::ExecuteJavascriptInIFrame(
330 const string16& iframe_xpath,
331 const string16& js) {
332 if (web_ui_) {
333 RenderViewHost* rvh = web_ui_->tab_contents()->render_view_host();
334 rvh->ExecuteJavascriptInWebFrame(iframe_xpath, js);
335 }
336 }
337