Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / chromeos / login / ui / webui_login_view.cc
blobc69032fcce16f3be4ebf90fffd41526a6dee3ca2
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 "chrome/common/chrome_version_info.h"
30 #include "chrome/common/render_messages.h"
31 #include "chromeos/dbus/dbus_thread_manager.h"
32 #include "chromeos/dbus/session_manager_client.h"
33 #include "chromeos/network/network_state.h"
34 #include "chromeos/network/network_state_handler.h"
35 #include "components/password_manager/core/browser/password_manager.h"
36 #include "components/web_modal/web_contents_modal_dialog_manager.h"
37 #include "content/public/browser/notification_service.h"
38 #include "content/public/browser/render_frame_host.h"
39 #include "content/public/browser/render_view_host.h"
40 #include "content/public/browser/render_widget_host_view.h"
41 #include "content/public/browser/web_contents.h"
42 #include "content/public/browser/web_ui.h"
43 #include "content/public/common/renderer_preferences.h"
44 #include "third_party/WebKit/public/web/WebInputEvent.h"
45 #include "ui/gfx/geometry/rect.h"
46 #include "ui/gfx/geometry/size.h"
47 #include "ui/views/controls/webview/webview.h"
48 #include "ui/views/widget/widget.h"
50 using content::NativeWebKeyboardEvent;
51 using content::RenderViewHost;
52 using content::WebContents;
53 using web_modal::WebContentsModalDialogManager;
55 namespace {
57 // These strings must be kept in sync with handleAccelerator()
58 // in display_manager.js.
59 const char kAccelNameCancel[] = "cancel";
60 const char kAccelNameEnableDebugging[] = "debugging";
61 const char kAccelNameEnrollment[] = "enrollment";
62 const char kAccelNameKioskEnable[] = "kiosk_enable";
63 const char kAccelNameVersion[] = "version";
64 const char kAccelNameReset[] = "reset";
65 const char kAccelFocusPrev[] = "focus_prev";
66 const char kAccelFocusNext[] = "focus_next";
67 const char kAccelNameDeviceRequisition[] = "device_requisition";
68 const char kAccelNameDeviceRequisitionRemora[] = "device_requisition_remora";
69 const char kAccelNameDeviceRequisitionShark[] = "device_requisition_shark";
70 const char kAccelNameAppLaunchBailout[] = "app_launch_bailout";
71 const char kAccelNameAppLaunchNetworkConfig[] = "app_launch_network_config";
72 const char kAccelNameToggleWebviewSignin[] = "toggle_webview_signin";
73 const char kAccelNameToggleNewLoginUI[] = "toggle_new_login_ui";
74 const char kAccelNameToggleEasyBootstrap[] = "toggle_easy_bootstrap";
76 // A class to change arrow key traversal behavior when it's alive.
77 class ScopedArrowKeyTraversal {
78 public:
79 explicit ScopedArrowKeyTraversal(bool new_arrow_key_tranversal_enabled)
80 : previous_arrow_key_traversal_enabled_(
81 views::FocusManager::arrow_key_traversal_enabled()) {
82 views::FocusManager::set_arrow_key_traversal_enabled(
83 new_arrow_key_tranversal_enabled);
85 ~ScopedArrowKeyTraversal() {
86 views::FocusManager::set_arrow_key_traversal_enabled(
87 previous_arrow_key_traversal_enabled_);
90 private:
91 const bool previous_arrow_key_traversal_enabled_;
92 DISALLOW_COPY_AND_ASSIGN(ScopedArrowKeyTraversal);
95 } // namespace
97 namespace chromeos {
99 // static
100 const char WebUILoginView::kViewClassName[] =
101 "browser/chromeos/login/WebUILoginView";
103 // WebUILoginView public: ------------------------------------------------------
105 WebUILoginView::WebUILoginView()
106 : webui_login_(NULL),
107 is_hidden_(false),
108 webui_visible_(false),
109 should_emit_login_prompt_visible_(true),
110 forward_keyboard_event_(true) {
111 registrar_.Add(this,
112 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE,
113 content::NotificationService::AllSources());
114 registrar_.Add(this,
115 chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN,
116 content::NotificationService::AllSources());
118 accel_map_[ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE)] =
119 kAccelNameCancel;
120 accel_map_[ui::Accelerator(ui::VKEY_E,
121 ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)] =
122 kAccelNameEnrollment;
123 accel_map_[ui::Accelerator(ui::VKEY_K,
124 ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)] =
125 kAccelNameKioskEnable;
126 accel_map_[ui::Accelerator(ui::VKEY_V, ui::EF_ALT_DOWN)] =
127 kAccelNameVersion;
128 accel_map_[ui::Accelerator(ui::VKEY_R,
129 ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN)] =
130 kAccelNameReset;
131 accel_map_[ui::Accelerator(ui::VKEY_X,
132 ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN)] =
133 kAccelNameEnableDebugging;
134 accel_map_[ui::Accelerator(
135 ui::VKEY_L, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN)] =
136 kAccelNameToggleNewLoginUI;
137 accel_map_[ui::Accelerator(
138 ui::VKEY_B, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN)] =
139 kAccelNameToggleEasyBootstrap;
141 accel_map_[ui::Accelerator(ui::VKEY_LEFT, ui::EF_NONE)] =
142 kAccelFocusPrev;
143 accel_map_[ui::Accelerator(ui::VKEY_RIGHT, ui::EF_NONE)] =
144 kAccelFocusNext;
146 // Ctrl-Alt-Shift-W for canary/dev builds only.
147 const chrome::VersionInfo::Channel channel =
148 chrome::VersionInfo::GetChannel();
149 if (channel != chrome::VersionInfo::CHANNEL_STABLE &&
150 channel != chrome::VersionInfo::CHANNEL_BETA) {
151 accel_map_[ui::Accelerator(
152 ui::VKEY_W, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN |
153 ui::EF_SHIFT_DOWN)] = kAccelNameToggleWebviewSignin;
156 // Use KEY_RELEASED because Gaia consumes KEY_PRESSED for up/down key.
157 ui::Accelerator key_up(ui::VKEY_UP, ui::EF_NONE);
158 key_up.set_type(ui::ET_KEY_RELEASED);
159 ui::Accelerator key_down(ui::VKEY_DOWN, ui::EF_NONE);
160 key_down.set_type(ui::ET_KEY_RELEASED);
161 accel_map_[key_up] = kAccelFocusPrev;
162 accel_map_[key_down] = kAccelFocusNext;
164 accel_map_[ui::Accelerator(
165 ui::VKEY_D, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN)] =
166 kAccelNameDeviceRequisition;
167 accel_map_[
168 ui::Accelerator(ui::VKEY_H, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)] =
169 kAccelNameDeviceRequisitionRemora;
170 accel_map_[
171 ui::Accelerator(ui::VKEY_H,
172 ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN | ui::EF_SHIFT_DOWN)] =
173 kAccelNameDeviceRequisitionShark;
175 accel_map_[ui::Accelerator(ui::VKEY_S,
176 ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)] =
177 kAccelNameAppLaunchBailout;
179 accel_map_[ui::Accelerator(ui::VKEY_N,
180 ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN)] =
181 kAccelNameAppLaunchNetworkConfig;
183 for (AccelMap::iterator i(accel_map_.begin()); i != accel_map_.end(); ++i)
184 AddAccelerator(i->first);
187 WebUILoginView::~WebUILoginView() {
188 FOR_EACH_OBSERVER(web_modal::ModalDialogHostObserver,
189 observer_list_,
190 OnHostDestroying());
192 if (ash::Shell::GetInstance()->HasPrimaryStatusArea()) {
193 ash::Shell::GetInstance()->GetPrimarySystemTray()->
194 SetNextFocusableView(NULL);
198 void WebUILoginView::Init() {
199 Profile* signin_profile = ProfileHelper::GetSigninProfile();
200 webui_login_ = new views::WebView(signin_profile);
201 webui_login_->set_allow_accelerators(true);
202 AddChildView(webui_login_);
204 WebContents* web_contents = webui_login_->GetWebContents();
206 // Ensure that the login UI has a tab ID, which will allow the GAIA auth
207 // extension's background script to tell it apart from a captive portal window
208 // that may be opened on top of this UI.
209 SessionTabHelper::CreateForWebContents(web_contents);
211 // Create the password manager that is needed for the proxy.
212 ChromePasswordManagerClient::CreateForWebContentsWithAutofillClient(
213 web_contents,
214 autofill::ChromeAutofillClient::FromWebContents(web_contents));
216 // LoginHandlerViews uses a constrained window for the password manager view.
217 WebContentsModalDialogManager::CreateForWebContents(web_contents);
218 WebContentsModalDialogManager::FromWebContents(web_contents)->
219 SetDelegate(this);
220 if (!popup_manager_.get())
221 popup_manager_.reset(new web_modal::PopupManager(this));
222 popup_manager_->RegisterWith(web_contents);
224 web_contents->SetDelegate(this);
225 extensions::ChromeExtensionWebContentsObserver::CreateForWebContents(
226 web_contents);
227 WebContentsObserver::Observe(web_contents);
228 content::RendererPreferences* prefs = web_contents->GetMutableRendererPrefs();
229 renderer_preferences_util::UpdateFromSystemSettings(
230 prefs, signin_profile, web_contents);
233 const char* WebUILoginView::GetClassName() const {
234 return kViewClassName;
237 void WebUILoginView::RequestFocus() {
238 webui_login_->RequestFocus();
241 web_modal::WebContentsModalDialogHost*
242 WebUILoginView::GetWebContentsModalDialogHost() {
243 return this;
246 gfx::NativeView WebUILoginView::GetHostView() const {
247 return GetWidget()->GetNativeView();
250 gfx::Point WebUILoginView::GetDialogPosition(const gfx::Size& size) {
251 // Center the widget.
252 gfx::Size widget_size = GetWidget()->GetWindowBoundsInScreen().size();
253 return gfx::Point(widget_size.width() / 2 - size.width() / 2,
254 widget_size.height() / 2 - size.height() / 2);
257 gfx::Size WebUILoginView::GetMaximumDialogSize() {
258 return GetWidget()->GetWindowBoundsInScreen().size();
261 void WebUILoginView::AddObserver(
262 web_modal::ModalDialogHostObserver* observer) {
263 if (observer && !observer_list_.HasObserver(observer))
264 observer_list_.AddObserver(observer);
267 void WebUILoginView::RemoveObserver(
268 web_modal::ModalDialogHostObserver* observer) {
269 observer_list_.RemoveObserver(observer);
272 bool WebUILoginView::AcceleratorPressed(
273 const ui::Accelerator& accelerator) {
274 AccelMap::const_iterator entry = accel_map_.find(accelerator);
275 if (entry == accel_map_.end())
276 return false;
278 if (!webui_login_)
279 return true;
281 content::WebUI* web_ui = GetWebUI();
282 if (web_ui) {
283 base::StringValue accel_name(entry->second);
284 web_ui->CallJavascriptFunction("cr.ui.Oobe.handleAccelerator",
285 accel_name);
288 return true;
291 gfx::NativeWindow WebUILoginView::GetNativeWindow() const {
292 return GetWidget()->GetNativeWindow();
295 void WebUILoginView::LoadURL(const GURL & url) {
296 webui_login_->LoadInitialURL(url);
297 webui_login_->RequestFocus();
299 // TODO(nkostylev): Use WebContentsObserver::RenderViewCreated to track
300 // when RenderView is created.
301 GetWebContents()->GetRenderViewHost()->GetView()->SetBackgroundColor(
302 SK_ColorTRANSPARENT);
305 content::WebUI* WebUILoginView::GetWebUI() {
306 return webui_login_->web_contents()->GetWebUI();
309 content::WebContents* WebUILoginView::GetWebContents() {
310 return webui_login_->web_contents();
313 void WebUILoginView::OpenProxySettings() {
314 const NetworkState* network =
315 NetworkHandler::Get()->network_state_handler()->DefaultNetwork();
316 if (!network) {
317 LOG(ERROR) << "No default network found!";
318 return;
320 ProxySettingsDialog* dialog =
321 new ProxySettingsDialog(ProfileHelper::GetSigninProfile(),
322 *network, NULL, GetNativeWindow());
323 dialog->Show();
326 void WebUILoginView::OnPostponedShow() {
327 set_is_hidden(false);
328 OnLoginPromptVisible();
331 void WebUILoginView::SetStatusAreaVisible(bool visible) {
332 if (ash::Shell::GetInstance()->HasPrimaryStatusArea()) {
333 ash::SystemTray* tray = ash::Shell::GetInstance()->GetPrimarySystemTray();
334 if (visible) {
335 // Tray may have been initialized being hidden.
336 tray->SetVisible(visible);
337 tray->GetWidget()->Show();
338 } else {
339 tray->GetWidget()->Hide();
344 void WebUILoginView::SetUIEnabled(bool enabled) {
345 forward_keyboard_event_ = enabled;
346 ash::SystemTray* tray = ash::Shell::GetInstance()->GetPrimarySystemTray();
348 // We disable the UI to prevent user from interracting with UI elements,
349 // particullary with the system tray menu. However, in case if the system tray
350 // bubble is opened at this point, it remains opened and interactictive even
351 // after SystemTray::SetEnabled(false) call, which can be dangerous
352 // (http://crbug.com/497080). Close the menu to fix it. Calling
353 // SystemTray::SetEnabled(false) guarantees, that the menu will not be opened
354 // until the UI is enabled again.
355 if (!enabled && tray->HasSystemBubble())
356 tray->CloseSystemBubble();
358 tray->SetEnabled(enabled);
361 void WebUILoginView::AddFrameObserver(FrameObserver* frame_observer) {
362 DCHECK(frame_observer);
363 DCHECK(!frame_observer_list_.HasObserver(frame_observer));
364 frame_observer_list_.AddObserver(frame_observer);
367 void WebUILoginView::RemoveFrameObserver(FrameObserver* frame_observer) {
368 DCHECK(frame_observer);
369 DCHECK(frame_observer_list_.HasObserver(frame_observer));
370 frame_observer_list_.RemoveObserver(frame_observer);
373 // WebUILoginView protected: ---------------------------------------------------
375 void WebUILoginView::Layout() {
376 DCHECK(webui_login_);
377 webui_login_->SetBoundsRect(bounds());
379 FOR_EACH_OBSERVER(web_modal::ModalDialogHostObserver,
380 observer_list_,
381 OnPositionRequiresUpdate());
384 void WebUILoginView::OnLocaleChanged() {
387 void WebUILoginView::ChildPreferredSizeChanged(View* child) {
388 Layout();
389 SchedulePaint();
392 void WebUILoginView::AboutToRequestFocusFromTabTraversal(bool reverse) {
393 // Return the focus to the web contents.
394 webui_login_->web_contents()->FocusThroughTabTraversal(reverse);
395 GetWidget()->Activate();
396 webui_login_->web_contents()->Focus();
399 void WebUILoginView::Observe(int type,
400 const content::NotificationSource& source,
401 const content::NotificationDetails& details) {
402 switch (type) {
403 case chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE:
404 case chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN: {
405 OnLoginPromptVisible();
406 registrar_.RemoveAll();
407 break;
409 default:
410 NOTREACHED() << "Unexpected notification " << type;
414 // WebUILoginView private: -----------------------------------------------------
416 bool WebUILoginView::HandleContextMenu(
417 const content::ContextMenuParams& params) {
418 // Do not show the context menu.
419 #ifndef NDEBUG
420 return false;
421 #else
422 return true;
423 #endif
426 void WebUILoginView::HandleKeyboardEvent(content::WebContents* source,
427 const NativeWebKeyboardEvent& event) {
428 if (forward_keyboard_event_) {
429 // Disable arrow key traversal because arrow keys are handled via
430 // accelerator when this view has focus.
431 ScopedArrowKeyTraversal arrow_key_traversal(false);
433 unhandled_keyboard_event_handler_.HandleKeyboardEvent(event,
434 GetFocusManager());
437 // Make sure error bubble is cleared on keyboard event. This is needed
438 // when the focus is inside an iframe. Only clear on KeyDown to prevent hiding
439 // an immediate authentication error (See crbug.com/103643).
440 if (event.type == blink::WebInputEvent::KeyDown) {
441 content::WebUI* web_ui = GetWebUI();
442 if (web_ui)
443 web_ui->CallJavascriptFunction("cr.ui.Oobe.clearErrors");
447 bool WebUILoginView::IsPopupOrPanel(const WebContents* source) const {
448 return true;
451 bool WebUILoginView::TakeFocus(content::WebContents* source, bool reverse) {
452 // In case of blocked UI (ex.: sign in is in progress)
453 // we should not process focus change events.
454 if (!forward_keyboard_event_)
455 return false;
457 ash::SystemTray* tray = ash::Shell::GetInstance()->GetPrimarySystemTray();
458 if (tray && tray->GetWidget()->IsVisible()) {
459 tray->SetNextFocusableView(this);
460 ash::Shell::GetInstance()->RotateFocus(reverse ? ash::Shell::BACKWARD :
461 ash::Shell::FORWARD);
464 return true;
467 void WebUILoginView::RequestMediaAccessPermission(
468 WebContents* web_contents,
469 const content::MediaStreamRequest& request,
470 const content::MediaResponseCallback& callback) {
471 if (MediaStreamInfoBarDelegate::Create(web_contents, request, callback))
472 NOTREACHED() << "Media stream not allowed for WebUI";
475 bool WebUILoginView::CheckMediaAccessPermission(
476 content::WebContents* web_contents,
477 const GURL& security_origin,
478 content::MediaStreamType type) {
479 return MediaCaptureDevicesDispatcher::GetInstance()
480 ->CheckMediaAccessPermission(web_contents, security_origin, type);
483 bool WebUILoginView::PreHandleGestureEvent(
484 content::WebContents* source,
485 const blink::WebGestureEvent& event) {
486 // Disable pinch zooming.
487 return event.type == blink::WebGestureEvent::GesturePinchBegin ||
488 event.type == blink::WebGestureEvent::GesturePinchUpdate ||
489 event.type == blink::WebGestureEvent::GesturePinchEnd;
492 void WebUILoginView::DidFailProvisionalLoad(
493 content::RenderFrameHost* render_frame_host,
494 const GURL& validated_url,
495 int error_code,
496 const base::string16& error_description,
497 bool was_ignored_by_handler) {
498 FOR_EACH_OBSERVER(FrameObserver,
499 frame_observer_list_,
500 OnFrameError(render_frame_host->GetFrameName()));
501 if (render_frame_host->GetFrameName() != "gaia-frame")
502 return;
504 GetWebUI()->CallJavascriptFunction("login.GaiaSigninScreen.onFrameError",
505 base::FundamentalValue(-error_code),
506 base::StringValue(validated_url.spec()));
509 void WebUILoginView::OnLoginPromptVisible() {
510 // If we're hidden than will generate this signal once we're shown.
511 if (is_hidden_ || webui_visible_) {
512 VLOG(1) << "Login WebUI >> not emitting signal, hidden: " << is_hidden_;
513 return;
515 TRACE_EVENT0("chromeos", "WebUILoginView::OnLoginPromoptVisible");
516 if (should_emit_login_prompt_visible_) {
517 VLOG(1) << "Login WebUI >> login-prompt-visible";
518 chromeos::DBusThreadManager::Get()->GetSessionManagerClient()->
519 EmitLoginPromptVisible();
522 webui_visible_ = true;
525 void WebUILoginView::ReturnFocus(bool reverse) {
526 // Return the focus to the web contents.
527 webui_login_->web_contents()->FocusThroughTabTraversal(reverse);
528 GetWidget()->Activate();
531 } // namespace chromeos