1// Copyright (c) 2012 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/ui/webui/set_as_default_browser_ui.h"
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/memory/weak_ptr.h"
10#include "base/metrics/histogram.h"
11#include "base/path_service.h"
12#include "base/prefs/pref_service.h"
13#include "chrome/browser/profiles/profile.h"
14#include "chrome/browser/shell_integration.h"
15#include "chrome/browser/ui/browser.h"
16#include "chrome/browser/ui/browser_dialogs.h"
17#include "chrome/browser/ui/browser_finder.h"
18#include "chrome/browser/ui/browser_list.h"
19#include "chrome/browser/ui/browser_list_observer.h"
20#include "chrome/browser/ui/browser_window.h"
21#include "chrome/browser/ui/chrome_pages.h"
22#include "chrome/browser/ui/singleton_tabs.h"
23#include "chrome/browser/ui/sync/sync_promo_ui.h"
24#include "chrome/browser/ui/tabs/tab_strip_model.h"
25#include "chrome/common/pref_names.h"
26#include "chrome/common/url_constants.h"
27#include "chrome/grit/generated_resources.h"
28#include "chrome/grit/locale_settings.h"
29#include "chrome/installer/util/install_util.h"
30#include "content/public/browser/browser_thread.h"
31#include "content/public/browser/web_contents.h"
32#include "content/public/browser/web_contents_delegate.h"
33#include "content/public/browser/web_ui.h"
34#include "content/public/browser/web_ui_data_source.h"
35#include "content/public/browser/web_ui_message_handler.h"
36#include "grit/browser_resources.h"
37#include "ui/base/l10n/l10n_font_util.h"
38#include "ui/base/l10n/l10n_util.h"
39#include "ui/gfx/font.h"
40#include "ui/views/widget/widget.h"
41#include "ui/web_dialogs/web_dialog_delegate.h"
42
43using content::BrowserThread;
44using content::WebContents;
45using content::WebUIMessageHandler;
46
47namespace {
48
49const char kSetAsDefaultBrowserHistogram[] = "DefaultBrowser.InteractionResult";
50
51// The enum permits registering in UMA the three possible outcomes (do not
52// reorder these).
53// ACCEPTED: user pressed Next and made Chrome default.
54// DECLINED: user simply closed the dialog without making Chrome default.
55// REGRETTED: user pressed Next but then elected a different default browser.
56enum MakeChromeDefaultResult {
57  MAKE_CHROME_DEFAULT_ACCEPTED = 0,
58  MAKE_CHROME_DEFAULT_DECLINED = 1,
59  MAKE_CHROME_DEFAULT_REGRETTED = 2,
60  // MAKE_CHROME_DEFAULT_ACCEPTED_IMMERSE = 3,  // Deprecated.
61  MAKE_CHROME_DEFAULT_MAX
62};
63
64content::WebUIDataSource* CreateSetAsDefaultBrowserUIHTMLSource() {
65  content::WebUIDataSource* data_source = content::WebUIDataSource::Create(
66      chrome::kChromeUIMetroFlowHost);
67  data_source->SetUseJsonJSFormatV2();
68  data_source->AddLocalizedString("page-title", IDS_METRO_FLOW_TAB_TITLE);
69  data_source->AddLocalizedString("flowTitle", IDS_METRO_FLOW_TITLE_SHORT);
70  data_source->AddLocalizedString("flowDescription",
71                                  IDS_METRO_FLOW_DESCRIPTION);
72  data_source->AddLocalizedString("flowNext",
73                                  IDS_METRO_FLOW_SET_DEFAULT);
74  data_source->AddLocalizedString("chromeLogoString",
75                                  IDS_METRO_FLOW_LOGO_STRING_ALT);
76  data_source->SetJsonPath("strings.js");
77  data_source->AddResourcePath("set_as_default_browser.js",
78                               IDR_SET_AS_DEFAULT_BROWSER_JS);
79  data_source->SetDefaultResource(IDR_SET_AS_DEFAULT_BROWSER_HTML);
80  return data_source;
81}
82
83// A simple class serving as a delegate for passing down the result of the
84// interaction.
85class ResponseDelegate {
86 public:
87  virtual void SetDialogInteractionResult(MakeChromeDefaultResult result) = 0;
88
89 protected:
90  virtual ~ResponseDelegate() { }
91};
92
93// Event handler for SetAsDefaultBrowserUI. Capable of setting Chrome as the
94// default browser on button click, closing itself and triggering Chrome
95// restart.
96class SetAsDefaultBrowserHandler
97    : public WebUIMessageHandler,
98      public base::SupportsWeakPtr<SetAsDefaultBrowserHandler>,
99      public ShellIntegration::DefaultWebClientObserver {
100 public:
101  explicit SetAsDefaultBrowserHandler(
102      const base::WeakPtr<ResponseDelegate>& response_delegate);
103  virtual ~SetAsDefaultBrowserHandler();
104
105  // WebUIMessageHandler implementation.
106  virtual void RegisterMessages() OVERRIDE;
107
108  // ShellIntegration::DefaultWebClientObserver implementation.
109  virtual void SetDefaultWebClientUIState(
110      ShellIntegration::DefaultWebClientUIState state) OVERRIDE;
111  virtual void OnSetAsDefaultConcluded(bool close_chrome)  OVERRIDE;
112  virtual bool IsInteractiveSetDefaultPermitted() OVERRIDE;
113
114 private:
115  // Handler for the 'Next' (or 'make Chrome the Metro browser') button.
116  void HandleLaunchSetDefaultBrowserFlow(const base::ListValue* args);
117
118  // Close this web ui.
119  void ConcludeInteraction(MakeChromeDefaultResult interaction_result);
120
121  scoped_refptr<ShellIntegration::DefaultBrowserWorker> default_browser_worker_;
122  bool set_default_returned_;
123  bool set_default_result_;
124  base::WeakPtr<ResponseDelegate> response_delegate_;
125
126  DISALLOW_COPY_AND_ASSIGN(SetAsDefaultBrowserHandler);
127};
128
129SetAsDefaultBrowserHandler::SetAsDefaultBrowserHandler(
130    const base::WeakPtr<ResponseDelegate>& response_delegate)
131    : default_browser_worker_(new ShellIntegration::DefaultBrowserWorker(this)),
132      set_default_returned_(false), set_default_result_(false),
133      response_delegate_(response_delegate) {
134}
135
136SetAsDefaultBrowserHandler::~SetAsDefaultBrowserHandler() {
137  default_browser_worker_->ObserverDestroyed();
138}
139
140void SetAsDefaultBrowserHandler::RegisterMessages() {
141  web_ui()->RegisterMessageCallback(
142      "SetAsDefaultBrowser:LaunchSetDefaultBrowserFlow",
143      base::Bind(&SetAsDefaultBrowserHandler::HandleLaunchSetDefaultBrowserFlow,
144                 base::Unretained(this)));
145}
146
147void SetAsDefaultBrowserHandler::SetDefaultWebClientUIState(
148    ShellIntegration::DefaultWebClientUIState state) {
149  // The callback is expected to be invoked once the procedure has completed.
150  DCHECK_CURRENTLY_ON(BrowserThread::UI);
151  if (!set_default_returned_)
152    return;
153
154  if (state == ShellIntegration::STATE_NOT_DEFAULT && set_default_result_) {
155    // The operation concluded, but Chrome is still not the default.
156    // If the call has succeeded, this suggests user has decided not to make
157    // chrome the default.
158    ConcludeInteraction(MAKE_CHROME_DEFAULT_REGRETTED);
159  } else if (state == ShellIntegration::STATE_IS_DEFAULT) {
160    ConcludeInteraction(MAKE_CHROME_DEFAULT_ACCEPTED);
161  }
162
163  // Otherwise, keep the dialog open since the user probably didn't make a
164  // choice.
165}
166
167void SetAsDefaultBrowserHandler::OnSetAsDefaultConcluded(bool call_result) {
168  set_default_returned_ = true;
169  set_default_result_ = call_result;
170}
171
172bool SetAsDefaultBrowserHandler::IsInteractiveSetDefaultPermitted() {
173  return true;
174}
175
176void SetAsDefaultBrowserHandler::HandleLaunchSetDefaultBrowserFlow(
177    const base::ListValue* args) {
178  set_default_returned_ = false;
179  set_default_result_ = false;
180  default_browser_worker_->StartSetAsDefault();
181}
182
183void SetAsDefaultBrowserHandler::ConcludeInteraction(
184    MakeChromeDefaultResult interaction_result) {
185  DCHECK_CURRENTLY_ON(BrowserThread::UI);
186
187  if (response_delegate_)
188    response_delegate_->SetDialogInteractionResult(interaction_result);
189
190  WebContents* contents = web_ui()->GetWebContents();
191
192  if (contents) {
193    content::WebContentsDelegate* delegate = contents->GetDelegate();
194    if (delegate)
195      delegate->CloseContents(contents);
196  }
197}
198
199// A web dialog delegate implementation for when 'Make Chrome Metro' UI
200// is displayed on a dialog.
201class SetAsDefaultBrowserDialogImpl : public ui::WebDialogDelegate,
202                                      public ResponseDelegate,
203                                      public chrome::BrowserListObserver {
204 public:
205  SetAsDefaultBrowserDialogImpl(Profile* profile, Browser* browser);
206  virtual ~SetAsDefaultBrowserDialogImpl();
207  // Show a modal web dialog with kChromeUIMetroFlowURL page.
208  void ShowDialog();
209
210 protected:
211  // Overridden from WebDialogDelegate:
212  virtual ui::ModalType GetDialogModalType() const OVERRIDE;
213  virtual base::string16 GetDialogTitle() const OVERRIDE;
214  virtual GURL GetDialogContentURL() const OVERRIDE;
215  virtual void GetWebUIMessageHandlers(
216      std::vector<WebUIMessageHandler*>* handlers) const OVERRIDE;
217  virtual void GetDialogSize(gfx::Size* size) const OVERRIDE;
218  virtual std::string GetDialogArgs() const OVERRIDE;
219  virtual void OnDialogClosed(const std::string& json_retval) OVERRIDE;
220  virtual void OnCloseContents(WebContents* source,
221                               bool* out_close_dialog) OVERRIDE;
222  virtual bool ShouldShowDialogTitle() const OVERRIDE;
223  virtual bool HandleContextMenu(
224      const content::ContextMenuParams& params) OVERRIDE;
225
226  // Overridden from ResponseDelegate:
227  virtual void SetDialogInteractionResult(MakeChromeDefaultResult result);
228
229  // Overridden from BrowserListObserver:
230  virtual void OnBrowserRemoved(Browser* browser) OVERRIDE;
231
232 private:
233  Profile* profile_;
234  Browser* browser_;
235  mutable bool owns_handler_;
236  base::WeakPtrFactory<ResponseDelegate> response_delegate_ptr_factory_;
237  SetAsDefaultBrowserHandler* handler_;
238  MakeChromeDefaultResult dialog_interaction_result_;
239
240  DISALLOW_COPY_AND_ASSIGN(SetAsDefaultBrowserDialogImpl);
241};
242
243SetAsDefaultBrowserDialogImpl::SetAsDefaultBrowserDialogImpl(Profile* profile,
244                                                             Browser* browser)
245    : profile_(profile),
246      browser_(browser),
247      owns_handler_(true),
248      response_delegate_ptr_factory_(this),
249      handler_(new SetAsDefaultBrowserHandler(
250          response_delegate_ptr_factory_.GetWeakPtr())),
251      dialog_interaction_result_(MAKE_CHROME_DEFAULT_DECLINED) {
252  BrowserList::AddObserver(this);
253}
254
255SetAsDefaultBrowserDialogImpl::~SetAsDefaultBrowserDialogImpl() {
256  if (browser_)
257    BrowserList::RemoveObserver(this);
258  if (owns_handler_)
259    delete handler_;
260}
261
262void SetAsDefaultBrowserDialogImpl::ShowDialog() {
263  // Use a NULL parent window to make sure that the dialog will have an item
264  // in the Windows task bar. The code below will make it highlight if the
265  // dialog is not in the foreground.
266  gfx::NativeWindow native_window = chrome::ShowWebDialog(NULL, profile_, this);
267  views::Widget* widget = views::Widget::GetWidgetForNativeWindow(
268      native_window);
269  widget->FlashFrame(true);
270}
271
272ui::ModalType SetAsDefaultBrowserDialogImpl::GetDialogModalType() const {
273  return ui::MODAL_TYPE_SYSTEM;
274}
275
276base::string16 SetAsDefaultBrowserDialogImpl::GetDialogTitle() const {
277  return l10n_util::GetStringUTF16(IDS_METRO_FLOW_TAB_TITLE);
278}
279
280GURL SetAsDefaultBrowserDialogImpl::GetDialogContentURL() const {
281  std::string url_string(chrome::kChromeUIMetroFlowURL);
282  return GURL(url_string);
283}
284
285void SetAsDefaultBrowserDialogImpl::GetWebUIMessageHandlers(
286    std::vector<WebUIMessageHandler*>* handlers) const {
287  handlers->push_back(handler_);
288  owns_handler_ = false;
289}
290
291void SetAsDefaultBrowserDialogImpl::GetDialogSize(gfx::Size* size) const {
292  PrefService* prefs = profile_->GetPrefs();
293  gfx::Font approximate_web_font(
294      prefs->GetString(prefs::kWebKitSansSerifFontFamily),
295      prefs->GetInteger(prefs::kWebKitDefaultFontSize));
296
297  *size = ui::GetLocalizedContentsSizeForFont(
298      IDS_METRO_FLOW_WIDTH_CHARS, IDS_METRO_FLOW_HEIGHT_LINES,
299      approximate_web_font);
300}
301
302std::string SetAsDefaultBrowserDialogImpl::GetDialogArgs() const {
303  return "[]";
304}
305
306void SetAsDefaultBrowserDialogImpl::OnDialogClosed(
307    const std::string& json_retval) {
308  // Register the user's response in UMA.
309  UMA_HISTOGRAM_ENUMERATION(kSetAsDefaultBrowserHistogram,
310                            dialog_interaction_result_,
311                            MAKE_CHROME_DEFAULT_MAX);
312
313  // If the user explicitly elected *not to* make Chrome default, we won't
314  // ask again.
315  if (dialog_interaction_result_ == MAKE_CHROME_DEFAULT_REGRETTED) {
316    PrefService* prefs = profile_->GetPrefs();
317    prefs->SetBoolean(prefs::kCheckDefaultBrowser, false);
318  }
319
320  // Carry on with a normal chrome session. For the purpose of surfacing this
321  // dialog the actual browser window had to remain hidden. Now it's time to
322  // show it.
323  if (browser_) {
324    BrowserWindow* window = browser_->window();
325    WebContents* contents = browser_->tab_strip_model()->GetActiveWebContents();
326    window->Show();
327    if (contents)
328      contents->SetInitialFocus();
329  }
330
331  delete this;
332}
333
334void SetAsDefaultBrowserDialogImpl::OnCloseContents(WebContents* source,
335                                                    bool* out_close_dialog) {
336  *out_close_dialog = true;
337}
338
339bool SetAsDefaultBrowserDialogImpl::ShouldShowDialogTitle() const {
340  return true;
341}
342
343bool SetAsDefaultBrowserDialogImpl::HandleContextMenu(
344    const content::ContextMenuParams& params) {
345  return true;
346}
347
348void SetAsDefaultBrowserDialogImpl::SetDialogInteractionResult(
349    MakeChromeDefaultResult result) {
350  dialog_interaction_result_ = result;
351}
352
353void SetAsDefaultBrowserDialogImpl::OnBrowserRemoved(Browser* browser) {
354  if (browser_ == browser) {
355    browser_ = NULL;
356    BrowserList::RemoveObserver(this);
357  }
358}
359
360}  // namespace
361
362SetAsDefaultBrowserUI::SetAsDefaultBrowserUI(content::WebUI* web_ui)
363    : ui::WebDialogUI(web_ui) {
364  content::WebUIDataSource::Add(
365      Profile::FromWebUI(web_ui), CreateSetAsDefaultBrowserUIHTMLSource());
366}
367
368// static
369void SetAsDefaultBrowserUI::Show(Profile* profile, Browser* browser) {
370  DCHECK_CURRENTLY_ON(BrowserThread::UI);
371  SetAsDefaultBrowserDialogImpl* dialog =
372      new SetAsDefaultBrowserDialogImpl(profile, browser);
373  dialog->ShowDialog();
374}
375