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