Home | History | Annotate | Download | only in cloud_print
      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