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