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