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