// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/remoting/remoting_setup_flow.h" #include "app/gfx/font_util.h" #include "base/json/json_writer.h" #include "base/singleton.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "base/values.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/dom_ui/chrome_url_data_manager.h" #include "chrome/browser/dom_ui/dom_ui_util.h" #include "chrome/browser/platform_util.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profile.h" #include "chrome/browser/remoting/remoting_resources_source.h" #include "chrome/browser/remoting/remoting_setup_message_handler.h" #include "chrome/browser/renderer_host/render_view_host.h" #include "chrome/browser/service/service_process_control.h" #include "chrome/browser/service/service_process_control_manager.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/common/net/gaia/gaia_auth_fetcher.h" #include "chrome/common/net/gaia/gaia_constants.h" #include "chrome/common/net/gaia/google_service_auth_error.h" #include "chrome/common/pref_names.h" #include "gfx/font.h" #include "grit/locale_settings.h" static const wchar_t kLoginIFrameXPath[] = L"//iframe[@id='login']"; static const wchar_t kDoneIframeXPath[] = L"//iframe[@id='done']"; //////////////////////////////////////////////////////////////////////////////// // RemotingServiceProcessHelper // // This is a helper class to perform actions when the service process // is connected or launched. The events are sent back to RemotingSetupFlow // when the dialog is still active. RemotingSetupFlow can detach from this // helper class when the dialog is closed. class RemotingServiceProcessHelper : public base::RefCountedThreadSafe { public: explicit RemotingServiceProcessHelper(RemotingSetupFlow* flow) : flow_(flow) { } void Detach() { flow_ = NULL; } void OnProcessLaunched() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); // If the flow is detached then show the done page. if (!flow_) return; flow_->OnProcessLaunched(); } private: RemotingSetupFlow* flow_; DISALLOW_COPY_AND_ASSIGN(RemotingServiceProcessHelper); }; //////////////////////////////////////////////////////////////////////////////// // RemotingSetupFlow implementation. // static RemotingSetupFlow* RemotingSetupFlow::OpenDialog(Profile* profile) { // Set the arguments for showing the gaia login page. DictionaryValue args; args.SetString("iframeToShow", "login"); args.SetString("user", ""); args.SetInteger("error", 0); args.SetBoolean("editable_user", true); if (profile->GetPrefs()->GetBoolean(prefs::kRemotingHasSetupCompleted)) { args.SetString("iframeToShow", "done"); } std::string json_args; base::JSONWriter::Write(&args, false, &json_args); Browser* b = BrowserList::GetLastActive(); if (!b) return NULL; RemotingSetupFlow* flow = new RemotingSetupFlow(json_args, profile); b->BrowserShowHtmlDialog(flow, NULL); return flow; } RemotingSetupFlow::RemotingSetupFlow(const std::string& args, Profile* profile) : dom_ui_(NULL), dialog_start_args_(args), profile_(profile), process_control_(NULL) { // TODO(hclam): The data source should be added once. BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, NewRunnableMethod(Singleton::get(), &ChromeURLDataManager::AddDataSource, make_scoped_refptr(new RemotingResourcesSource()))); } RemotingSetupFlow::~RemotingSetupFlow() { } void RemotingSetupFlow::Focus() { // TODO(pranavk): implement this method. NOTIMPLEMENTED(); } /////////////////////////////////////////////////////////////////////////////// // HtmlDialogUIDelegate implementation. GURL RemotingSetupFlow::GetDialogContentURL() const { return GURL("chrome://remotingresources/setup"); } void RemotingSetupFlow::GetDOMMessageHandlers( std::vector* handlers) const { // Create the message handler only after we are asked, the caller is // responsible for deleting the objects. handlers->push_back( new RemotingSetupMessageHandler(const_cast(this))); } void RemotingSetupFlow::GetDialogSize(gfx::Size* size) const { PrefService* prefs = profile_->GetPrefs(); gfx::Font approximate_web_font( UTF8ToWide(prefs->GetString(prefs::kWebKitSansSerifFontFamily)), prefs->GetInteger(prefs::kWebKitDefaultFontSize)); // TODO(pranavk) Replace the following SYNC resources with REMOTING Resources. *size = gfx::GetLocalizedContentsSizeForFont( IDS_SYNC_SETUP_WIZARD_WIDTH_CHARS, IDS_SYNC_SETUP_WIZARD_HEIGHT_LINES, approximate_web_font); } // A callback to notify the delegate that the dialog closed. void RemotingSetupFlow::OnDialogClosed(const std::string& json_retval) { // If we are fetching the token then cancel the request. if (authenticator_.get()) authenticator_->CancelRequest(); // If the service process helper is still active then detach outself from it. // This is because the dialog is closing and this object is going to be // deleted but the service process launch is still in progress so we don't // the service process helper to call us when the process is launched. if (service_process_helper_.get()) service_process_helper_->Detach(); delete this; } std::string RemotingSetupFlow::GetDialogArgs() const { return dialog_start_args_; } void RemotingSetupFlow::OnCloseContents(TabContents* source, bool* out_close_dialog) { } std::wstring RemotingSetupFlow::GetDialogTitle() const { return l10n_util::GetString(IDS_REMOTING_SETUP_DIALOG_TITLE); } bool RemotingSetupFlow::IsDialogModal() const { return true; } bool RemotingSetupFlow::ShouldShowDialogTitle() const { return true; } /////////////////////////////////////////////////////////////////////////////// // GaiaAuthConsumer implementation. void RemotingSetupFlow::OnClientLoginFailure( const GoogleServiceAuthError& error) { ShowGaiaFailed(error); authenticator_.reset(); } void RemotingSetupFlow::OnClientLoginSuccess( const GaiaAuthConsumer::ClientLoginResult& credentials) { // Save the token for remoting. remoting_token_ = credentials.token; // After login has succeeded try to fetch the token for sync. // We need the token for sync to connect to the talk network. authenticator_->StartIssueAuthToken(credentials.sid, credentials.lsid, GaiaConstants::kSyncService); } void RemotingSetupFlow::OnIssueAuthTokenSuccess(const std::string& service, const std::string& auth_token) { // Show that Gaia login has succeeded. ShowGaiaSuccessAndSettingUp(); // Save the sync token. sync_token_ = auth_token; authenticator_.reset(); // And then launch the service process if it has not started yet. // If we have already connected to the service process then submit the tokens // to it to register the host. process_control_ = ServiceProcessControlManager::instance()->GetProcessControl(profile_); if (process_control_->is_connected()) { // TODO(hclam): Need to figure out what to do when the service process is // already connected. } else { #if defined(OS_WIN) // TODO(hclam): This call only works on Windows. I need to make it work // on other platforms. service_process_helper_ = new RemotingServiceProcessHelper(this); Task* process_launched_task = NewRunnableMethod(service_process_helper_.get(), &RemotingServiceProcessHelper::OnProcessLaunched); process_control_->Launch(process_launched_task, process_launched_task); #else ShowSetupDone(); #endif } } void RemotingSetupFlow::OnIssueAuthTokenFailure(const std::string& service, const GoogleServiceAuthError& error) { ShowGaiaFailed(error); authenticator_.reset(); } /////////////////////////////////////////////////////////////////////////////// // Methods called by RemotingSetupMessageHandler void RemotingSetupFlow::Attach(DOMUI* dom_ui) { dom_ui_ = dom_ui; } void RemotingSetupFlow::OnUserSubmittedAuth(const std::string& user, const std::string& password, const std::string& captcha) { // Save the login name only. login_ = user; // Start the authenticator. authenticator_.reset( new GaiaAuthFetcher(this, GaiaConstants::kChromeSource, profile_->GetRequestContext())); authenticator_->StartClientLogin(user, password, GaiaConstants::kRemotingService, "", captcha, GaiaAuthFetcher::HostedAccountsAllowed); } /////////////////////////////////////////////////////////////////////////////// // Method called by RemotingServicePRocessHelper void RemotingSetupFlow::OnProcessLaunched() { DCHECK(process_control_->is_connected()); // TODO(hclam): Need to wait for an ACK to be sure that it is actually active. process_control_->EnableRemotingWithTokens(login_, remoting_token_, sync_token_); // Save the preference that we have completed the setup of remoting. profile_->GetPrefs()->SetBoolean(prefs::kRemotingHasSetupCompleted, true); ShowSetupDone(); } /////////////////////////////////////////////////////////////////////////////// // Helper methods for showing contents of the DOM UI void RemotingSetupFlow::ShowGaiaLogin(const DictionaryValue& args) { if (dom_ui_) dom_ui_->CallJavascriptFunction(L"showGaiaLoginIframe"); std::string json; base::JSONWriter::Write(&args, false, &json); std::wstring javascript = std::wstring(L"showGaiaLogin") + L"(" + UTF8ToWide(json) + L");"; ExecuteJavascriptInIFrame(kLoginIFrameXPath, javascript); } void RemotingSetupFlow::ShowGaiaSuccessAndSettingUp() { ExecuteJavascriptInIFrame(kLoginIFrameXPath, L"showGaiaSuccessAndSettingUp();"); } void RemotingSetupFlow::ShowGaiaFailed(const GoogleServiceAuthError& error) { DictionaryValue args; args.SetString("iframeToShow", "login"); args.SetString("user", ""); args.SetInteger("error", error.state()); args.SetBoolean("editable_user", true); args.SetString("captchaUrl", error.captcha().image_url.spec()); ShowGaiaLogin(args); } void RemotingSetupFlow::ShowSetupDone() { std::wstring javascript = L"setMessage('You are all set!');"; ExecuteJavascriptInIFrame(kDoneIframeXPath, javascript); if (dom_ui_) dom_ui_->CallJavascriptFunction(L"showSetupDone"); ExecuteJavascriptInIFrame(kDoneIframeXPath, L"onPageShown();"); } void RemotingSetupFlow::ExecuteJavascriptInIFrame( const std::wstring& iframe_xpath, const std::wstring& js) { if (dom_ui_) { RenderViewHost* rvh = dom_ui_->tab_contents()->render_view_host(); rvh->ExecuteJavascriptInWebFrame(iframe_xpath, js); } }