cloud_print_setup_flow.cc revision 3f50c38dc070f4bb515c1b64450dae14f316474e
1// Copyright (c) 2010 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 "app/gfx/font_util.h"
8#include "base/json/json_writer.h"
9#include "base/singleton.h"
10#include "base/string_util.h"
11#include "base/utf_string_conversions.h"
12#include "base/values.h"
13#include "chrome/browser/browser_list.h"
14#include "chrome/browser/browser_thread.h"
15#include "chrome/browser/dom_ui/chrome_url_data_manager.h"
16#include "chrome/browser/dom_ui/dom_ui_util.h"
17#if defined(TOOLKIT_GTK)
18#include "chrome/browser/gtk/html_dialog_gtk.h"
19#endif  // defined(TOOLKIT_GTK)
20#include "chrome/browser/platform_util.h"
21#include "chrome/browser/prefs/pref_service.h"
22#include "chrome/browser/printing/cloud_print/cloud_print_proxy_service.h"
23#include "chrome/browser/printing/cloud_print/cloud_print_setup_message_handler.h"
24#include "chrome/browser/printing/cloud_print/cloud_print_setup_source.h"
25#include "chrome/browser/printing/cloud_print/cloud_print_url.h"
26#include "chrome/browser/profiles/profile.h"
27#include "chrome/browser/renderer_host/render_view_host.h"
28#include "chrome/browser/service/service_process_control.h"
29#include "chrome/browser/service/service_process_control_manager.h"
30#include "chrome/browser/tab_contents/tab_contents.h"
31#include "chrome/browser/ui/browser.h"
32#include "chrome/browser/ui/browser_window.h"
33#if defined(TOOLKIT_VIEWS)
34#include "chrome/browser/views/browser_dialogs.h"
35#endif  // defined(TOOLKIT_GTK)
36#include "chrome/common/net/gaia/gaia_auth_fetcher.h"
37#include "chrome/common/net/gaia/gaia_constants.h"
38#include "chrome/common/net/gaia/google_service_auth_error.h"
39#include "chrome/common/pref_names.h"
40#include "chrome/common/service_messages.h"
41#include "gfx/font.h"
42
43#include "grit/chromium_strings.h"
44#include "grit/locale_settings.h"
45
46static const wchar_t kGaiaLoginIFrameXPath[] = L"//iframe[@id='gaialogin']";
47static const wchar_t kDoneIframeXPath[] = L"//iframe[@id='setupdone']";
48
49////////////////////////////////////////////////////////////////////////////////
50// CloudPrintSetupFlow implementation.
51
52// static
53CloudPrintSetupFlow* CloudPrintSetupFlow::OpenDialog(
54    Profile* profile, Delegate* delegate, gfx::NativeWindow parent_window) {
55  // Set the arguments for showing the gaia login page.
56  DictionaryValue args;
57  args.SetString("user", "");
58  args.SetInteger("error", 0);
59  args.SetBoolean("editable_user", true);
60
61  bool setup_done = false;
62  if (profile->GetPrefs()->HasPrefPath(prefs::kCloudPrintEmail) &&
63      !profile->GetPrefs()->GetString(prefs::kCloudPrintEmail).empty())
64    setup_done = true;
65  args.SetString("pageToShow", setup_done ? "setupdone" : "cloudprintsetup");
66
67  std::string json_args;
68  base::JSONWriter::Write(&args, false, &json_args);
69
70  CloudPrintSetupFlow* flow = new CloudPrintSetupFlow(json_args, profile,
71                                                      delegate, setup_done);
72  // We may not always have a browser. This can happen when we are being
73  // invoked in the context of a "token expired" notfication. If we don't have
74  // a brower, use the underlying dialog system to show the dialog without
75  // using a browser.
76  Browser* browser = BrowserList::GetLastActive();
77  if (browser) {
78    browser->BrowserShowHtmlDialog(flow, parent_window);
79  } else {
80#if defined(TOOLKIT_VIEWS)
81    browser::ShowHtmlDialogView(parent_window, profile, flow);
82#elif defined(TOOLKIT_GTK)
83    HtmlDialogGtk* html_dialog =
84        new HtmlDialogGtk(profile, flow, parent_window);
85    html_dialog->InitDialog();
86#endif  // defined(TOOLKIT_VIEWS)
87  // TODO(sanjeevr): Implement the "no browser" scenario for the Mac.
88  }
89  return flow;
90}
91
92CloudPrintSetupFlow::CloudPrintSetupFlow(const std::string& args,
93                                         Profile* profile,
94                                         Delegate* delegate,
95                                         bool setup_done)
96    : dom_ui_(NULL),
97      dialog_start_args_(args),
98      setup_done_(setup_done),
99      process_control_(NULL),
100      delegate_(delegate) {
101  // TODO(hclam): The data source should be added once.
102  profile_ = profile;
103  BrowserThread::PostTask(
104      BrowserThread::IO, FROM_HERE,
105      NewRunnableMethod(ChromeURLDataManager::GetInstance(),
106                        &ChromeURLDataManager::AddDataSource,
107                        make_scoped_refptr(new CloudPrintSetupSource())));
108}
109
110CloudPrintSetupFlow::~CloudPrintSetupFlow() {
111}
112
113void CloudPrintSetupFlow::Focus() {
114  // TODO(pranavk): implement this method.
115  NOTIMPLEMENTED();
116}
117
118///////////////////////////////////////////////////////////////////////////////
119// HtmlDialogUIDelegate implementation.
120GURL CloudPrintSetupFlow::GetDialogContentURL() const {
121  return GURL("chrome://cloudprintsetup/setupflow");
122}
123
124void CloudPrintSetupFlow::GetDOMMessageHandlers(
125    std::vector<DOMMessageHandler*>* handlers) const {
126  // Create the message handler only after we are asked, the caller is
127  // responsible for deleting the objects.
128  handlers->push_back(
129      new CloudPrintSetupMessageHandler(
130          const_cast<CloudPrintSetupFlow*>(this)));
131}
132
133void CloudPrintSetupFlow::GetDialogSize(gfx::Size* size) const {
134  PrefService* prefs = profile_->GetPrefs();
135  gfx::Font approximate_web_font(
136      UTF8ToWide(prefs->GetString(prefs::kWebKitSansSerifFontFamily)),
137      prefs->GetInteger(prefs::kWebKitDefaultFontSize));
138
139  if (setup_done_) {
140    *size = gfx::GetLocalizedContentsSizeForFont(
141        IDS_CLOUD_PRINT_SETUP_WIZARD_DONE_WIDTH_CHARS,
142        IDS_CLOUD_PRINT_SETUP_WIZARD_DONE_HEIGHT_LINES,
143        approximate_web_font);
144  } else {
145    *size = gfx::GetLocalizedContentsSizeForFont(
146        IDS_CLOUD_PRINT_SETUP_WIZARD_WIDTH_CHARS,
147        IDS_CLOUD_PRINT_SETUP_WIZARD_HEIGHT_LINES,
148        approximate_web_font);
149  }
150}
151
152// A callback to notify the delegate that the dialog closed.
153void CloudPrintSetupFlow::OnDialogClosed(const std::string& json_retval) {
154  // If we are fetching the token then cancel the request.
155  if (authenticator_.get())
156    authenticator_->CancelRequest();
157
158  if (delegate_) {
159    delegate_->OnDialogClosed();
160  }
161  delete this;
162}
163
164std::string CloudPrintSetupFlow::GetDialogArgs() const {
165    return dialog_start_args_;
166}
167
168void CloudPrintSetupFlow::OnCloseContents(TabContents* source,
169                                          bool* out_close_dialog) {
170}
171
172std::wstring CloudPrintSetupFlow::GetDialogTitle() const {
173  return UTF16ToWideHack(
174      l10n_util::GetStringUTF16(IDS_CLOUD_PRINT_SETUP_DIALOG_TITLE));
175}
176
177bool CloudPrintSetupFlow::IsDialogModal() const {
178  // We are always modeless.
179  return false;
180}
181
182bool CloudPrintSetupFlow::ShouldShowDialogTitle() const {
183  return true;
184}
185
186///////////////////////////////////////////////////////////////////////////////
187// GaiaAuthConsumer implementation.
188void CloudPrintSetupFlow::OnClientLoginFailure(
189    const GoogleServiceAuthError& error) {
190  ShowGaiaFailed(error);
191  authenticator_.reset();
192}
193
194void CloudPrintSetupFlow::OnClientLoginSuccess(
195    const GaiaAuthConsumer::ClientLoginResult& credentials) {
196  // Save the token for the cloud print proxy.
197  lsid_ = credentials.lsid;
198
199  // Show that Gaia login has succeeded.
200  ShowGaiaSuccessAndSettingUp();
201  authenticator_.reset();
202
203  profile_->GetCloudPrintProxyService()->EnableForUser(credentials.lsid,
204                                                       login_);
205  // TODO(sanjeevr): Should we wait and verify that the enable succeeded?
206  ShowSetupDone();
207}
208
209///////////////////////////////////////////////////////////////////////////////
210// Methods called by CloudPrintSetupMessageHandler
211void CloudPrintSetupFlow::Attach(DOMUI* dom_ui) {
212  dom_ui_ = dom_ui;
213}
214
215void CloudPrintSetupFlow::OnUserSubmittedAuth(const std::string& user,
216                                              const std::string& password,
217                                              const std::string& captcha) {
218  // Save the login name only.
219  login_ = user;
220
221  // Start the authenticator.
222  authenticator_.reset(
223      new GaiaAuthFetcher(this, GaiaConstants::kChromeSource,
224                          profile_->GetRequestContext()));
225
226  authenticator_->StartClientLogin(user, password,
227                                   GaiaConstants::kCloudPrintService,
228                                   "", captcha,
229                                   GaiaAuthFetcher::HostedAccountsAllowed);
230}
231
232void CloudPrintSetupFlow::OnUserClickedLearnMore() {
233  dom_ui_->tab_contents()->OpenURL(CloudPrintURL::GetCloudPrintLearnMoreURL(),
234                                   GURL(), NEW_FOREGROUND_TAB,
235                                   PageTransition::LINK);
236}
237
238void CloudPrintSetupFlow::OnUserClickedPrintTestPage() {
239  dom_ui_->tab_contents()->OpenURL(CloudPrintURL::GetCloudPrintTestPageURL(),
240                                   GURL(), NEW_FOREGROUND_TAB,
241                                   PageTransition::LINK);
242}
243
244///////////////////////////////////////////////////////////////////////////////
245// Helper methods for showing contents of the DOM UI
246void CloudPrintSetupFlow::ShowGaiaLogin(const DictionaryValue& args) {
247  if (dom_ui_)
248    dom_ui_->CallJavascriptFunction(L"cloudprint.showSetupLogin");
249
250  std::string json;
251  base::JSONWriter::Write(&args, false, &json);
252  std::wstring javascript = std::wstring(L"showGaiaLogin") +
253      L"(" + UTF8ToWide(json) + L");";
254  ExecuteJavascriptInIFrame(kGaiaLoginIFrameXPath, javascript);
255}
256
257void CloudPrintSetupFlow::ShowGaiaSuccessAndSettingUp() {
258  ExecuteJavascriptInIFrame(kGaiaLoginIFrameXPath,
259                            L"showGaiaSuccessAndSettingUp();");
260}
261
262void CloudPrintSetupFlow::ShowGaiaFailed(const GoogleServiceAuthError& error) {
263  DictionaryValue args;
264  args.SetString("pageToShow", "cloudprintsetup");
265  args.SetString("user", "");
266  args.SetInteger("error", error.state());
267  args.SetBoolean("editable_user", true);
268  args.SetString("captchaUrl", error.captcha().image_url.spec());
269  ShowGaiaLogin(args);
270}
271
272void CloudPrintSetupFlow::ShowSetupDone() {
273  setup_done_ = true;
274  string16 product_name = l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
275  std::wstring message =
276      UTF16ToWideHack(l10n_util::GetStringFUTF16(IDS_CLOUD_PRINT_SETUP_DONE,
277                                                 product_name,
278                                                 UTF8ToUTF16(login_)));
279  std::wstring javascript = L"cloudprint.setMessage('" + message + L"');";
280  ExecuteJavascriptInIFrame(kDoneIframeXPath, javascript);
281
282  if (dom_ui_) {
283    PrefService* prefs = profile_->GetPrefs();
284    gfx::Font approximate_web_font(
285        UTF8ToWide(prefs->GetString(prefs::kWebKitSansSerifFontFamily)),
286        prefs->GetInteger(prefs::kWebKitDefaultFontSize));
287    gfx::Size done_size = gfx::GetLocalizedContentsSizeForFont(
288        IDS_CLOUD_PRINT_SETUP_WIZARD_DONE_WIDTH_CHARS,
289        IDS_CLOUD_PRINT_SETUP_WIZARD_DONE_HEIGHT_LINES,
290        approximate_web_font);
291
292    FundamentalValue new_width(done_size.width());
293    FundamentalValue new_height(done_size.height());
294    dom_ui_->CallJavascriptFunction(L"cloudprint.showSetupDone",
295                                    new_width, new_height);
296  }
297
298  ExecuteJavascriptInIFrame(kDoneIframeXPath, L"cloudprint.onPageShown();");
299}
300
301void CloudPrintSetupFlow::ExecuteJavascriptInIFrame(
302    const std::wstring& iframe_xpath,
303    const std::wstring& js) {
304  if (dom_ui_) {
305    RenderViewHost* rvh = dom_ui_->tab_contents()->render_view_host();
306    rvh->ExecuteJavascriptInWebFrame(iframe_xpath, js);
307  }
308}
309