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/remoting/setup_flow.h"
6
7#include "base/json/json_reader.h"
8#include "base/json/json_writer.h"
9#include "base/utf_string_conversions.h"
10#include "base/values.h"
11#include "chrome/browser/prefs/pref_service.h"
12#include "chrome/browser/profiles/profile.h"
13#include "chrome/browser/remoting/remoting_resources_source.h"
14#include "chrome/browser/remoting/setup_flow_login_step.h"
15#include "chrome/browser/ui/browser.h"
16#include "chrome/browser/ui/browser_list.h"
17#include "chrome/common/pref_names.h"
18#include "content/browser/browser_thread.h"
19#include "content/browser/renderer_host/render_view_host.h"
20#include "content/browser/tab_contents/tab_contents.h"
21#include "grit/generated_resources.h"
22#include "grit/locale_settings.h"
23#include "ui/base/l10n/l10n_font_util.h"
24#include "ui/base/l10n/l10n_util.h"
25#include "ui/gfx/font.h"
26
27namespace remoting {
28
29static const wchar_t kDoneIframeXPath[] = L"//iframe[@id='done']";
30static const wchar_t kErrorIframeXPath[] = L"//iframe[@id='error']";
31
32SetupFlowStep::SetupFlowStep() { }
33SetupFlowStep::~SetupFlowStep() { }
34
35SetupFlowStepBase::SetupFlowStepBase()
36    : flow_(NULL),
37      done_(false),
38      next_step_(NULL) {
39}
40
41SetupFlowStepBase::~SetupFlowStepBase() { }
42
43void SetupFlowStepBase::Start(SetupFlow* flow, DoneCallback* done_callback) {
44  done_callback_.reset(done_callback);
45  flow_ = flow;
46  DoStart();
47}
48
49SetupFlowStep* SetupFlowStepBase::GetNextStep() {
50  DCHECK(done_);
51  return next_step_;
52}
53
54void SetupFlowStepBase::ExecuteJavascriptInIFrame(
55    const std::wstring& iframe_xpath, const std::wstring& js) {
56  WebUI* web_ui = flow()->web_ui();
57  DCHECK(web_ui);
58
59  RenderViewHost* rvh = web_ui->tab_contents()->render_view_host();
60  rvh->ExecuteJavascriptInWebFrame(WideToUTF16Hack(iframe_xpath),
61                                   WideToUTF16Hack(js));
62}
63
64void SetupFlowStepBase::FinishStep(SetupFlowStep* next_step) {
65  next_step_ = next_step;
66  done_ = true;
67  done_callback_->Run();
68}
69
70SetupFlowErrorStepBase::SetupFlowErrorStepBase() { }
71SetupFlowErrorStepBase::~SetupFlowErrorStepBase() { }
72
73void SetupFlowErrorStepBase::HandleMessage(const std::string& message,
74                                           const Value* arg) {
75  if (message == "Retry") {
76    Retry();
77  }
78}
79
80void SetupFlowErrorStepBase::Cancel() { }
81
82void SetupFlowErrorStepBase::DoStart() {
83  std::wstring javascript =
84      L"setMessage('" + UTF16ToWide(GetErrorMessage()) + L"');";
85  ExecuteJavascriptInIFrame(kErrorIframeXPath, javascript);
86
87  flow()->web_ui()->CallJavascriptFunction("showError");
88
89  ExecuteJavascriptInIFrame(kErrorIframeXPath, L"onPageShown();");
90}
91
92SetupFlowDoneStep::SetupFlowDoneStep() {
93  message_ = l10n_util::GetStringUTF16(IDS_REMOTING_SUCCESS_MESSAGE);
94}
95
96SetupFlowDoneStep::SetupFlowDoneStep(const string16& message)
97    : message_(message) {
98}
99
100SetupFlowDoneStep::~SetupFlowDoneStep() { }
101
102void SetupFlowDoneStep::HandleMessage(const std::string& message,
103                                      const Value* arg) {
104}
105
106void SetupFlowDoneStep::Cancel() { }
107
108void SetupFlowDoneStep::DoStart() {
109  std::wstring javascript =
110      L"setMessage('" + UTF16ToWide(message_) + L"');";
111  ExecuteJavascriptInIFrame(kDoneIframeXPath, javascript);
112
113  flow()->web_ui()->CallJavascriptFunction("showSetupDone");
114
115  ExecuteJavascriptInIFrame(kDoneIframeXPath, L"onPageShown();");
116}
117
118SetupFlowContext::SetupFlowContext() { }
119SetupFlowContext::~SetupFlowContext() { }
120
121SetupFlow::SetupFlow(const std::string& args,
122                     Profile* profile,
123                     SetupFlowStep* first_step)
124    : web_ui_(NULL),
125      dialog_start_args_(args),
126      profile_(profile),
127      current_step_(first_step) {
128  // TODO(hclam): The data source should be added once.
129  profile->GetChromeURLDataManager()->AddDataSource(
130      new RemotingResourcesSource());
131}
132
133SetupFlow::~SetupFlow() { }
134
135// static
136SetupFlow* SetupFlow::OpenSetupDialog(Profile* profile) {
137  // Set the arguments for showing the gaia login page.
138  DictionaryValue args;
139  args.SetString("iframeToShow", "login");
140  args.SetString("user", "");
141  args.SetInteger("error", 0);
142  args.SetBoolean("editable_user", true);
143
144  std::string json_args;
145  base::JSONWriter::Write(&args, false, &json_args);
146
147  Browser* b = BrowserList::GetLastActive();
148  if (!b)
149    return NULL;
150
151  SetupFlow *flow = new SetupFlow(json_args, profile, new SetupFlowLoginStep());
152  b->BrowserShowHtmlDialog(flow, NULL);
153  return flow;
154}
155
156GURL SetupFlow::GetDialogContentURL() const {
157  return GURL("chrome://remotingresources/setup");
158}
159
160void SetupFlow::GetWebUIMessageHandlers(
161    std::vector<WebUIMessageHandler*>* handlers) const {
162  // The called will be responsible for deleting this object.
163  handlers->push_back(const_cast<SetupFlow*>(this));
164}
165
166void SetupFlow::GetDialogSize(gfx::Size* size) const {
167  PrefService* prefs = profile_->GetPrefs();
168  gfx::Font approximate_web_font(
169      UTF8ToUTF16(prefs->GetString(prefs::kWebKitSansSerifFontFamily)),
170      prefs->GetInteger(prefs::kWebKitDefaultFontSize));
171
172  // TODO(pranavk) Replace the following SYNC resources with REMOTING Resources.
173  *size = ui::GetLocalizedContentsSizeForFont(
174      IDS_REMOTING_SETUP_WIZARD_WIDTH_CHARS,
175      IDS_REMOTING_SETUP_WIZARD_HEIGHT_LINES,
176      approximate_web_font);
177}
178
179// A callback to notify the delegate that the dialog closed.
180void SetupFlow::OnDialogClosed(const std::string& json_retval) {
181  if (current_step_ != NULL)
182    current_step_->Cancel();
183}
184
185std::string SetupFlow::GetDialogArgs() const {
186    return dialog_start_args_;
187}
188
189void SetupFlow::OnCloseContents(TabContents* source,
190                                bool* out_close_dialog) {
191}
192
193std::wstring SetupFlow::GetDialogTitle() const {
194  return UTF16ToWideHack(
195      l10n_util::GetStringUTF16(IDS_REMOTING_SETUP_DIALOG_TITLE));
196}
197
198bool SetupFlow::IsDialogModal() const {
199  return false;
200}
201
202bool SetupFlow::ShouldShowDialogTitle() const {
203  return true;
204}
205
206WebUIMessageHandler* SetupFlow::Attach(WebUI* web_ui) {
207  web_ui_ = web_ui;
208  StartCurrentStep();
209  return WebUIMessageHandler::Attach(web_ui);
210}
211
212void SetupFlow::RegisterMessages() {
213  web_ui_->RegisterMessageCallback(
214      "SubmitAuth", NewCallback(this, &SetupFlow::HandleSubmitAuth));
215  web_ui_->RegisterMessageCallback(
216      "RemotingSetup", NewCallback(this, &SetupFlow::HandleUIMessage));
217}
218
219void SetupFlow::HandleSubmitAuth(const ListValue* args) {
220  Value* first_arg = NULL;
221  if (!args->Get(0, &first_arg)) {
222    NOTREACHED();
223    return;
224  }
225
226  current_step_->HandleMessage("SubmitAuth", first_arg);
227}
228
229void SetupFlow::HandleUIMessage(const ListValue* args) {
230  std::string message;
231  Value* message_value;
232  if (!args->Get(0, &message_value) ||
233      !message_value->GetAsString(&message)) {
234    NOTREACHED();
235    return;
236  }
237
238  // Message argument is optional and set to NULL if it is not
239  // provided by the sending page.
240  Value* arg_value = NULL;
241  if (args->GetSize() >= 2) {
242    if (!args->Get(1, &arg_value)) {
243      NOTREACHED();
244      return;
245    }
246  }
247
248  current_step_->HandleMessage(message, arg_value);
249}
250
251void SetupFlow::StartCurrentStep() {
252  current_step_->Start(this, NewCallback(this, &SetupFlow::OnStepDone));
253}
254
255void SetupFlow::OnStepDone() {
256  SetupFlowStep* next_step = current_step_->GetNextStep();
257
258  if (current_step_.get()) {
259    // Can't destroy current step here. Schedule it to be destroyed later.
260    BrowserThread::PostTask(
261        BrowserThread::UI, FROM_HERE,
262        new DeleteTask<SetupFlowStep>(current_step_.release()));
263  }
264
265  current_step_.reset(next_step);
266  StartCurrentStep();
267}
268
269}  // namespace remoting
270