Add new certificateProvider extension API.
[chromium-blink-merge.git] / chrome / browser / chromeos / login / ui / webui_login_view.cc
blob015b55f9e2bc30ae287b96262ba6bf35cf08fc04
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.
5 #include "chrome/browser/chromeos/login/ui/webui_login_view.h"
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/i18n/rtl.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/trace_event/trace_event.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_capture_devices_dispatcher.h"
23 #include "chrome/browser/media/media_stream_infobar_delegate.h"
24 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
25 #include "chrome/browser/renderer_preferences_util.h"
26 #include "chrome/browser/sessions/session_tab_helper.h"
27 #include "chrome/browser/ui/autofill/chrome_autofill_client.h"
28 #include "chrome/browser/ui/webui/chromeos/login/oobe_ui.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 "content/public/common/renderer_preferences.h"
42 #include "third_party/WebKit/public/web/WebInputEvent.h"
43 #include "ui/gfx/geometry/rect.h"
44 #include "ui/gfx/geometry/size.h"
45 #include "ui/views/controls/webview/webview.h"
46 #include "ui/views/widget/widget.h"
48 using content::NativeWebKeyboardEvent;
49 using content::RenderViewHost;
50 using content::WebContents;
51 using web_modal::WebContentsModalDialogManager;
53 namespace {
55 // These strings must be kept in sync with handleAccelerator()
56 // in display_manager.js.
57 const char kAccelNameCancel[] = "cancel";
58 const char kAccelNameEnableDebugging[] = "debugging";
59 const char kAccelNameEnrollment[] = "enrollment";
60 const char kAccelNameKioskEnable[] = "kiosk_enable";
61 const char kAccelNameVersion[] = "version";
62 const char kAccelNameReset[] = "reset";
63 const char kAccelFocusPrev[] = "focus_prev";
64 const char kAccelFocusNext[] = "focus_next";
65 const char kAccelNameDeviceRequisition[] = "device_requisition";
66 const char kAccelNameDeviceRequisitionRemora[] = "device_requisition_remora";
67 const char kAccelNameDeviceRequisitionShark[] = "device_requisition_shark";
68 const char kAccelNameAppLaunchBailout[] = "app_launch_bailout";
69 const char kAccelNameAppLaunchNetworkConfig[] = "app_launch_network_config";
70 const char kAccelNameToggleEasyBootstrap[] = "toggle_easy_bootstrap";
72 // A class to change arrow key traversal behavior when it's alive.
73 class ScopedArrowKeyTraversal {
74 public:
75 explicit ScopedArrowKeyTraversal(bool new_arrow_key_tranversal_enabled)
76 : previous_arrow_key_traversal_enabled_(
77 views::FocusManager::arrow_key_traversal_enabled()) {
78 views::FocusManager::set_arrow_key_traversal_enabled(
79 new_arrow_key_tranversal_enabled);
81 ~ScopedArrowKeyTraversal() {
82 views::FocusManager::set_arrow_key_traversal_enabled(
83 previous_arrow_key_traversal_enabled_);
86 private:
87 const bool previous_arrow_key_traversal_enabled_;
88 DISALLOW_COPY_AND_ASSIGN(ScopedArrowKeyTraversal);
91 } // namespace
93 namespace chromeos {
95 // static
96 const char WebUILoginView::kViewClassName[] =
97 "browser/chromeos/login/WebUILoginView";
99 // WebUILoginView public: ------------------------------------------------------
101 WebUILoginView::WebUILoginView()
102 : webui_login_(NULL),
103 is_hidden_(false),
104 webui_visible_(false),
105 should_emit_login_prompt_visible_(true),
106 forward_keyboard_event_(true) {
107 registrar_.Add(this,
108 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
109 content::NotificationService::AllSources());
110 registrar_.Add(this,
111 chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN,
112 content::NotificationService::AllSources());
114 accel_map_[ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE)] =
115 kAccelNameCancel;
116 accel_map_[ui::Accelerator(ui::VKEY_E,
117 ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)] =
118 kAccelNameEnrollment;
119 accel_map_[ui::Accelerator(ui::VKEY_K,
120 ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)] =
121 kAccelNameKioskEnable;
122 accel_map_[ui::Accelerator(ui::VKEY_V, ui::EF_ALT_DOWN)] =
123 kAccelNameVersion;
124 accel_map_[ui::Accelerator(ui::VKEY_R,
125 ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN)] =
126 kAccelNameReset;
127 accel_map_[ui::Accelerator(ui::VKEY_X,
128 ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN)] =
129 kAccelNameEnableDebugging;
130 accel_map_[ui::Accelerator(
131 ui::VKEY_B, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN)] =
132 kAccelNameToggleEasyBootstrap;
134 accel_map_[ui::Accelerator(ui::VKEY_LEFT, ui::EF_NONE)] =
135 kAccelFocusPrev;
136 accel_map_[ui::Accelerator(ui::VKEY_RIGHT, ui::EF_NONE)] =
137 kAccelFocusNext;
139 accel_map_[ui::Accelerator(
140 ui::VKEY_D, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN)] =
141 kAccelNameDeviceRequisition;
142 accel_map_[
143 ui::Accelerator(ui::VKEY_H, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)] =
144 kAccelNameDeviceRequisitionRemora;
145 accel_map_[
146 ui::Accelerator(ui::VKEY_H,
147 ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN)] =
148 kAccelNameDeviceRequisitionShark;
150 accel_map_[ui::Accelerator(ui::VKEY_S,
151 ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)] =
152 kAccelNameAppLaunchBailout;
154 accel_map_[ui::Accelerator(ui::VKEY_N,
155 ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)] =
156 kAccelNameAppLaunchNetworkConfig;
158 for (AccelMap::iterator i(accel_map_.begin()); i != accel_map_.end(); ++i)
159 AddAccelerator(i->first);
162 WebUILoginView::~WebUILoginView() {
163 FOR_EACH_OBSERVER(web_modal::ModalDialogHostObserver,
164 observer_list_,
165 OnHostDestroying());
167 if (ash::Shell::GetInstance()->HasPrimaryStatusArea()) {
168 ash::Shell::GetInstance()->GetPrimarySystemTray()->
169 SetNextFocusableView(NULL);
173 void WebUILoginView::Init() {
174 Profile* signin_profile = ProfileHelper::GetSigninProfile();
175 webui_login_ = new views::WebView(signin_profile);
176 webui_login_->set_allow_accelerators(true);
177 AddChildView(webui_login_);
179 WebContents* web_contents = webui_login_->GetWebContents();
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);
186 // Create the password manager that is needed for the proxy.
187 ChromePasswordManagerClient::CreateForWebContentsWithAutofillClient(
188 web_contents,
189 autofill::ChromeAutofillClient::FromWebContents(web_contents));
191 // LoginHandlerViews uses a constrained window for the password manager view.
192 WebContentsModalDialogManager::CreateForWebContents(web_contents);
193 WebContentsModalDialogManager::FromWebContents(web_contents)->
194 SetDelegate(this);
196 web_contents->SetDelegate(this);
197 extensions::ChromeExtensionWebContentsObserver::CreateForWebContents(
198 web_contents);
199 WebContentsObserver::Observe(web_contents);
200 content::RendererPreferences* prefs = web_contents->GetMutableRendererPrefs();
201 renderer_preferences_util::UpdateFromSystemSettings(
202 prefs, signin_profile, web_contents);
205 const char* WebUILoginView::GetClassName() const {
206 return kViewClassName;
209 void WebUILoginView::RequestFocus() {
210 webui_login_->RequestFocus();
213 web_modal::WebContentsModalDialogHost*
214 WebUILoginView::GetWebContentsModalDialogHost() {
215 return this;
218 gfx::NativeView WebUILoginView::GetHostView() const {
219 return GetWidget()->GetNativeView();
222 gfx::Point WebUILoginView::GetDialogPosition(const gfx::Size& size) {
223 // Center the widget.
224 gfx::Size widget_size = GetWidget()->GetWindowBoundsInScreen().size();
225 return gfx::Point(widget_size.width() / 2 - size.width() / 2,
226 widget_size.height() / 2 - size.height() / 2);
229 gfx::Size WebUILoginView::GetMaximumDialogSize() {
230 return GetWidget()->GetWindowBoundsInScreen().size();
233 void WebUILoginView::AddObserver(
234 web_modal::ModalDialogHostObserver* observer) {
235 if (observer && !observer_list_.HasObserver(observer))
236 observer_list_.AddObserver(observer);
239 void WebUILoginView::RemoveObserver(
240 web_modal::ModalDialogHostObserver* observer) {
241 observer_list_.RemoveObserver(observer);
244 bool WebUILoginView::AcceleratorPressed(
245 const ui::Accelerator& accelerator) {
246 AccelMap::const_iterator entry = accel_map_.find(accelerator);
247 if (entry == accel_map_.end())
248 return false;
250 if (!webui_login_)
251 return true;
253 content::WebUI* web_ui = GetWebUI();
254 if (web_ui) {
255 base::StringValue accel_name(entry->second);
256 web_ui->CallJavascriptFunction("cr.ui.Oobe.handleAccelerator",
257 accel_name);
260 return true;
263 gfx::NativeWindow WebUILoginView::GetNativeWindow() const {
264 return GetWidget()->GetNativeWindow();
267 void WebUILoginView::LoadURL(const GURL & url) {
268 webui_login_->LoadInitialURL(url);
269 webui_login_->RequestFocus();
271 // TODO(nkostylev): Use WebContentsObserver::RenderViewCreated to track
272 // when RenderView is created.
273 GetWebContents()->GetRenderViewHost()->GetView()->SetBackgroundColor(
274 SK_ColorTRANSPARENT);
277 content::WebUI* WebUILoginView::GetWebUI() {
278 return webui_login_->web_contents()->GetWebUI();
281 content::WebContents* WebUILoginView::GetWebContents() {
282 return webui_login_->web_contents();
285 void WebUILoginView::OpenProxySettings() {
286 const NetworkState* network =
287 NetworkHandler::Get()->network_state_handler()->DefaultNetwork();
288 if (!network) {
289 LOG(ERROR) << "No default network found!";
290 return;
292 ProxySettingsDialog* dialog =
293 new ProxySettingsDialog(ProfileHelper::GetSigninProfile(),
294 *network, NULL, GetNativeWindow());
295 dialog->Show();
298 void WebUILoginView::OnPostponedShow() {
299 set_is_hidden(false);
300 OnLoginPromptVisible();
303 void WebUILoginView::SetStatusAreaVisible(bool visible) {
304 if (ash::Shell::GetInstance()->HasPrimaryStatusArea()) {
305 ash::SystemTray* tray = ash::Shell::GetInstance()->GetPrimarySystemTray();
306 if (visible) {
307 // Tray may have been initialized being hidden.
308 tray->SetVisible(visible);
309 tray->GetWidget()->Show();
310 } else {
311 tray->GetWidget()->Hide();
316 void WebUILoginView::SetUIEnabled(bool enabled) {
317 forward_keyboard_event_ = enabled;
318 ash::SystemTray* tray = ash::Shell::GetInstance()->GetPrimarySystemTray();
320 // We disable the UI to prevent user from interracting with UI elements,
321 // particullary with the system tray menu. However, in case if the system tray
322 // bubble is opened at this point, it remains opened and interactictive even
323 // after SystemTray::SetEnabled(false) call, which can be dangerous
324 // (http://crbug.com/497080). Close the menu to fix it. Calling
325 // SystemTray::SetEnabled(false) guarantees, that the menu will not be opened
326 // until the UI is enabled again.
327 if (!enabled && tray->HasSystemBubble())
328 tray->CloseSystemBubble();
330 tray->SetEnabled(enabled);
333 void WebUILoginView::AddFrameObserver(FrameObserver* frame_observer) {
334 DCHECK(frame_observer);
335 DCHECK(!frame_observer_list_.HasObserver(frame_observer));
336 frame_observer_list_.AddObserver(frame_observer);
339 void WebUILoginView::RemoveFrameObserver(FrameObserver* frame_observer) {
340 DCHECK(frame_observer);
341 DCHECK(frame_observer_list_.HasObserver(frame_observer));
342 frame_observer_list_.RemoveObserver(frame_observer);
345 // WebUILoginView protected: ---------------------------------------------------
347 void WebUILoginView::Layout() {
348 DCHECK(webui_login_);
349 webui_login_->SetBoundsRect(bounds());
351 FOR_EACH_OBSERVER(web_modal::ModalDialogHostObserver,
352 observer_list_,
353 OnPositionRequiresUpdate());
356 void WebUILoginView::OnLocaleChanged() {
359 void WebUILoginView::ChildPreferredSizeChanged(View* child) {
360 Layout();
361 SchedulePaint();
364 void WebUILoginView::AboutToRequestFocusFromTabTraversal(bool reverse) {
365 // Return the focus to the web contents.
366 webui_login_->web_contents()->FocusThroughTabTraversal(reverse);
367 GetWidget()->Activate();
368 webui_login_->web_contents()->Focus();
371 void WebUILoginView::Observe(int type,
372 const content::NotificationSource& source,
373 const content::NotificationDetails& details) {
374 switch (type) {
375 case chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE:
376 case chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN: {
377 OnLoginPromptVisible();
378 registrar_.RemoveAll();
379 break;
381 default:
382 NOTREACHED() << "Unexpected notification " << type;
386 // WebUILoginView private: -----------------------------------------------------
388 bool WebUILoginView::HandleContextMenu(
389 const content::ContextMenuParams& params) {
390 // Do not show the context menu.
391 #ifndef NDEBUG
392 return false;
393 #else
394 return true;
395 #endif
398 void WebUILoginView::HandleKeyboardEvent(content::WebContents* source,
399 const NativeWebKeyboardEvent& event) {
400 if (forward_keyboard_event_) {
401 // Disable arrow key traversal because arrow keys are handled via
402 // accelerator when this view has focus.
403 ScopedArrowKeyTraversal arrow_key_traversal(false);
405 unhandled_keyboard_event_handler_.HandleKeyboardEvent(event,
406 GetFocusManager());
409 // Make sure error bubble is cleared on keyboard event. This is needed
410 // when the focus is inside an iframe. Only clear on KeyDown to prevent hiding
411 // an immediate authentication error (See crbug.com/103643).
412 if (event.type == blink::WebInputEvent::KeyDown) {
413 content::WebUI* web_ui = GetWebUI();
414 if (web_ui)
415 web_ui->CallJavascriptFunction("cr.ui.Oobe.clearErrors");
419 bool WebUILoginView::IsPopupOrPanel(const WebContents* source) const {
420 return true;
423 bool WebUILoginView::TakeFocus(content::WebContents* source, bool reverse) {
424 // In case of blocked UI (ex.: sign in is in progress)
425 // we should not process focus change events.
426 if (!forward_keyboard_event_)
427 return false;
429 ash::SystemTray* tray = ash::Shell::GetInstance()->GetPrimarySystemTray();
430 if (tray && tray->GetWidget()->IsVisible()) {
431 tray->SetNextFocusableView(this);
432 ash::Shell::GetInstance()->RotateFocus(reverse ? ash::Shell::BACKWARD :
433 ash::Shell::FORWARD);
436 return true;
439 void WebUILoginView::RequestMediaAccessPermission(
440 WebContents* web_contents,
441 const content::MediaStreamRequest& request,
442 const content::MediaResponseCallback& callback) {
443 if (MediaStreamInfoBarDelegate::Create(web_contents, request, callback))
444 NOTREACHED() << "Media stream not allowed for WebUI";
447 bool WebUILoginView::CheckMediaAccessPermission(
448 content::WebContents* web_contents,
449 const GURL& security_origin,
450 content::MediaStreamType type) {
451 return MediaCaptureDevicesDispatcher::GetInstance()
452 ->CheckMediaAccessPermission(web_contents, security_origin, type);
455 bool WebUILoginView::PreHandleGestureEvent(
456 content::WebContents* source,
457 const blink::WebGestureEvent& event) {
458 // Disable pinch zooming.
459 return event.type == blink::WebGestureEvent::GesturePinchBegin ||
460 event.type == blink::WebGestureEvent::GesturePinchUpdate ||
461 event.type == blink::WebGestureEvent::GesturePinchEnd;
464 void WebUILoginView::DidFailProvisionalLoad(
465 content::RenderFrameHost* render_frame_host,
466 const GURL& validated_url,
467 int error_code,
468 const base::string16& error_description,
469 bool was_ignored_by_handler) {
470 FOR_EACH_OBSERVER(FrameObserver,
471 frame_observer_list_,
472 OnFrameError(render_frame_host->GetFrameName()));
473 if (render_frame_host->GetFrameName() != "gaia-frame")
474 return;
476 GetWebUI()->CallJavascriptFunction("login.GaiaSigninScreen.onFrameError",
477 base::FundamentalValue(-error_code),
478 base::StringValue(validated_url.spec()));
481 void WebUILoginView::OnLoginPromptVisible() {
482 // If we're hidden than will generate this signal once we're shown.
483 if (is_hidden_ || webui_visible_) {
484 VLOG(1) << "Login WebUI >> not emitting signal, hidden: " << is_hidden_;
485 return;
487 TRACE_EVENT0("chromeos", "WebUILoginView::OnLoginPromoptVisible");
488 if (should_emit_login_prompt_visible_) {
489 VLOG(1) << "Login WebUI >> login-prompt-visible";
490 chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->
491 EmitLoginPromptVisible();
494 webui_visible_ = true;
497 void WebUILoginView::ReturnFocus(bool reverse) {
498 // Return the focus to the web contents.
499 webui_login_->web_contents()->FocusThroughTabTraversal(reverse);
500 GetWidget()->Activate();
503 } // namespace chromeos