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.
5 #include "chrome/browser/chromeos/login/webui_login_view.h"
8 #include "ash/system/tray/system_tray.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/login_display_host_impl.h"
18 #include "chrome/browser/chromeos/login/proxy_settings_dialog.h"
19 #include "chrome/browser/chromeos/login/webui_login_display.h"
20 #include "chrome/browser/chromeos/profiles/profile_helper.h"
21 #include "chrome/browser/extensions/extension_web_contents_observer.h"
22 #include "chrome/browser/media/media_stream_infobar_delegate.h"
23 #include "chrome/browser/password_manager/password_manager.h"
24 #include "chrome/browser/password_manager/password_manager_delegate_impl.h"
25 #include "chrome/browser/renderer_preferences_util.h"
26 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
27 #include "chrome/common/render_messages.h"
28 #include "chromeos/dbus/dbus_thread_manager.h"
29 #include "chromeos/dbus/session_manager_client.h"
30 #include "chromeos/network/network_state.h"
31 #include "chromeos/network/network_state_handler.h"
32 #include "components/web_modal/web_contents_modal_dialog_manager.h"
33 #include "content/public/browser/notification_service.h"
34 #include "content/public/browser/render_view_host.h"
35 #include "content/public/browser/render_widget_host_view.h"
36 #include "content/public/browser/web_contents.h"
37 #include "content/public/browser/web_contents_view.h"
38 #include "content/public/browser/web_ui.h"
39 #include "ui/gfx/rect.h"
40 #include "ui/gfx/size.h"
41 #include "ui/views/controls/webview/webview.h"
42 #include "ui/views/widget/widget.h"
44 using content::NativeWebKeyboardEvent
;
45 using content::RenderViewHost
;
46 using content::WebContents
;
47 using web_modal::WebContentsModalDialogManager
;
51 // These strings must be kept in sync with handleAccelerator()
52 // in display_manager.js.
53 const char kAccelNameCancel
[] = "cancel";
54 const char kAccelNameEnrollment
[] = "enrollment";
55 const char kAccelNameKioskEnable
[] = "kiosk_enable";
56 const char kAccelNameVersion
[] = "version";
57 const char kAccelNameReset
[] = "reset";
58 const char kAccelFocusPrev
[] = "focus_prev";
59 const char kAccelFocusNext
[] = "focus_next";
60 const char kAccelNameDeviceRequisition
[] = "device_requisition";
61 const char kAccelNameDeviceRequisitionRemora
[] = "device_requisition_remora";
62 const char kAccelNameAppLaunchBailout
[] = "app_launch_bailout";
64 // A class to change arrow key traversal behavior when it's alive.
65 class ScopedArrowKeyTraversal
{
67 explicit ScopedArrowKeyTraversal(bool new_arrow_key_tranversal_enabled
)
68 : previous_arrow_key_traversal_enabled_(
69 views::FocusManager::arrow_key_traversal_enabled()) {
70 views::FocusManager::set_arrow_key_traversal_enabled(
71 new_arrow_key_tranversal_enabled
);
73 ~ScopedArrowKeyTraversal() {
74 views::FocusManager::set_arrow_key_traversal_enabled(
75 previous_arrow_key_traversal_enabled_
);
79 const bool previous_arrow_key_traversal_enabled_
;
80 DISALLOW_COPY_AND_ASSIGN(ScopedArrowKeyTraversal
);
88 const char WebUILoginView::kViewClassName
[] =
89 "browser/chromeos/login/WebUILoginView";
91 // WebUILoginView public: ------------------------------------------------------
93 WebUILoginView::WebUILoginView()
96 webui_visible_(false),
97 should_emit_login_prompt_visible_(true),
98 forward_keyboard_event_(true) {
100 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE
,
101 content::NotificationService::AllSources());
103 chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN
,
104 content::NotificationService::AllSources());
106 accel_map_
[ui::Accelerator(ui::VKEY_ESCAPE
, ui::EF_NONE
)] =
108 accel_map_
[ui::Accelerator(ui::VKEY_E
,
109 ui::EF_CONTROL_DOWN
| ui::EF_ALT_DOWN
)] =
110 kAccelNameEnrollment
;
111 accel_map_
[ui::Accelerator(ui::VKEY_K
,
112 ui::EF_CONTROL_DOWN
| ui::EF_ALT_DOWN
)] =
113 kAccelNameKioskEnable
;
114 accel_map_
[ui::Accelerator(ui::VKEY_V
, ui::EF_ALT_DOWN
)] =
116 accel_map_
[ui::Accelerator(ui::VKEY_R
,
117 ui::EF_CONTROL_DOWN
| ui::EF_ALT_DOWN
| ui::EF_SHIFT_DOWN
)] =
120 accel_map_
[ui::Accelerator(ui::VKEY_LEFT
, ui::EF_NONE
)] =
122 accel_map_
[ui::Accelerator(ui::VKEY_RIGHT
, ui::EF_NONE
)] =
125 // Use KEY_RELEASED because Gaia consumes KEY_PRESSED for up/down key.
126 ui::Accelerator
key_up(ui::VKEY_UP
, ui::EF_NONE
);
127 key_up
.set_type(ui::ET_KEY_RELEASED
);
128 ui::Accelerator
key_down(ui::VKEY_DOWN
, ui::EF_NONE
);
129 key_down
.set_type(ui::ET_KEY_RELEASED
);
130 accel_map_
[key_up
] = kAccelFocusPrev
;
131 accel_map_
[key_down
] = kAccelFocusNext
;
133 accel_map_
[ui::Accelerator(
134 ui::VKEY_D
, ui::EF_CONTROL_DOWN
| ui::EF_ALT_DOWN
| ui::EF_SHIFT_DOWN
)] =
135 kAccelNameDeviceRequisition
;
137 ui::Accelerator(ui::VKEY_H
, ui::EF_CONTROL_DOWN
| ui::EF_ALT_DOWN
)] =
138 kAccelNameDeviceRequisitionRemora
;
140 accel_map_
[ui::Accelerator(ui::VKEY_S
,
141 ui::EF_CONTROL_DOWN
| ui::EF_ALT_DOWN
)] =
142 kAccelNameAppLaunchBailout
;
144 for (AccelMap::iterator
i(accel_map_
.begin()); i
!= accel_map_
.end(); ++i
)
145 AddAccelerator(i
->first
);
148 WebUILoginView::~WebUILoginView() {
149 FOR_EACH_OBSERVER(web_modal::ModalDialogHostObserver
,
153 if (ash::Shell::GetInstance()->HasPrimaryStatusArea()) {
154 ash::Shell::GetInstance()->GetPrimarySystemTray()->
155 SetNextFocusableView(NULL
);
159 void WebUILoginView::Init() {
160 Profile
* signin_profile
= ProfileHelper::GetSigninProfile();
161 auth_extension_
.reset(new ScopedGaiaAuthExtension(signin_profile
));
162 webui_login_
= new views::WebView(signin_profile
);
163 webui_login_
->set_allow_accelerators(true);
164 AddChildView(webui_login_
);
166 WebContents
* web_contents
= webui_login_
->GetWebContents();
168 // Create the password manager that is needed for the proxy.
169 PasswordManagerDelegateImpl::CreateForWebContents(web_contents
);
170 PasswordManager::CreateForWebContentsAndDelegate(
171 web_contents
, PasswordManagerDelegateImpl::FromWebContents(web_contents
));
173 // LoginHandlerViews uses a constrained window for the password manager view.
174 WebContentsModalDialogManager::CreateForWebContents(web_contents
);
175 WebContentsModalDialogManager::FromWebContents(web_contents
)->
178 web_contents
->SetDelegate(this);
179 extensions::ExtensionWebContentsObserver::CreateForWebContents(web_contents
);
180 WebContentsObserver::Observe(web_contents
);
181 renderer_preferences_util::UpdateFromSystemSettings(
182 web_contents
->GetMutableRendererPrefs(),
186 const char* WebUILoginView::GetClassName() const {
187 return kViewClassName
;
190 web_modal::WebContentsModalDialogHost
*
191 WebUILoginView::GetWebContentsModalDialogHost() {
195 gfx::NativeView
WebUILoginView::GetHostView() const {
196 return GetWidget()->GetNativeView();
199 gfx::Point
WebUILoginView::GetDialogPosition(const gfx::Size
& size
) {
200 // Center the widget.
201 gfx::Size widget_size
= GetWidget()->GetWindowBoundsInScreen().size();
202 return gfx::Point(widget_size
.width() / 2 - size
.width() / 2,
203 widget_size
.height() / 2 - size
.height() / 2);
206 gfx::Size
WebUILoginView::GetMaximumDialogSize() {
207 return GetWidget()->GetWindowBoundsInScreen().size();
210 void WebUILoginView::AddObserver(
211 web_modal::ModalDialogHostObserver
* observer
) {
212 if (observer
&& !observer_list_
.HasObserver(observer
))
213 observer_list_
.AddObserver(observer
);
216 void WebUILoginView::RemoveObserver(
217 web_modal::ModalDialogHostObserver
* observer
) {
218 observer_list_
.RemoveObserver(observer
);
221 bool WebUILoginView::AcceleratorPressed(
222 const ui::Accelerator
& accelerator
) {
223 AccelMap::const_iterator entry
= accel_map_
.find(accelerator
);
224 if (entry
== accel_map_
.end())
230 content::WebUI
* web_ui
= GetWebUI();
232 base::StringValue
accel_name(entry
->second
);
233 web_ui
->CallJavascriptFunction("cr.ui.Oobe.handleAccelerator",
240 gfx::NativeWindow
WebUILoginView::GetNativeWindow() const {
241 return GetWidget()->GetNativeWindow();
244 void WebUILoginView::LoadURL(const GURL
& url
) {
245 webui_login_
->LoadInitialURL(url
);
246 webui_login_
->RequestFocus();
248 // TODO(nkostylev): Use WebContentsObserver::RenderViewCreated to track
249 // when RenderView is created.
250 // Use a background with transparency to trigger transparency in Webkit.
252 background
.setConfig(SkBitmap::kARGB_8888_Config
, 1, 1);
253 background
.allocPixels();
254 background
.eraseARGB(0x00, 0x00, 0x00, 0x00);
255 content::RenderViewHost
* host
= GetWebContents()->GetRenderViewHost();
256 host
->GetView()->SetBackground(background
);
259 content::WebUI
* WebUILoginView::GetWebUI() {
260 return webui_login_
->web_contents()->GetWebUI();
263 content::WebContents
* WebUILoginView::GetWebContents() {
264 return webui_login_
->web_contents();
267 void WebUILoginView::OpenProxySettings() {
268 const NetworkState
* network
=
269 NetworkHandler::Get()->network_state_handler()->DefaultNetwork();
271 LOG(ERROR
) << "No default network found!";
274 ProxySettingsDialog
* dialog
=
275 new ProxySettingsDialog(*network
, NULL
, GetNativeWindow());
279 void WebUILoginView::OnPostponedShow() {
280 set_is_hidden(false);
281 OnLoginPromptVisible();
284 void WebUILoginView::SetStatusAreaVisible(bool visible
) {
285 if (ash::Shell::GetInstance()->HasPrimaryStatusArea()) {
286 ash::SystemTray
* tray
= ash::Shell::GetInstance()->GetPrimarySystemTray();
288 // Tray may have been initialized being hidden.
289 tray
->SetVisible(visible
);
290 tray
->GetWidget()->Show();
292 tray
->GetWidget()->Hide();
297 void WebUILoginView::SetUIEnabled(bool enabled
) {
298 forward_keyboard_event_
= enabled
;
299 ash::Shell::GetInstance()->GetPrimarySystemTray()->SetEnabled(enabled
);
302 // WebUILoginView protected: ---------------------------------------------------
304 void WebUILoginView::Layout() {
305 DCHECK(webui_login_
);
306 webui_login_
->SetBoundsRect(bounds());
308 FOR_EACH_OBSERVER(web_modal::ModalDialogHostObserver
,
310 OnPositionRequiresUpdate());
313 void WebUILoginView::OnLocaleChanged() {
316 void WebUILoginView::ChildPreferredSizeChanged(View
* child
) {
321 void WebUILoginView::AboutToRequestFocusFromTabTraversal(bool reverse
) {
322 // Return the focus to the web contents.
323 webui_login_
->web_contents()->FocusThroughTabTraversal(reverse
);
324 GetWidget()->Activate();
325 webui_login_
->web_contents()->GetView()->Focus();
328 void WebUILoginView::Observe(int type
,
329 const content::NotificationSource
& source
,
330 const content::NotificationDetails
& details
) {
332 case chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE
:
333 case chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN
: {
334 OnLoginPromptVisible();
335 registrar_
.RemoveAll();
339 NOTREACHED() << "Unexpected notification " << type
;
343 // WebUILoginView private: -----------------------------------------------------
345 bool WebUILoginView::HandleContextMenu(
346 const content::ContextMenuParams
& params
) {
347 // Do not show the context menu.
355 void WebUILoginView::HandleKeyboardEvent(content::WebContents
* source
,
356 const NativeWebKeyboardEvent
& event
) {
357 if (forward_keyboard_event_
) {
358 // Disable arrow key traversal because arrow keys are handled via
359 // accelerator when this view has focus.
360 ScopedArrowKeyTraversal
arrow_key_traversal(false);
362 unhandled_keyboard_event_handler_
.HandleKeyboardEvent(event
,
366 // Make sure error bubble is cleared on keyboard event. This is needed
367 // when the focus is inside an iframe. Only clear on KeyDown to prevent hiding
368 // an immediate authentication error (See crbug.com/103643).
369 if (event
.type
== blink::WebInputEvent::KeyDown
) {
370 content::WebUI
* web_ui
= GetWebUI();
372 web_ui
->CallJavascriptFunction("cr.ui.Oobe.clearErrors");
376 bool WebUILoginView::IsPopupOrPanel(const WebContents
* source
) const {
380 bool WebUILoginView::TakeFocus(content::WebContents
* source
, bool reverse
) {
381 // In case of blocked UI (ex.: sign in is in progress)
382 // we should not process focus change events.
383 if (!forward_keyboard_event_
)
386 ash::SystemTray
* tray
= ash::Shell::GetInstance()->GetPrimarySystemTray();
387 if (tray
&& tray
->GetWidget()->IsVisible()) {
388 tray
->SetNextFocusableView(this);
389 ash::Shell::GetInstance()->RotateFocus(reverse
? ash::Shell::BACKWARD
:
390 ash::Shell::FORWARD
);
396 void WebUILoginView::RequestMediaAccessPermission(
397 WebContents
* web_contents
,
398 const content::MediaStreamRequest
& request
,
399 const content::MediaResponseCallback
& callback
) {
400 if (MediaStreamInfoBarDelegate::Create(web_contents
, request
, callback
))
401 NOTREACHED() << "Media stream not allowed for WebUI";
404 void WebUILoginView::DidFailProvisionalLoad(
406 const base::string16
& frame_unique_name
,
408 const GURL
& validated_url
,
410 const base::string16
& error_description
,
411 content::RenderViewHost
* render_view_host
) {
412 if (frame_unique_name
!= base::UTF8ToUTF16("gaia-frame"))
415 base::FundamentalValue
error_value(-error_code
);
416 GetWebUI()->CallJavascriptFunction("login.GaiaSigninScreen.onFrameError",
420 void WebUILoginView::OnLoginPromptVisible() {
421 // If we're hidden than will generate this signal once we're shown.
422 if (is_hidden_
|| webui_visible_
) {
423 LOG(WARNING
) << "Login WebUI >> not emitting signal, hidden: "
427 TRACE_EVENT0("chromeos", "WebUILoginView::OnLoginPromoptVisible");
428 if (should_emit_login_prompt_visible_
) {
429 LOG(WARNING
) << "Login WebUI >> login-prompt-visible";
430 chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->
431 EmitLoginPromptVisible();
434 webui_visible_
= true;
437 void WebUILoginView::ReturnFocus(bool reverse
) {
438 // Return the focus to the web contents.
439 webui_login_
->web_contents()->FocusThroughTabTraversal(reverse
);
440 GetWidget()->Activate();
443 } // namespace chromeos