| OLD | NEW |
| (Empty) |
| 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. | |
| 4 | |
| 5 #include "chrome/browser/chromeos/login/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/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/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/tab_autofill_manager_delegate.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::CreateForWebContentsWithAutofillManagerDelegate( | |
| 182 web_contents, | |
| 183 autofill::TabAutofillManagerDelegate::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 web_modal::WebContentsModalDialogHost* | |
| 204 WebUILoginView::GetWebContentsModalDialogHost() { | |
| 205 return this; | |
| 206 } | |
| 207 | |
| 208 gfx::NativeView WebUILoginView::GetHostView() const { | |
| 209 return GetWidget()->GetNativeView(); | |
| 210 } | |
| 211 | |
| 212 gfx::Point WebUILoginView::GetDialogPosition(const gfx::Size& size) { | |
| 213 // Center the widget. | |
| 214 gfx::Size widget_size = GetWidget()->GetWindowBoundsInScreen().size(); | |
| 215 return gfx::Point(widget_size.width() / 2 - size.width() / 2, | |
| 216 widget_size.height() / 2 - size.height() / 2); | |
| 217 } | |
| 218 | |
| 219 gfx::Size WebUILoginView::GetMaximumDialogSize() { | |
| 220 return GetWidget()->GetWindowBoundsInScreen().size(); | |
| 221 } | |
| 222 | |
| 223 void WebUILoginView::AddObserver( | |
| 224 web_modal::ModalDialogHostObserver* observer) { | |
| 225 if (observer && !observer_list_.HasObserver(observer)) | |
| 226 observer_list_.AddObserver(observer); | |
| 227 } | |
| 228 | |
| 229 void WebUILoginView::RemoveObserver( | |
| 230 web_modal::ModalDialogHostObserver* observer) { | |
| 231 observer_list_.RemoveObserver(observer); | |
| 232 } | |
| 233 | |
| 234 bool WebUILoginView::AcceleratorPressed( | |
| 235 const ui::Accelerator& accelerator) { | |
| 236 AccelMap::const_iterator entry = accel_map_.find(accelerator); | |
| 237 if (entry == accel_map_.end()) | |
| 238 return false; | |
| 239 | |
| 240 if (!webui_login_) | |
| 241 return true; | |
| 242 | |
| 243 content::WebUI* web_ui = GetWebUI(); | |
| 244 if (web_ui) { | |
| 245 base::StringValue accel_name(entry->second); | |
| 246 web_ui->CallJavascriptFunction("cr.ui.Oobe.handleAccelerator", | |
| 247 accel_name); | |
| 248 } | |
| 249 | |
| 250 return true; | |
| 251 } | |
| 252 | |
| 253 gfx::NativeWindow WebUILoginView::GetNativeWindow() const { | |
| 254 return GetWidget()->GetNativeWindow(); | |
| 255 } | |
| 256 | |
| 257 void WebUILoginView::LoadURL(const GURL & url) { | |
| 258 webui_login_->LoadInitialURL(url); | |
| 259 webui_login_->RequestFocus(); | |
| 260 | |
| 261 // TODO(nkostylev): Use WebContentsObserver::RenderViewCreated to track | |
| 262 // when RenderView is created. | |
| 263 GetWebContents()->GetRenderViewHost()->GetView()->SetBackgroundOpaque(false); | |
| 264 } | |
| 265 | |
| 266 content::WebUI* WebUILoginView::GetWebUI() { | |
| 267 return webui_login_->web_contents()->GetWebUI(); | |
| 268 } | |
| 269 | |
| 270 content::WebContents* WebUILoginView::GetWebContents() { | |
| 271 return webui_login_->web_contents(); | |
| 272 } | |
| 273 | |
| 274 void WebUILoginView::OpenProxySettings() { | |
| 275 const NetworkState* network = | |
| 276 NetworkHandler::Get()->network_state_handler()->DefaultNetwork(); | |
| 277 if (!network) { | |
| 278 LOG(ERROR) << "No default network found!"; | |
| 279 return; | |
| 280 } | |
| 281 ProxySettingsDialog* dialog = | |
| 282 new ProxySettingsDialog(ProfileHelper::GetSigninProfile(), | |
| 283 *network, NULL, GetNativeWindow()); | |
| 284 dialog->Show(); | |
| 285 } | |
| 286 | |
| 287 void WebUILoginView::OnPostponedShow() { | |
| 288 set_is_hidden(false); | |
| 289 OnLoginPromptVisible(); | |
| 290 } | |
| 291 | |
| 292 void WebUILoginView::SetStatusAreaVisible(bool visible) { | |
| 293 if (ash::Shell::GetInstance()->HasPrimaryStatusArea()) { | |
| 294 ash::SystemTray* tray = ash::Shell::GetInstance()->GetPrimarySystemTray(); | |
| 295 if (visible) { | |
| 296 // Tray may have been initialized being hidden. | |
| 297 tray->SetVisible(visible); | |
| 298 tray->GetWidget()->Show(); | |
| 299 } else { | |
| 300 tray->GetWidget()->Hide(); | |
| 301 } | |
| 302 } | |
| 303 } | |
| 304 | |
| 305 void WebUILoginView::SetUIEnabled(bool enabled) { | |
| 306 forward_keyboard_event_ = enabled; | |
| 307 ash::Shell::GetInstance()->GetPrimarySystemTray()->SetEnabled(enabled); | |
| 308 } | |
| 309 | |
| 310 // WebUILoginView protected: --------------------------------------------------- | |
| 311 | |
| 312 void WebUILoginView::Layout() { | |
| 313 DCHECK(webui_login_); | |
| 314 webui_login_->SetBoundsRect(bounds()); | |
| 315 | |
| 316 FOR_EACH_OBSERVER(web_modal::ModalDialogHostObserver, | |
| 317 observer_list_, | |
| 318 OnPositionRequiresUpdate()); | |
| 319 } | |
| 320 | |
| 321 void WebUILoginView::OnLocaleChanged() { | |
| 322 } | |
| 323 | |
| 324 void WebUILoginView::ChildPreferredSizeChanged(View* child) { | |
| 325 Layout(); | |
| 326 SchedulePaint(); | |
| 327 } | |
| 328 | |
| 329 void WebUILoginView::AboutToRequestFocusFromTabTraversal(bool reverse) { | |
| 330 // Return the focus to the web contents. | |
| 331 webui_login_->web_contents()->FocusThroughTabTraversal(reverse); | |
| 332 GetWidget()->Activate(); | |
| 333 webui_login_->web_contents()->Focus(); | |
| 334 } | |
| 335 | |
| 336 void WebUILoginView::Observe(int type, | |
| 337 const content::NotificationSource& source, | |
| 338 const content::NotificationDetails& details) { | |
| 339 switch (type) { | |
| 340 case chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE: | |
| 341 case chrome::NOTIFICATION_LOGIN_NETWORK_ERROR_SHOWN: { | |
| 342 OnLoginPromptVisible(); | |
| 343 registrar_.RemoveAll(); | |
| 344 break; | |
| 345 } | |
| 346 default: | |
| 347 NOTREACHED() << "Unexpected notification " << type; | |
| 348 } | |
| 349 } | |
| 350 | |
| 351 // WebUILoginView private: ----------------------------------------------------- | |
| 352 | |
| 353 bool WebUILoginView::HandleContextMenu( | |
| 354 const content::ContextMenuParams& params) { | |
| 355 // Do not show the context menu. | |
| 356 #ifndef NDEBUG | |
| 357 return false; | |
| 358 #else | |
| 359 return true; | |
| 360 #endif | |
| 361 } | |
| 362 | |
| 363 void WebUILoginView::HandleKeyboardEvent(content::WebContents* source, | |
| 364 const NativeWebKeyboardEvent& event) { | |
| 365 if (forward_keyboard_event_) { | |
| 366 // Disable arrow key traversal because arrow keys are handled via | |
| 367 // accelerator when this view has focus. | |
| 368 ScopedArrowKeyTraversal arrow_key_traversal(false); | |
| 369 | |
| 370 unhandled_keyboard_event_handler_.HandleKeyboardEvent(event, | |
| 371 GetFocusManager()); | |
| 372 } | |
| 373 | |
| 374 // Make sure error bubble is cleared on keyboard event. This is needed | |
| 375 // when the focus is inside an iframe. Only clear on KeyDown to prevent hiding | |
| 376 // an immediate authentication error (See crbug.com/103643). | |
| 377 if (event.type == blink::WebInputEvent::KeyDown) { | |
| 378 content::WebUI* web_ui = GetWebUI(); | |
| 379 if (web_ui) | |
| 380 web_ui->CallJavascriptFunction("cr.ui.Oobe.clearErrors"); | |
| 381 } | |
| 382 } | |
| 383 | |
| 384 bool WebUILoginView::IsPopupOrPanel(const WebContents* source) const { | |
| 385 return true; | |
| 386 } | |
| 387 | |
| 388 bool WebUILoginView::TakeFocus(content::WebContents* source, bool reverse) { | |
| 389 // In case of blocked UI (ex.: sign in is in progress) | |
| 390 // we should not process focus change events. | |
| 391 if (!forward_keyboard_event_) | |
| 392 return false; | |
| 393 | |
| 394 ash::SystemTray* tray = ash::Shell::GetInstance()->GetPrimarySystemTray(); | |
| 395 if (tray && tray->GetWidget()->IsVisible()) { | |
| 396 tray->SetNextFocusableView(this); | |
| 397 ash::Shell::GetInstance()->RotateFocus(reverse ? ash::Shell::BACKWARD : | |
| 398 ash::Shell::FORWARD); | |
| 399 } | |
| 400 | |
| 401 return true; | |
| 402 } | |
| 403 | |
| 404 void WebUILoginView::RequestMediaAccessPermission( | |
| 405 WebContents* web_contents, | |
| 406 const content::MediaStreamRequest& request, | |
| 407 const content::MediaResponseCallback& callback) { | |
| 408 if (MediaStreamInfoBarDelegate::Create(web_contents, request, callback)) | |
| 409 NOTREACHED() << "Media stream not allowed for WebUI"; | |
| 410 } | |
| 411 | |
| 412 bool WebUILoginView::PreHandleGestureEvent( | |
| 413 content::WebContents* source, | |
| 414 const blink::WebGestureEvent& event) { | |
| 415 // Disable pinch zooming. | |
| 416 return event.type == blink::WebGestureEvent::GesturePinchBegin || | |
| 417 event.type == blink::WebGestureEvent::GesturePinchUpdate || | |
| 418 event.type == blink::WebGestureEvent::GesturePinchEnd; | |
| 419 } | |
| 420 | |
| 421 void WebUILoginView::DidFailProvisionalLoad( | |
| 422 int64 frame_id, | |
| 423 const base::string16& frame_unique_name, | |
| 424 bool is_main_frame, | |
| 425 const GURL& validated_url, | |
| 426 int error_code, | |
| 427 const base::string16& error_description, | |
| 428 content::RenderViewHost* render_view_host) { | |
| 429 if (frame_unique_name != base::UTF8ToUTF16("gaia-frame")) | |
| 430 return; | |
| 431 | |
| 432 GetWebUI()->CallJavascriptFunction("login.GaiaSigninScreen.onFrameError", | |
| 433 base::FundamentalValue(-error_code), | |
| 434 base::StringValue(validated_url.spec())); | |
| 435 } | |
| 436 | |
| 437 void WebUILoginView::OnLoginPromptVisible() { | |
| 438 // If we're hidden than will generate this signal once we're shown. | |
| 439 if (is_hidden_ || webui_visible_) { | |
| 440 LOG(WARNING) << "Login WebUI >> not emitting signal, hidden: " | |
| 441 << is_hidden_; | |
| 442 return; | |
| 443 } | |
| 444 TRACE_EVENT0("chromeos", "WebUILoginView::OnLoginPromoptVisible"); | |
| 445 if (should_emit_login_prompt_visible_) { | |
| 446 LOG(WARNING) << "Login WebUI >> login-prompt-visible"; | |
| 447 chromeos::DBusThreadManager::Get()->GetSessionManagerClient()-> | |
| 448 EmitLoginPromptVisible(); | |
| 449 } | |
| 450 | |
| 451 webui_visible_ = true; | |
| 452 } | |
| 453 | |
| 454 void WebUILoginView::ReturnFocus(bool reverse) { | |
| 455 // Return the focus to the web contents. | |
| 456 webui_login_->web_contents()->FocusThroughTabTraversal(reverse); | |
| 457 GetWidget()->Activate(); | |
| 458 } | |
| 459 | |
| 460 } // namespace chromeos | |
| OLD | NEW |