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
39namespace {
40
41string16& SetupIframeXPath() {
42  static string16 kSetupIframeXPath =
43      ASCIIToUTF16("//iframe[@id='cloudprintsetup']");
44  return kSetupIframeXPath;
45}
46
47string16& 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
59CloudPrintSetupFlow* 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
94CloudPrintSetupFlow::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
111CloudPrintSetupFlow::~CloudPrintSetupFlow() {
112}
113
114void CloudPrintSetupFlow::Focus() {
115  // TODO(pranavk): implement this method.
116  NOTIMPLEMENTED();
117}
118
119///////////////////////////////////////////////////////////////////////////////
120// HtmlDialogUIDelegate implementation.
121GURL CloudPrintSetupFlow::GetDialogContentURL() const {
122  return GURL("chrome://cloudprintsetup/setupflow");
123}
124
125void 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
134void 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.
171void 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
181std::string CloudPrintSetupFlow::GetDialogArgs() const {
182  return dialog_start_args_;
183}
184
185void CloudPrintSetupFlow::OnCloseContents(TabContents* source,
186                                          bool* out_close_dialog) {
187}
188
189std::wstring CloudPrintSetupFlow::GetDialogTitle() const {
190  return UTF16ToWideHack(
191      l10n_util::GetStringUTF16(IDS_CLOUD_PRINT_SETUP_DIALOG_TITLE));
192}
193
194bool CloudPrintSetupFlow::IsDialogModal() const {
195  // We are always modeless.
196  return false;
197}
198
199bool CloudPrintSetupFlow::ShouldShowDialogTitle() const {
200  return true;
201}
202
203///////////////////////////////////////////////////////////////////////////////
204// GaiaAuthConsumer implementation.
205void 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
220void 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
229void CloudPrintSetupFlow::Attach(WebUI* web_ui) {
230  web_ui_ = web_ui;
231}
232
233void 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
258void CloudPrintSetupFlow::OnUserClickedLearnMore() {
259  web_ui_->tab_contents()->OpenURL(CloudPrintURL::GetCloudPrintLearnMoreURL(),
260                                   GURL(), NEW_FOREGROUND_TAB,
261                                   PageTransition::LINK);
262}
263
264void 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
272void 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
283void CloudPrintSetupFlow::ShowGaiaSuccessAndSettingUp() {
284  ExecuteJavascriptInIFrame(
285      SetupIframeXPath(),
286      ASCIIToUTF16("cloudprint.showGaiaSuccessAndSettingUp();"));
287}
288
289void 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
299void 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
329void 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