cdadcf225f7a530e33cee2b76bdc792289c5616d
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / login / ui / webui_login_view.cc
1 // Copyright 2014 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/chromeos/login/ui/webui_login_view.h"
6
7 #include "ash/shell.h"
8 #include "ash/system/tray/system_tray.h"
9 #include "base/bind.h"
10 #include "base/callback.h"
11 #include "base/debug/trace_event.h"
12 #include "base/i18n/rtl.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/values.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "chrome/browser/chromeos/accessibility/accessibility_util.h"
17 #include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
18 #include "chrome/browser/chromeos/login/ui/proxy_settings_dialog.h"
19 #include "chrome/browser/chromeos/login/ui/webui_login_display.h"
20 #include "chrome/browser/chromeos/profiles/profile_helper.h"
21 #include "chrome/browser/extensions/chrome_extension_web_contents_observer.h"
22 #include "chrome/browser/media/media_stream_infobar_delegate.h"
23 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
24 #include "chrome/browser/renderer_preferences_util.h"
25 #include "chrome/browser/sessions/session_tab_helper.h"
26 #include "chrome/browser/ui/autofill/chrome_autofill_client.h"
27 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
28 #include "chrome/common/render_messages.h"
29 #include "chromeos/dbus/dbus_thread_manager.h"
30 #include "chromeos/dbus/session_manager_client.h"
31 #include "chromeos/network/network_state.h"
32 #include "chromeos/network/network_state_handler.h"
33 #include "components/password_manager/core/browser/password_manager.h"
34 #include "components/web_modal/web_contents_modal_dialog_manager.h"
35 #include "content/public/browser/notification_service.h"
36 #include "content/public/browser/render_view_host.h"
37 #include "content/public/browser/render_widget_host_view.h"
38 #include "content/public/browser/web_contents.h"
39 #include "content/public/browser/web_ui.h"
40 #include "third_party/WebKit/public/web/WebInputEvent.h"
41 #include "ui/gfx/rect.h"
42 #include "ui/gfx/size.h"
43 #include "ui/views/controls/webview/webview.h"
44 #include "ui/views/widget/widget.h"
45
46 using content::NativeWebKeyboardEvent;
47 using content::RenderViewHost;
48 using content::WebContents;
49 using web_modal::WebContentsModalDialogManager;
50
51 namespace {
52
53 // These strings must be kept in sync with handleAccelerator()
54 // in display_manager.js.
55 const char kAccelNameCancel[] = "cancel";
56 const char kAccelNameEnrollment[] = "enrollment";
57 const char kAccelNameKioskEnable[] = "kiosk_enable";
58 const char kAccelNameVersion[] = "version";
59 const char kAccelNameReset[] = "reset";
60 const char kAccelFocusPrev[] = "focus_prev";
61 const char kAccelFocusNext[] = "focus_next";
62 const char kAccelNameDeviceRequisition[] = "device_requisition";
63 const char kAccelNameDeviceRequisitionRemora[] = "device_requisition_remora";
64 const char kAccelNameAppLaunchBailout[] = "app_launch_bailout";
65 const char kAccelNameAppLaunchNetworkConfig[] = "app_launch_network_config";
66
67 // A class to change arrow key traversal behavior when it's alive.
68 class ScopedArrowKeyTraversal {
69  public:
70   explicit ScopedArrowKeyTraversal(bool new_arrow_key_tranversal_enabled)
71       : previous_arrow_key_traversal_enabled_(
72             views::FocusManager::arrow_key_traversal_enabled()) {
73     views::FocusManager::set_arrow_key_traversal_enabled(
74         new_arrow_key_tranversal_enabled);
75   }
76   ~ScopedArrowKeyTraversal() {
77     views::FocusManager::set_arrow_key_traversal_enabled(
78         previous_arrow_key_traversal_enabled_);
79   }
80
81  private:
82   const bool previous_arrow_key_traversal_enabled_;
83   DISALLOW_COPY_AND_ASSIGN(ScopedArrowKeyTraversal);
84 };
85
86 }  // namespace
87
88 namespace chromeos {
89
90 // static
91 const char WebUILoginView::kViewClassName[] =
92     "browser/chromeos/login/WebUILoginView";
93
94 // WebUILoginView public: ------------------------------------------------------
95
96 WebUILoginView::WebUILoginView()
97     : webui_login_(NULL),
98       is_hidden_(false),
99       webui_visible_(false),
100       should_emit_login_prompt_visible_(true),
101       forward_keyboard_event_(true) {
102   registrar_.Add(this,
103                  chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
104                  content::NotificationService::AllSources());
105   registrar_.Add(this,
106                  chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN,
107                  content::NotificationService::AllSources());
108
109   accel_map_[ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE)] =
110       kAccelNameCancel;
111   accel_map_[ui::Accelerator(ui::VKEY_E,
112                              ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)] =
113       kAccelNameEnrollment;
114   accel_map_[ui::Accelerator(ui::VKEY_K,
115                              ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)] =
116       kAccelNameKioskEnable;
117   accel_map_[ui::Accelerator(ui::VKEY_V, ui::EF_ALT_DOWN)] =
118       kAccelNameVersion;
119   accel_map_[ui::Accelerator(ui::VKEY_R,
120       ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN)] =
121       kAccelNameReset;
122
123   accel_map_[ui::Accelerator(ui::VKEY_LEFT, ui::EF_NONE)] =
124       kAccelFocusPrev;
125   accel_map_[ui::Accelerator(ui::VKEY_RIGHT, ui::EF_NONE)] =
126       kAccelFocusNext;
127
128   // Use KEY_RELEASED because Gaia consumes KEY_PRESSED for up/down key.
129   ui::Accelerator key_up(ui::VKEY_UP, ui::EF_NONE);
130   key_up.set_type(ui::ET_KEY_RELEASED);
131   ui::Accelerator key_down(ui::VKEY_DOWN, ui::EF_NONE);
132   key_down.set_type(ui::ET_KEY_RELEASED);
133   accel_map_[key_up] = kAccelFocusPrev;
134   accel_map_[key_down] = kAccelFocusNext;
135
136   accel_map_[ui::Accelerator(
137       ui::VKEY_D, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN)] =
138       kAccelNameDeviceRequisition;
139   accel_map_[
140       ui::Accelerator(ui::VKEY_H, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)] =
141       kAccelNameDeviceRequisitionRemora;
142
143   accel_map_[ui::Accelerator(ui::VKEY_S,
144                              ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)] =
145       kAccelNameAppLaunchBailout;
146
147   accel_map_[ui::Accelerator(ui::VKEY_N,
148                              ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)] =
149       kAccelNameAppLaunchNetworkConfig;
150
151   for (AccelMap::iterator i(accel_map_.begin()); i != accel_map_.end(); ++i)
152     AddAccelerator(i->first);
153 }
154
155 WebUILoginView::~WebUILoginView() {
156   FOR_EACH_OBSERVER(web_modal::ModalDialogHostObserver,
157                     observer_list_,
158                     OnHostDestroying());
159
160   if (ash::Shell::GetInstance()->HasPrimaryStatusArea()) {
161     ash::Shell::GetInstance()->GetPrimarySystemTray()->
162         SetNextFocusableView(NULL);
163   }
164 }
165
166 void WebUILoginView::Init() {
167   Profile* signin_profile = ProfileHelper::GetSigninProfile();
168   auth_extension_.reset(new ScopedGaiaAuthExtension(signin_profile));
169   webui_login_ = new views::WebView(signin_profile);
170   webui_login_->set_allow_accelerators(true);
171   AddChildView(webui_login_);
172
173   WebContents* web_contents = webui_login_->GetWebContents();
174
175   // Ensure that the login UI has a tab ID, which will allow the GAIA auth
176   // extension's background script to tell it apart from a captive portal window
177   // that may be opened on top of this UI.
178   SessionTabHelper::CreateForWebContents(web_contents);
179
180   // Create the password manager that is needed for the proxy.
181   ChromePasswordManagerClient::CreateForWebContentsWithAutofillClient(
182       web_contents,
183       autofill::ChromeAutofillClient::FromWebContents(web_contents));
184
185   // LoginHandlerViews uses a constrained window for the password manager view.
186   WebContentsModalDialogManager::CreateForWebContents(web_contents);
187   WebContentsModalDialogManager::FromWebContents(web_contents)->
188       SetDelegate(this);
189
190   web_contents->SetDelegate(this);
191   extensions::ChromeExtensionWebContentsObserver::CreateForWebContents(
192       web_contents);
193   WebContentsObserver::Observe(web_contents);
194   renderer_preferences_util::UpdateFromSystemSettings(
195       web_contents->GetMutableRendererPrefs(),
196       signin_profile);
197 }
198
199 const char* WebUILoginView::GetClassName() const {
200   return kViewClassName;
201 }
202
203 void WebUILoginView::RequestFocus() {
204   webui_login_->RequestFocus();
205 }
206
207 web_modal::WebContentsModalDialogHost*
208     WebUILoginView::GetWebContentsModalDialogHost() {
209   return this;
210 }
211
212 gfx::NativeView WebUILoginView::GetHostView() const {
213   return GetWidget()->GetNativeView();
214 }
215
216 gfx::Point WebUILoginView::GetDialogPosition(const gfx::Size& size) {
217   // Center the widget.
218   gfx::Size widget_size = GetWidget()->GetWindowBoundsInScreen().size();
219   return gfx::Point(widget_size.width() / 2 - size.width() / 2,
220                     widget_size.height() / 2 - size.height() / 2);
221 }
222
223 gfx::Size WebUILoginView::GetMaximumDialogSize() {
224   return GetWidget()->GetWindowBoundsInScreen().size();
225 }
226
227 void WebUILoginView::AddObserver(
228     web_modal::ModalDialogHostObserver* observer) {
229   if (observer && !observer_list_.HasObserver(observer))
230     observer_list_.AddObserver(observer);
231 }
232
233 void WebUILoginView::RemoveObserver(
234     web_modal::ModalDialogHostObserver* observer) {
235   observer_list_.RemoveObserver(observer);
236 }
237
238 bool WebUILoginView::AcceleratorPressed(
239     const ui::Accelerator& accelerator) {
240   AccelMap::const_iterator entry = accel_map_.find(accelerator);
241   if (entry == accel_map_.end())
242     return false;
243
244   if (!webui_login_)
245     return true;
246
247   content::WebUI* web_ui = GetWebUI();
248   if (web_ui) {
249     base::StringValue accel_name(entry->second);
250     web_ui->CallJavascriptFunction("cr.ui.Oobe.handleAccelerator",
251                                    accel_name);
252   }
253
254   return true;
255 }
256
257 gfx::NativeWindow WebUILoginView::GetNativeWindow() const {
258   return GetWidget()->GetNativeWindow();
259 }
260
261 void WebUILoginView::LoadURL(const GURL & url) {
262   webui_login_->LoadInitialURL(url);
263   webui_login_->RequestFocus();
264
265   // TODO(nkostylev): Use WebContentsObserver::RenderViewCreated to track
266   // when RenderView is created.
267   GetWebContents()->GetRenderViewHost()->GetView()->SetBackgroundOpaque(false);
268 }
269
270 content::WebUI* WebUILoginView::GetWebUI() {
271   return webui_login_->web_contents()->GetWebUI();
272 }
273
274 content::WebContents* WebUILoginView::GetWebContents() {
275   return webui_login_->web_contents();
276 }
277
278 void WebUILoginView::OpenProxySettings() {
279   const NetworkState* network =
280       NetworkHandler::Get()->network_state_handler()->DefaultNetwork();
281   if (!network) {
282     LOG(ERROR) << "No default network found!";
283     return;
284   }
285   ProxySettingsDialog* dialog =
286       new ProxySettingsDialog(ProfileHelper::GetSigninProfile(),
287                               *network, NULL, GetNativeWindow());
288   dialog->Show();
289 }
290
291 void WebUILoginView::OnPostponedShow() {
292   set_is_hidden(false);
293   OnLoginPromptVisible();
294 }
295
296 void WebUILoginView::SetStatusAreaVisible(bool visible) {
297   if (ash::Shell::GetInstance()->HasPrimaryStatusArea()) {
298     ash::SystemTray* tray = ash::Shell::GetInstance()->GetPrimarySystemTray();
299     if (visible) {
300       // Tray may have been initialized being hidden.
301       tray->SetVisible(visible);
302       tray->GetWidget()->Show();
303     } else {
304       tray->GetWidget()->Hide();
305     }
306   }
307 }
308
309 void WebUILoginView::SetUIEnabled(bool enabled) {
310   forward_keyboard_event_ = enabled;
311   ash::Shell::GetInstance()->GetPrimarySystemTray()->SetEnabled(enabled);
312 }
313
314 // WebUILoginView protected: ---------------------------------------------------
315
316 void WebUILoginView::Layout() {
317   DCHECK(webui_login_);
318   webui_login_->SetBoundsRect(bounds());
319
320   FOR_EACH_OBSERVER(web_modal::ModalDialogHostObserver,
321                     observer_list_,
322                     OnPositionRequiresUpdate());
323 }
324
325 void WebUILoginView::OnLocaleChanged() {
326 }
327
328 void WebUILoginView::ChildPreferredSizeChanged(View* child) {
329   Layout();
330   SchedulePaint();
331 }
332
333 void WebUILoginView::AboutToRequestFocusFromTabTraversal(bool reverse) {
334   // Return the focus to the web contents.
335   webui_login_->web_contents()->FocusThroughTabTraversal(reverse);
336   GetWidget()->Activate();
337   webui_login_->web_contents()->Focus();
338 }
339
340 void WebUILoginView::Observe(int type,
341                              const content::NotificationSource& source,
342                              const content::NotificationDetails& details) {
343   switch (type) {
344     case chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE:
345     case chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN: {
346       OnLoginPromptVisible();
347       registrar_.RemoveAll();
348       break;
349     }
350     default:
351       NOTREACHED() << "Unexpected notification " << type;
352   }
353 }
354
355 // WebUILoginView private: -----------------------------------------------------
356
357 bool WebUILoginView::HandleContextMenu(
358     const content::ContextMenuParams& params) {
359   // Do not show the context menu.
360 #ifndef NDEBUG
361   return false;
362 #else
363   return true;
364 #endif
365 }
366
367 void WebUILoginView::HandleKeyboardEvent(content::WebContents* source,
368                                          const NativeWebKeyboardEvent& event) {
369   if (forward_keyboard_event_) {
370     // Disable arrow key traversal because arrow keys are handled via
371     // accelerator when this view has focus.
372     ScopedArrowKeyTraversal arrow_key_traversal(false);
373
374     unhandled_keyboard_event_handler_.HandleKeyboardEvent(event,
375                                                           GetFocusManager());
376   }
377
378   // Make sure error bubble is cleared on keyboard event. This is needed
379   // when the focus is inside an iframe. Only clear on KeyDown to prevent hiding
380   // an immediate authentication error (See crbug.com/103643).
381   if (event.type == blink::WebInputEvent::KeyDown) {
382     content::WebUI* web_ui = GetWebUI();
383     if (web_ui)
384       web_ui->CallJavascriptFunction("cr.ui.Oobe.clearErrors");
385   }
386 }
387
388 bool WebUILoginView::IsPopupOrPanel(const WebContents* source) const {
389   return true;
390 }
391
392 bool WebUILoginView::TakeFocus(content::WebContents* source, bool reverse) {
393   // In case of blocked UI (ex.: sign in is in progress)
394   // we should not process focus change events.
395   if (!forward_keyboard_event_)
396     return false;
397
398   ash::SystemTray* tray = ash::Shell::GetInstance()->GetPrimarySystemTray();
399   if (tray && tray->GetWidget()->IsVisible()) {
400     tray->SetNextFocusableView(this);
401     ash::Shell::GetInstance()->RotateFocus(reverse ? ash::Shell::BACKWARD :
402                                                      ash::Shell::FORWARD);
403   }
404
405   return true;
406 }
407
408 void WebUILoginView::RequestMediaAccessPermission(
409     WebContents* web_contents,
410     const content::MediaStreamRequest& request,
411     const content::MediaResponseCallback& callback) {
412   if (MediaStreamInfoBarDelegate::Create(web_contents, request, callback))
413     NOTREACHED() << "Media stream not allowed for WebUI";
414 }
415
416 bool WebUILoginView::PreHandleGestureEvent(
417     content::WebContents* source,
418     const blink::WebGestureEvent& event) {
419   // Disable pinch zooming.
420   return event.type == blink::WebGestureEvent::GesturePinchBegin ||
421       event.type == blink::WebGestureEvent::GesturePinchUpdate ||
422       event.type == blink::WebGestureEvent::GesturePinchEnd;
423 }
424
425 void WebUILoginView::DidFailProvisionalLoad(
426     int64 frame_id,
427     const base::string16& frame_unique_name,
428     bool is_main_frame,
429     const GURL& validated_url,
430     int error_code,
431     const base::string16& error_description,
432     content::RenderViewHost* render_view_host) {
433   if (frame_unique_name != base::UTF8ToUTF16("gaia-frame"))
434     return;
435
436   GetWebUI()->CallJavascriptFunction("login.GaiaSigninScreen.onFrameError",
437                                      base::FundamentalValue(-error_code),
438                                      base::StringValue(validated_url.spec()));
439 }
440
441 void WebUILoginView::OnLoginPromptVisible() {
442   // If we're hidden than will generate this signal once we're shown.
443   if (is_hidden_ || webui_visible_) {
444     LOG(WARNING) << "Login WebUI >> not emitting signal, hidden: "
445                  << is_hidden_;
446     return;
447   }
448   TRACE_EVENT0("chromeos", "WebUILoginView::OnLoginPromoptVisible");
449   if (should_emit_login_prompt_visible_) {
450     LOG(WARNING) << "Login WebUI >> login-prompt-visible";
451     chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->
452         EmitLoginPromptVisible();
453   }
454
455   webui_visible_ = true;
456 }
457
458 void WebUILoginView::ReturnFocus(bool reverse) {
459   // Return the focus to the web contents.
460   webui_login_->web_contents()->FocusThroughTabTraversal(reverse);
461   GetWidget()->Activate();
462 }
463
464 }  // namespace chromeos