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