Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / panels / panel_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/ui/views/panels/panel_view.h"
6
7 #include <map>
8 #include "base/logging.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "chrome/app/chrome_command_ids.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/ui/host_desktop.h"
14 #include "chrome/browser/ui/panels/panel.h"
15 #include "chrome/browser/ui/panels/panel_bounds_animation.h"
16 #include "chrome/browser/ui/panels/panel_manager.h"
17 #include "chrome/browser/ui/panels/stacked_panel_collection.h"
18 #include "chrome/browser/ui/views/auto_keep_alive.h"
19 #include "chrome/browser/ui/views/panels/panel_frame_view.h"
20 #include "content/public/browser/render_view_host.h"
21 #include "content/public/browser/render_widget_host_view.h"
22 #include "content/public/browser/web_contents.h"
23 #include "ui/aura/window.h"
24 #include "ui/aura/window_tree_host.h"
25 #include "ui/gfx/image/image.h"
26 #include "ui/gfx/path.h"
27 #include "ui/gfx/screen.h"
28 #include "ui/views/controls/button/image_button.h"
29 #include "ui/views/controls/webview/webview.h"
30 #include "ui/views/widget/widget.h"
31
32 #if defined(OS_WIN)
33 #include "base/win/windows_version.h"
34 #include "chrome/browser/shell_integration.h"
35 #include "chrome/browser/ui/views/panels/taskbar_window_thumbnailer_win.h"
36 #include "ui/base/win/shell.h"
37 #include "ui/gfx/icon_util.h"
38 #include "ui/views/win/hwnd_util.h"
39 #endif
40
41 #if defined(USE_X11) && !defined(OS_CHROMEOS)
42 #include "chrome/browser/shell_integration_linux.h"
43 #include "chrome/browser/ui/views/panels/x11_panel_resizer.h"
44 #include "chrome/browser/web_applications/web_app.h"
45 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h"
46 #endif
47
48 namespace {
49
50 #if defined(OS_WIN)
51 // If the height of a stacked panel shrinks below this threshold during the
52 // user resizing, it will be treated as minimized.
53 const int kStackedPanelHeightShrinkThresholdToBecomeMinimized =
54     panel::kTitlebarHeight + 20;
55 #endif
56
57 // Supported accelerators.
58 // Note: We can't use the acclerator table defined in chrome/browser/ui/views
59 // due to checkdeps violation.
60 struct AcceleratorMapping {
61   ui::KeyboardCode keycode;
62   int modifiers;
63   int command_id;
64 };
65 const AcceleratorMapping kPanelAcceleratorMap[] = {
66   { ui::VKEY_W, ui::EF_CONTROL_DOWN, IDC_CLOSE_WINDOW },
67   { ui::VKEY_W, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, IDC_CLOSE_WINDOW },
68   { ui::VKEY_F4, ui::EF_ALT_DOWN, IDC_CLOSE_WINDOW },
69   { ui::VKEY_R, ui::EF_CONTROL_DOWN, IDC_RELOAD },
70   { ui::VKEY_F5, ui::EF_NONE, IDC_RELOAD },
71   { ui::VKEY_R, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN,
72       IDC_RELOAD_IGNORING_CACHE },
73   { ui::VKEY_F5, ui::EF_CONTROL_DOWN, IDC_RELOAD_IGNORING_CACHE },
74   { ui::VKEY_F5, ui::EF_SHIFT_DOWN, IDC_RELOAD_IGNORING_CACHE },
75   { ui::VKEY_ESCAPE, ui::EF_NONE, IDC_STOP },
76   { ui::VKEY_OEM_MINUS, ui::EF_CONTROL_DOWN, IDC_ZOOM_MINUS },
77   { ui::VKEY_SUBTRACT, ui::EF_CONTROL_DOWN, IDC_ZOOM_MINUS },
78   { ui::VKEY_0, ui::EF_CONTROL_DOWN, IDC_ZOOM_NORMAL },
79   { ui::VKEY_NUMPAD0, ui::EF_CONTROL_DOWN, IDC_ZOOM_NORMAL },
80   { ui::VKEY_OEM_PLUS, ui::EF_CONTROL_DOWN, IDC_ZOOM_PLUS },
81   { ui::VKEY_ADD, ui::EF_CONTROL_DOWN, IDC_ZOOM_PLUS },
82   { ui::VKEY_I, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, IDC_DEV_TOOLS },
83   { ui::VKEY_J, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN,
84     IDC_DEV_TOOLS_CONSOLE },
85 };
86
87 const std::map<ui::Accelerator, int>& GetAcceleratorTable() {
88   static std::map<ui::Accelerator, int>* accelerators = NULL;
89   if (!accelerators) {
90     accelerators = new std::map<ui::Accelerator, int>();
91     for (size_t i = 0; i < arraysize(kPanelAcceleratorMap); ++i) {
92       ui::Accelerator accelerator(kPanelAcceleratorMap[i].keycode,
93                                   kPanelAcceleratorMap[i].modifiers);
94       (*accelerators)[accelerator] = kPanelAcceleratorMap[i].command_id;
95     }
96   }
97   return *accelerators;
98 }
99
100 // NativePanelTesting implementation.
101 class NativePanelTestingViews : public NativePanelTesting {
102  public:
103   explicit NativePanelTestingViews(PanelView* panel_view);
104   ~NativePanelTestingViews() override;
105
106  private:
107   void PressLeftMouseButtonTitlebar(const gfx::Point& mouse_location,
108                                     panel::ClickModifier modifier) override;
109   void ReleaseMouseButtonTitlebar(panel::ClickModifier modifier) override;
110   void DragTitlebar(const gfx::Point& mouse_location) override;
111   void CancelDragTitlebar() override;
112   void FinishDragTitlebar() override;
113   bool VerifyDrawingAttention() const override;
114   bool VerifyActiveState(bool is_active) override;
115   bool VerifyAppIcon() const override;
116   bool VerifySystemMinimizeState() const override;
117   bool IsWindowVisible() const override;
118   bool IsWindowSizeKnown() const override;
119   bool IsAnimatingBounds() const override;
120   bool IsButtonVisible(panel::TitlebarButtonType button_type) const override;
121   panel::CornerStyle GetWindowCornerStyle() const override;
122   bool EnsureApplicationRunOnForeground() override;
123
124   PanelView* panel_view_;
125 };
126
127 NativePanelTestingViews::NativePanelTestingViews(PanelView* panel_view)
128     : panel_view_(panel_view) {
129 }
130
131 NativePanelTestingViews::~NativePanelTestingViews() {
132 }
133
134 void NativePanelTestingViews::PressLeftMouseButtonTitlebar(
135     const gfx::Point& mouse_location, panel::ClickModifier modifier) {
136   panel_view_->OnTitlebarMousePressed(mouse_location);
137 }
138
139 void NativePanelTestingViews::ReleaseMouseButtonTitlebar(
140     panel::ClickModifier modifier) {
141   panel_view_->OnTitlebarMouseReleased(modifier);
142 }
143
144 void NativePanelTestingViews::DragTitlebar(const gfx::Point& mouse_location) {
145   panel_view_->OnTitlebarMouseDragged(mouse_location);
146 }
147
148 void NativePanelTestingViews::CancelDragTitlebar() {
149   panel_view_->OnTitlebarMouseCaptureLost();
150 }
151
152 void NativePanelTestingViews::FinishDragTitlebar() {
153   panel_view_->OnTitlebarMouseReleased(panel::NO_MODIFIER);
154 }
155
156 bool NativePanelTestingViews::VerifyDrawingAttention() const {
157   base::MessageLoop::current()->RunUntilIdle();
158   return panel_view_->GetFrameView()->GetPaintState() ==
159          PanelFrameView::PAINT_FOR_ATTENTION;
160 }
161
162 bool NativePanelTestingViews::VerifyActiveState(bool is_active) {
163   return panel_view_->GetFrameView()->GetPaintState() ==
164          (is_active ? PanelFrameView::PAINT_AS_ACTIVE
165                     : PanelFrameView::PAINT_AS_INACTIVE);
166 }
167
168 bool NativePanelTestingViews::VerifyAppIcon() const {
169 #if defined(OS_WIN)
170   // We only care about Windows 7 and later.
171   if (base::win::GetVersion() < base::win::VERSION_WIN7)
172     return true;
173
174   HWND native_window = views::HWNDForWidget(panel_view_->window());
175   HICON app_icon = reinterpret_cast<HICON>(
176       ::SendMessage(native_window, WM_GETICON, ICON_BIG, 0L));
177   if (!app_icon)
178     return false;
179   scoped_ptr<SkBitmap> bitmap(IconUtil::CreateSkBitmapFromHICON(app_icon));
180   return bitmap.get() &&
181          bitmap->width() == panel::kPanelAppIconSize &&
182          bitmap->height() == panel::kPanelAppIconSize;
183 #else
184   return true;
185 #endif
186 }
187
188 bool NativePanelTestingViews::VerifySystemMinimizeState() const {
189 #if defined(OS_WIN)
190   HWND native_window = views::HWNDForWidget(panel_view_->window());
191   WINDOWPLACEMENT placement;
192   if (!::GetWindowPlacement(native_window, &placement))
193     return false;
194   if (placement.showCmd == SW_MINIMIZE || placement.showCmd == SW_SHOWMINIMIZED)
195     return true;
196
197   // If the panel window has owner window, as in stacked mode, check its owner
198   // window. Note that owner window, instead of parent window, is returned
199   // though GWL_HWNDPARENT contains 'parent'.
200   HWND owner_window =
201       reinterpret_cast<HWND>(::GetWindowLongPtr(native_window,
202                                                 GWLP_HWNDPARENT));
203   if (!owner_window || !::GetWindowPlacement(owner_window, &placement))
204     return false;
205   return placement.showCmd == SW_MINIMIZE ||
206          placement.showCmd == SW_SHOWMINIMIZED;
207 #else
208   return true;
209 #endif
210 }
211
212 bool NativePanelTestingViews::IsWindowVisible() const {
213   return panel_view_->window()->IsVisible();
214 }
215
216 bool NativePanelTestingViews::IsWindowSizeKnown() const {
217   return true;
218 }
219
220 bool NativePanelTestingViews::IsAnimatingBounds() const {
221   return panel_view_->IsAnimatingBounds();
222 }
223
224 bool NativePanelTestingViews::IsButtonVisible(
225     panel::TitlebarButtonType button_type) const {
226   PanelFrameView* frame_view = panel_view_->GetFrameView();
227
228   switch (button_type) {
229     case panel::CLOSE_BUTTON:
230       return frame_view->close_button()->visible();
231     case panel::MINIMIZE_BUTTON:
232       return frame_view->minimize_button()->visible();
233     case panel::RESTORE_BUTTON:
234       return frame_view->restore_button()->visible();
235     default:
236       NOTREACHED();
237   }
238   return false;
239 }
240
241 panel::CornerStyle NativePanelTestingViews::GetWindowCornerStyle() const {
242   return panel_view_->GetFrameView()->corner_style();
243 }
244
245 bool NativePanelTestingViews::EnsureApplicationRunOnForeground() {
246   // Not needed on views.
247   return true;
248 }
249
250 }  // namespace
251
252 // static
253 NativePanel* Panel::CreateNativePanel(Panel* panel,
254                                       const gfx::Rect& bounds,
255                                       bool always_on_top) {
256   return new PanelView(panel, bounds, always_on_top);
257 }
258
259 // The panel window has to be created as always-on-top. We cannot create it
260 // as non-always-on-top and then change it to always-on-top because Windows
261 // system might deny making a window always-on-top if the application is not
262 // a foreground application.
263 PanelView::PanelView(Panel* panel, const gfx::Rect& bounds, bool always_on_top)
264     : panel_(panel),
265       bounds_(bounds),
266       window_(NULL),
267       window_closed_(false),
268       web_view_(NULL),
269       always_on_top_(always_on_top),
270       focused_(false),
271       user_resizing_(false),
272 #if defined(OS_WIN)
273       user_resizing_interior_stacked_panel_edge_(false),
274 #endif
275       mouse_pressed_(false),
276       mouse_dragging_state_(NO_DRAGGING),
277       is_drawing_attention_(false),
278       force_to_paint_as_inactive_(false),
279       old_focused_view_(NULL) {
280   window_ = new views::Widget;
281   views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
282   params.delegate = this;
283   params.remove_standard_frame = true;
284   params.keep_on_top = always_on_top;
285   params.visible_on_all_workspaces = always_on_top;
286   params.bounds = bounds;
287
288 #if defined(USE_X11) && !defined(OS_CHROMEOS)
289   params.wm_class_name = web_app::GetWMClassFromAppName(panel->app_name());
290   params.wm_class_class = shell_integration_linux::GetProgramClassName();
291 #endif
292
293   window_->Init(params);
294   window_->set_frame_type(views::Widget::FRAME_TYPE_FORCE_CUSTOM);
295   window_->set_focus_on_creation(false);
296   window_->AddObserver(this);
297
298   // Prevent the browser process from shutting down while this window is open.
299   keep_alive_.reset(new AutoKeepAlive(GetNativePanelWindow()));
300
301   web_view_ = new views::WebView(NULL);
302   AddChildView(web_view_);
303
304   // Register accelarators supported by panels.
305   views::FocusManager* focus_manager = GetFocusManager();
306   const std::map<ui::Accelerator, int>& accelerator_table =
307       GetAcceleratorTable();
308   for (std::map<ui::Accelerator, int>::const_iterator iter =
309            accelerator_table.begin();
310        iter != accelerator_table.end(); ++iter) {
311     focus_manager->RegisterAccelerator(
312         iter->first, ui::AcceleratorManager::kNormalPriority, this);
313   }
314
315 #if defined(OS_WIN)
316   ui::win::SetAppIdForWindow(
317       ShellIntegration::GetAppModelIdForProfile(
318           base::UTF8ToWide(panel->app_name()), panel->profile()->GetPath()),
319       views::HWNDForWidget(window_));
320   ui::win::PreventWindowFromPinning(views::HWNDForWidget(window_));
321 #endif
322
323 #if defined(USE_X11) && !defined(OS_CHROMEOS)
324   // Swap the default non client event handler with one which handles resizes
325   // for panels entirely within Chrome. This is needed because it is not
326   // possible to tell when a resize performed by the window manager ends.
327   views::DesktopWindowTreeHostX11* host =
328       views::DesktopWindowTreeHostX11::GetHostForXID(
329           window_->GetNativeView()->GetHost()->GetAcceleratedWidget());
330   scoped_ptr<ui::EventHandler> resizer(
331       new X11PanelResizer(panel_.get(), window_->GetNativeWindow()));
332   host->SwapNonClientEventHandler(resizer.Pass());
333 #endif
334 }
335
336 PanelView::~PanelView() {
337 }
338
339 void PanelView::ShowPanel() {
340   ShowPanelInactive();
341   ActivatePanel();
342 }
343
344 void PanelView::ShowPanelInactive() {
345   if (window_->IsVisible())
346     return;
347   window_->ShowInactive();
348   // No animation is used for initial creation of a panel on Win.
349   // Signal immediately that pending actions can be performed.
350   panel_->manager()->OnPanelAnimationEnded(panel_.get());
351 }
352
353 gfx::Rect PanelView::GetPanelBounds() const {
354   return bounds_;
355 }
356
357 void PanelView::SetPanelBounds(const gfx::Rect& bounds) {
358   SetBoundsInternal(bounds, true);
359 }
360
361 void PanelView::SetPanelBoundsInstantly(const gfx::Rect& bounds) {
362   SetBoundsInternal(bounds, false);
363 }
364
365 void PanelView::SetBoundsInternal(const gfx::Rect& new_bounds, bool animate) {
366   if (bounds_ == new_bounds)
367     return;
368
369   bounds_ = new_bounds;
370
371   if (!animate) {
372     // If no animation is in progress, apply bounds change instantly. Otherwise,
373     // continue the animation with new target bounds.
374     if (!IsAnimatingBounds())
375       SetWidgetBounds(bounds_);
376     return;
377   }
378
379   animation_start_bounds_ = window_->GetWindowBoundsInScreen();
380
381   bounds_animator_.reset(new PanelBoundsAnimation(
382       this, panel_.get(), animation_start_bounds_, new_bounds));
383   bounds_animator_->Start();
384 }
385
386 #if defined(OS_WIN)
387 bool PanelView::FilterMessage(HWND hwnd,
388                               UINT message,
389                               WPARAM w_param,
390                               LPARAM l_param,
391                               LRESULT* l_result) {
392   switch (message) {
393     case WM_SIZING:
394       if (w_param == WMSZ_BOTTOM)
395         user_resizing_interior_stacked_panel_edge_ = true;
396       break;
397   }
398   return false;
399 }
400 #endif
401
402 void PanelView::AnimationEnded(const gfx::Animation* animation) {
403   panel_->manager()->OnPanelAnimationEnded(panel_.get());
404 }
405
406 void PanelView::AnimationProgressed(const gfx::Animation* animation) {
407   gfx::Rect new_bounds = bounds_animator_->CurrentValueBetween(
408       animation_start_bounds_, bounds_);
409   SetWidgetBounds(new_bounds);
410 }
411
412 void PanelView::SetWidgetBounds(const gfx::Rect& new_bounds) {
413 #if defined(OS_WIN)
414   // An overlapped window is a top-level window that has a titlebar, border,
415   // and client area. The Windows system will automatically put the shadow
416   // around the whole window. Also the system will enforce the minimum height
417   // (38 pixels based on observation) for the overlapped window such that it
418   // will always has the space for the titlebar.
419   //
420   // On contrast, a popup window is a bare minimum window without border and
421   // titlebar by default. It is often used for the popup menu and the window
422   // with short life. The Windows system does not add the shadow around the
423   // whole window though CS_DROPSHADOW class style could be passed to add the
424   // drop shadow which is only around the right and bottom edges.
425   //
426   // The height of the title-only or minimized panel is smaller than the minimum
427   // overlapped window height. If the panel still uses the overlapped window
428   // style, Windows system will automatically increase the window height. To
429   // work around this limitation, we temporarily change the window style to
430   // popup when the height to set is smaller than the minimum overlapped window
431   // height and then restore the window style to overlapped when the height
432   // grows.
433   static const int kMinimumOverlappedWindowHeight = 38;
434   gfx::Rect old_bounds = GetWidget()->GetRestoredBounds();
435   if (old_bounds.height() > kMinimumOverlappedWindowHeight &&
436       new_bounds.height() <= kMinimumOverlappedWindowHeight) {
437     // When the panel height shrinks below the minimum overlapped window height,
438     // change the window style to popup such that we can show the title-only
439     // and minimized panel without additional height being added by the system.
440     UpdateWindowAttribute(GWL_STYLE,
441                           WS_POPUP,
442                           WS_OVERLAPPED | WS_THICKFRAME | WS_SYSMENU,
443                           true);
444   } else if (old_bounds.height() <= kMinimumOverlappedWindowHeight &&
445              new_bounds.height() > kMinimumOverlappedWindowHeight) {
446     // Change the window style back to overlappped when the panel height grow
447     // taller than the minimum overlapped window height.
448     UpdateWindowAttribute(GWL_STYLE,
449                           WS_OVERLAPPED | WS_THICKFRAME | WS_SYSMENU,
450                           WS_POPUP,
451                           true);
452   }
453 #endif
454
455   GetWidget()->SetBounds(new_bounds);
456 }
457
458 void PanelView::ClosePanel() {
459   // We're already closing. Do nothing.
460   if (window_closed_)
461     return;
462
463   if (!panel_->ShouldCloseWindow())
464     return;
465
466   // Cancel any currently running animation since we're closing down.
467   if (bounds_animator_.get())
468     bounds_animator_.reset();
469
470   if (panel_->GetWebContents()) {
471     // Still have web contents. Allow renderer to shut down.
472     // When web contents are destroyed, we will be called back again.
473     panel_->OnWindowClosing();
474     return;
475   }
476
477   panel_->OnNativePanelClosed();
478   if (window_)
479     window_->Close();
480   window_closed_ = true;
481 }
482
483 void PanelView::ActivatePanel() {
484   window_->Activate();
485 }
486
487 void PanelView::DeactivatePanel() {
488   if (!focused_)
489     return;
490
491 #if defined(OS_WIN)
492   // Need custom behavior for always-on-top panels to avoid
493   // the OS activating a minimized panel when this one is
494   // deactivated.
495   if (always_on_top_) {
496     ::SetForegroundWindow(::GetDesktopWindow());
497     return;
498   }
499 #endif
500
501   window_->Deactivate();
502 }
503
504 bool PanelView::IsPanelActive() const {
505   return focused_;
506 }
507
508 void PanelView::PreventActivationByOS(bool prevent_activation) {
509 #if defined(OS_WIN)
510   // Set the flags "NoActivate" to make sure the minimized panels do not get
511   // activated by the OS. In addition, set "AppWindow" to make sure the
512   // minimized panels do appear in the taskbar and Alt-Tab menu if it is not
513   // in a stack.
514   int value_to_change = WS_EX_NOACTIVATE;
515   if (!panel_->stack())
516     value_to_change |= WS_EX_APPWINDOW;
517   if (prevent_activation)
518     UpdateWindowAttribute(GWL_EXSTYLE, value_to_change, 0, false);
519   else
520     UpdateWindowAttribute(GWL_EXSTYLE, 0, value_to_change, false);
521 #endif
522 }
523
524 gfx::NativeWindow PanelView::GetNativePanelWindow() {
525   return window_->GetNativeWindow();
526 }
527
528 void PanelView::UpdatePanelTitleBar() {
529   UpdateWindowTitle();
530   UpdateWindowIcon();
531 }
532
533 void PanelView::UpdatePanelLoadingAnimations(bool should_animate) {
534   GetFrameView()->UpdateThrobber();
535 }
536
537 void PanelView::PanelWebContentsFocused(content::WebContents* contents) {
538   web_view_->OnWebContentsFocused(contents);
539 }
540
541 void PanelView::PanelCut() {
542   // Nothing to do since we do not have panel-specific system menu.
543   NOTREACHED();
544 }
545
546 void PanelView::PanelCopy() {
547   // Nothing to do since we do not have panel-specific system menu.
548   NOTREACHED();
549 }
550
551 void PanelView::PanelPaste() {
552   // Nothing to do since we do not have panel-specific system menu.
553   NOTREACHED();
554 }
555
556 void PanelView::DrawAttention(bool draw_attention) {
557   DCHECK((panel_->attention_mode() & Panel::USE_PANEL_ATTENTION) != 0);
558
559   if (is_drawing_attention_ == draw_attention)
560     return;
561   is_drawing_attention_ = draw_attention;
562   GetFrameView()->SchedulePaint();
563
564   if ((panel_->attention_mode() & Panel::USE_SYSTEM_ATTENTION) != 0) {
565 #if defined(OS_WIN)
566     // The default implementation of Widget::FlashFrame only flashes 5 times.
567     // We need more than that.
568     FLASHWINFO fwi;
569     fwi.cbSize = sizeof(fwi);
570     fwi.hwnd = views::HWNDForWidget(window_);
571     if (draw_attention) {
572       fwi.dwFlags = FLASHW_ALL;
573       fwi.uCount = panel::kNumberOfTimesToFlashPanelForAttention;
574       fwi.dwTimeout = 0;
575     } else {
576       // TODO(jianli): calling FlashWindowEx with FLASHW_STOP flag for the
577       // panel window has the same problem as the stack window. However,
578       // we cannot take the similar fix since there is no background window
579       // to replace for the regular panel window. More investigation is needed.
580       fwi.dwFlags = FLASHW_STOP;
581     }
582     ::FlashWindowEx(&fwi);
583 #else
584     window_->FlashFrame(draw_attention);
585 #endif
586   }
587 }
588
589 bool PanelView::IsDrawingAttention() const {
590   return is_drawing_attention_;
591 }
592
593 void PanelView::HandlePanelKeyboardEvent(
594     const content::NativeWebKeyboardEvent& event) {
595   views::FocusManager* focus_manager = GetFocusManager();
596   if (focus_manager->shortcut_handling_suspended())
597     return;
598
599   ui::Accelerator accelerator(
600       static_cast<ui::KeyboardCode>(event.windowsKeyCode),
601       content::GetModifiersFromNativeWebKeyboardEvent(event));
602   if (event.type == blink::WebInputEvent::KeyUp)
603     accelerator.set_type(ui::ET_KEY_RELEASED);
604   focus_manager->ProcessAccelerator(accelerator);
605 }
606
607 void PanelView::FullScreenModeChanged(bool is_full_screen) {
608   if (is_full_screen) {
609     if (window_->IsVisible() && always_on_top_)
610       window_->Hide();
611   } else {
612     if (!window_->IsVisible()) {
613       ShowPanelInactive();
614
615 #if defined(OS_WIN)
616       // When hiding and showing again a top-most window that belongs to a
617       // background application (i.e. the application is not a foreground one),
618       // the window may loose top-most placement even though its WS_EX_TOPMOST
619       // bit is still set. Re-issuing SetWindowsPos() returns the window to its
620       // top-most placement.
621       if (always_on_top_)
622         window_->SetAlwaysOnTop(true);
623 #endif
624     }
625   }
626 }
627
628 bool PanelView::IsPanelAlwaysOnTop() const {
629   return always_on_top_;
630 }
631
632 void PanelView::SetPanelAlwaysOnTop(bool on_top) {
633   if (always_on_top_ == on_top)
634     return;
635   always_on_top_ = on_top;
636
637   window_->SetAlwaysOnTop(on_top);
638   window_->SetVisibleOnAllWorkspaces(on_top);
639   window_->non_client_view()->Layout();
640   window_->client_view()->Layout();
641 }
642
643 void PanelView::UpdatePanelMinimizeRestoreButtonVisibility() {
644   GetFrameView()->UpdateTitlebarMinimizeRestoreButtonVisibility();
645 }
646
647 void PanelView::SetWindowCornerStyle(panel::CornerStyle corner_style) {
648   GetFrameView()->SetWindowCornerStyle(corner_style);
649 }
650
651 void PanelView::PanelExpansionStateChanging(Panel::ExpansionState old_state,
652                                             Panel::ExpansionState new_state) {
653 #if defined(OS_WIN)
654   // Live preview is only available since Windows 7.
655   if (base::win::GetVersion() < base::win::VERSION_WIN7)
656     return;
657
658   if (panel_->collection()->type() != PanelCollection::DOCKED)
659     return;
660
661   bool is_minimized = old_state != Panel::EXPANDED;
662   bool will_be_minimized = new_state != Panel::EXPANDED;
663   if (is_minimized == will_be_minimized)
664     return;
665
666   HWND native_window = views::HWNDForWidget(window_);
667
668   if (!thumbnailer_.get()) {
669     DCHECK(native_window);
670     thumbnailer_.reset(new TaskbarWindowThumbnailerWin(native_window, NULL));
671   }
672
673   // Cache the image at this point.
674   if (will_be_minimized) {
675     // If the panel is still active (we will deactivate the minimizd panel at
676     // later time), we need to paint it immediately as inactive so that we can
677     // take a snapshot of inactive panel.
678     if (focused_) {
679       force_to_paint_as_inactive_ = true;
680       ::RedrawWindow(native_window, NULL, NULL,
681                      RDW_NOCHILDREN | RDW_INVALIDATE | RDW_UPDATENOW);
682     }
683
684     // Start the thumbnailer and capture the snapshot now.
685     thumbnailer_->Start();
686     thumbnailer_->CaptureSnapshot();
687   } else {
688     force_to_paint_as_inactive_ = false;
689     thumbnailer_->Stop();
690   }
691
692 #endif
693 }
694
695 gfx::Size PanelView::WindowSizeFromContentSize(
696     const gfx::Size& content_size) const {
697   gfx::Size frame = GetFrameView()->NonClientAreaSize();
698   return gfx::Size(content_size.width() + frame.width(),
699                    content_size.height() + frame.height());
700 }
701
702 gfx::Size PanelView::ContentSizeFromWindowSize(
703     const gfx::Size& window_size) const {
704   gfx::Size frame = GetFrameView()->NonClientAreaSize();
705   return gfx::Size(window_size.width() - frame.width(),
706                    window_size.height() - frame.height());
707 }
708
709 int PanelView::TitleOnlyHeight() const {
710   return panel::kTitlebarHeight;
711 }
712
713 void PanelView::MinimizePanelBySystem() {
714   window_->Minimize();
715 }
716
717 bool PanelView::IsPanelMinimizedBySystem() const {
718   return window_->IsMinimized();
719 }
720
721 bool PanelView::IsPanelShownOnActiveDesktop() const {
722 #if defined(OS_WIN)
723   // Virtual desktop is not supported by the native Windows system.
724   return true;
725 #else
726   NOTIMPLEMENTED();
727   return true;
728 #endif
729 }
730
731 void PanelView::ShowShadow(bool show) {
732 #if defined(OS_WIN)
733   // The overlapped window has the shadow while the popup window does not have
734   // the shadow.
735   int overlap_style = WS_OVERLAPPED | WS_THICKFRAME | WS_SYSMENU;
736   int popup_style = WS_POPUP;
737   UpdateWindowAttribute(GWL_STYLE,
738                         show ? overlap_style : popup_style,
739                         show ? popup_style : overlap_style,
740                         true);
741 #endif
742 }
743
744 void PanelView::AttachWebContents(content::WebContents* contents) {
745   web_view_->SetWebContents(contents);
746 }
747
748 void PanelView::DetachWebContents(content::WebContents* contents) {
749   web_view_->SetWebContents(NULL);
750 }
751
752 NativePanelTesting* PanelView::CreateNativePanelTesting() {
753   return new NativePanelTestingViews(this);
754 }
755
756 void PanelView::OnDisplayChanged() {
757   panel_->manager()->display_settings_provider()->OnDisplaySettingsChanged();
758 }
759
760 void PanelView::OnWorkAreaChanged() {
761   panel_->manager()->display_settings_provider()->OnDisplaySettingsChanged();
762 }
763
764 bool PanelView::WillProcessWorkAreaChange() const {
765   return true;
766 }
767
768 views::View* PanelView::GetContentsView() {
769   return this;
770 }
771
772 views::NonClientFrameView* PanelView::CreateNonClientFrameView(
773     views::Widget* widget) {
774   PanelFrameView* frame_view = new PanelFrameView(this);
775   frame_view->Init();
776   return frame_view;
777 }
778
779 bool PanelView::CanResize() const {
780   return true;
781 }
782
783 bool PanelView::CanMaximize() const {
784   return false;
785 }
786
787 bool PanelView::CanMinimize() const {
788   return false;
789 }
790
791 base::string16 PanelView::GetWindowTitle() const {
792   return panel_->GetWindowTitle();
793 }
794
795 gfx::ImageSkia PanelView::GetWindowAppIcon() {
796   gfx::Image app_icon = panel_->app_icon();
797   if (app_icon.IsEmpty())
798     return GetWindowIcon();
799   else
800     return *app_icon.ToImageSkia();
801 }
802
803 gfx::ImageSkia PanelView::GetWindowIcon() {
804   gfx::Image icon = panel_->GetCurrentPageIcon();
805   return icon.IsEmpty() ? gfx::ImageSkia() : *icon.ToImageSkia();
806 }
807
808 void PanelView::WindowClosing() {
809   // When closing a panel via window.close, API or the close button,
810   // ClosePanel() is called first, destroying the native |window_|
811   // which results in this method being called. ClosePanel() sets
812   // |window_closed_| to NULL.
813   // If we still have a |window_closed_| here, the close was triggered by the
814   // OS, (e.g. clicking on taskbar menu), which destroys the native |window_|
815   // without invoking ClosePanel() beforehand.
816   if (!window_closed_) {
817     panel_->OnWindowClosing();
818     ClosePanel();
819     DCHECK(window_closed_);
820   }
821 }
822
823 void PanelView::DeleteDelegate() {
824   delete this;
825 }
826
827 void PanelView::OnWindowBeginUserBoundsChange() {
828   user_resizing_ = true;
829   panel_->OnPanelStartUserResizing();
830
831 #if defined(OS_WIN)
832   StackedPanelCollection* stack = panel_->stack();
833   if (stack) {
834     // Listen to WM_SIZING message in order to find out whether the interior
835     // edge is being resized such that the specific maximum size could be
836     // passed to the system.
837     if (panel_->stack()->GetPanelBelow(panel_.get())) {
838       ui::HWNDSubclass::AddFilterToTarget(views::HWNDForWidget(window_), this);
839       user_resizing_interior_stacked_panel_edge_ = false;
840     }
841
842     // Keep track of the original full size of the resizing panel such that it
843     // can be restored to this size once it is shrunk to minimized state.
844     original_full_size_of_resizing_panel_ = panel_->full_size();
845
846     // Keep track of the original full size of the panel below the resizing
847     // panel such that it can be restored to this size once it is shrunk to
848     // minimized state.
849     Panel* below_panel = stack->GetPanelBelow(panel_.get());
850     if (below_panel && !below_panel->IsMinimized()) {
851       original_full_size_of_panel_below_resizing_panel_ =
852           below_panel->full_size();
853     }
854   }
855 #endif
856 }
857
858 void PanelView::OnWindowEndUserBoundsChange() {
859   user_resizing_ = false;
860   panel_->OnPanelEndUserResizing();
861
862   // No need to proceed with post-resizing update when there is no size change.
863   gfx::Rect new_bounds = window_->GetWindowBoundsInScreen();
864   if (bounds_ == new_bounds)
865     return;
866   bounds_ = new_bounds;
867
868   panel_->IncreaseMaxSize(bounds_.size());
869   panel_->set_full_size(bounds_.size());
870
871 #if defined(OS_WIN)
872   StackedPanelCollection* stack = panel_->stack();
873   if (stack) {
874     // No need to listen to WM_SIZING message any more.
875     ui::HWNDSubclass::RemoveFilterFromAllTargets(this);
876
877     // If the height of resizing panel shrinks close to the titlebar height,
878     // treate it as minimized. This could occur when the user is dragging
879     // 1) the top edge of the top panel downward to shrink it; or
880     // 2) the bottom edge of any panel upward to shrink it.
881     if (panel_->GetBounds().height() <
882             kStackedPanelHeightShrinkThresholdToBecomeMinimized) {
883       stack->MinimizePanel(panel_.get());
884       panel_->set_full_size(original_full_size_of_resizing_panel_);
885     }
886
887     // If the height of panel below the resizing panel shrinks close to the
888     // titlebar height, treat it as minimized. This could occur when the user
889     // is dragging the bottom edge of non-bottom panel downward to expand it
890     // and also shrink the panel below.
891     Panel* below_panel = stack->GetPanelBelow(panel_.get());
892     if (below_panel && !below_panel->IsMinimized() &&
893         below_panel->GetBounds().height() <
894             kStackedPanelHeightShrinkThresholdToBecomeMinimized) {
895       stack->MinimizePanel(below_panel);
896       below_panel->set_full_size(
897           original_full_size_of_panel_below_resizing_panel_);
898     }
899   }
900 #endif
901
902   panel_->collection()->RefreshLayout();
903 }
904
905 views::Widget* PanelView::GetWidget() {
906   return window_;
907 }
908
909 const views::Widget* PanelView::GetWidget() const {
910   return window_;
911 }
912
913 void PanelView::UpdateLoadingAnimations(bool should_animate) {
914   GetFrameView()->UpdateThrobber();
915 }
916
917 void PanelView::UpdateWindowTitle() {
918   window_->UpdateWindowTitle();
919   GetFrameView()->UpdateTitle();
920 }
921
922 void PanelView::UpdateWindowIcon() {
923   window_->UpdateWindowIcon();
924   GetFrameView()->UpdateIcon();
925 }
926
927 void PanelView::Layout() {
928   // |web_view_| might not be created yet when the window is first created.
929   if (web_view_)
930     web_view_->SetBounds(0, 0, width(), height());
931 }
932
933 gfx::Size PanelView::GetMinimumSize() const {
934   // If the panel is minimized, it can be rendered to very small size, like
935   // 4-pixel lines when it is docked. Otherwise, its size should not be less
936   // than its minimum size.
937   return panel_->IsMinimized() ? gfx::Size() :
938       gfx::Size(panel::kPanelMinWidth, panel::kPanelMinHeight);
939 }
940
941 gfx::Size PanelView::GetMaximumSize() const {
942   // If the user is resizing a stacked panel by its bottom edge, make sure its
943   // height cannot grow more than what the panel below it could offer. This is
944   // because growing a stacked panel by y amount will shrink the panel below it
945   // by same amount and we do not want the panel below it being shrunk to be
946   // smaller than the titlebar.
947 #if defined(OS_WIN)
948   if (panel_->stack() && user_resizing_interior_stacked_panel_edge_) {
949     Panel* below_panel = panel_->stack()->GetPanelBelow(panel_.get());
950     if (below_panel && !below_panel->IsMinimized()) {
951       return gfx::Size(0, below_panel->GetBounds().bottom() -
952           panel_->GetBounds().y() - panel::kTitlebarHeight);
953     }
954   }
955 #endif
956   return gfx::Size();
957 }
958
959 bool PanelView::AcceleratorPressed(const ui::Accelerator& accelerator) {
960   if (mouse_pressed_ && accelerator.key_code() == ui::VKEY_ESCAPE) {
961     OnTitlebarMouseCaptureLost();
962     return true;
963   }
964
965   // No other accelerator is allowed when the drag begins.
966   if (mouse_dragging_state_ == DRAGGING_STARTED)
967     return true;
968
969   const std::map<ui::Accelerator, int>& accelerator_table =
970       GetAcceleratorTable();
971   std::map<ui::Accelerator, int>::const_iterator iter =
972       accelerator_table.find(accelerator);
973   DCHECK(iter != accelerator_table.end());
974   return panel_->ExecuteCommandIfEnabled(iter->second);
975 }
976
977 void PanelView::OnWidgetDestroying(views::Widget* widget) {
978   window_ = NULL;
979 }
980
981 void PanelView::OnWidgetActivationChanged(views::Widget* widget, bool active) {
982 #if defined(OS_WIN)
983   // WM_NCACTIVATED could be sent when an active window is being destroyed on
984   // Windows. We need to guard against this.
985   if (window_closed_)
986     return;
987
988   bool focused = active;
989   if (chrome::GetActiveDesktop() == chrome::HOST_DESKTOP_TYPE_NATIVE) {
990     // The panel window is in focus (actually accepting keystrokes) if it is
991     // active and belongs to a foreground application.
992     focused = active &&
993         views::HWNDForWidget(widget) == ::GetForegroundWindow();
994   }
995 #else
996   bool focused = active;
997 #endif
998
999   if (focused_ == focused)
1000     return;
1001   focused_ = focused;
1002
1003   // Expand the panel if the minimized panel is activated by means other than
1004   // clicking on its titlebar. This is the workaround to support restoring the
1005   // minimized panel by other means, like alt-tabbing, win-tabbing, or clicking
1006   // the taskbar icon. Note that this workaround does not work for one edge
1007   // case: the mouse happens to be at the minimized panel when the user tries to
1008   // bring up the panel with the above alternatives.
1009   // When the user clicks on the minimized panel, the panel expansion will be
1010   // done when we process the mouse button pressed message.
1011 #if defined(OS_WIN)
1012   if (focused_ && panel_->IsMinimized() &&
1013       panel_->collection()->type() == PanelCollection::DOCKED &&
1014       gfx::Screen::GetScreenFor(widget->GetNativeWindow())->
1015           GetWindowUnderCursor() != widget->GetNativeWindow()) {
1016     panel_->Restore();
1017   }
1018 #endif
1019
1020   panel()->OnActiveStateChanged(focused);
1021
1022    // Give web contents view a chance to set focus to the appropriate element.
1023   if (focused_) {
1024     content::WebContents* web_contents = panel_->GetWebContents();
1025     if (web_contents)
1026       web_contents->RestoreFocus();
1027   }
1028 }
1029
1030 void PanelView::OnWidgetBoundsChanged(views::Widget* widget,
1031                                       const gfx::Rect& new_bounds) {
1032   if (user_resizing_)
1033     panel()->collection()->OnPanelResizedByMouse(panel(), new_bounds);
1034 }
1035
1036 bool PanelView::OnTitlebarMousePressed(const gfx::Point& mouse_location) {
1037   mouse_pressed_ = true;
1038   mouse_dragging_state_ = NO_DRAGGING;
1039   last_mouse_location_ = mouse_location;
1040   return true;
1041 }
1042
1043 bool PanelView::OnTitlebarMouseDragged(const gfx::Point& mouse_location) {
1044   if (!mouse_pressed_)
1045     return false;
1046
1047   if (mouse_dragging_state_ == NO_DRAGGING &&
1048       ExceededDragThreshold(mouse_location - last_mouse_location_)) {
1049     // When a drag begins, we do not want to the client area to still receive
1050     // the focus. We do not need to do this for the unfocused minimized panel.
1051     if (!panel_->IsMinimized()) {
1052       old_focused_view_ = GetFocusManager()->GetFocusedView();
1053       GetFocusManager()->SetFocusedView(GetFrameView());
1054     }
1055
1056     panel_->manager()->StartDragging(panel_.get(), last_mouse_location_);
1057     mouse_dragging_state_ = DRAGGING_STARTED;
1058   }
1059   if (mouse_dragging_state_ == DRAGGING_STARTED) {
1060     panel_->manager()->Drag(mouse_location);
1061
1062     // Once in drag, update |last_mouse_location_| on each drag fragment, since
1063     // we already dragged the panel up to the current mouse location.
1064     last_mouse_location_ = mouse_location;
1065   }
1066   return true;
1067 }
1068
1069 bool PanelView::OnTitlebarMouseReleased(panel::ClickModifier modifier) {
1070   if (mouse_dragging_state_ != NO_DRAGGING) {
1071     // Ensure dragging a minimized panel does not leave it activated.
1072     // Windows activates a panel on mouse-down, regardless of our attempts
1073     // to prevent activation of a minimized panel. Now that we know mouse-down
1074     // resulted in a mouse-drag, we need to ensure the minimized panel is
1075     // deactivated.
1076     if (panel_->IsMinimized() && focused_)
1077       panel_->Deactivate();
1078
1079     if (mouse_dragging_state_ == DRAGGING_STARTED) {
1080       // When a drag ends, restore the focus.
1081       if (old_focused_view_) {
1082         GetFocusManager()->SetFocusedView(old_focused_view_);
1083         old_focused_view_ = NULL;
1084       }
1085       return EndDragging(false);
1086     }
1087
1088     // The panel drag was cancelled before the mouse is released. Do not
1089     // treat this as a click.
1090     return true;
1091   }
1092
1093   panel_->OnTitlebarClicked(modifier);
1094   return true;
1095 }
1096
1097 bool PanelView::OnTitlebarMouseCaptureLost() {
1098   if (mouse_dragging_state_ == DRAGGING_STARTED)
1099     return EndDragging(true);
1100   return true;
1101 }
1102
1103 bool PanelView::EndDragging(bool cancelled) {
1104   // Only handle clicks that started in our window.
1105   if (!mouse_pressed_)
1106     return false;
1107   mouse_pressed_ = false;
1108
1109   mouse_dragging_state_ = DRAGGING_ENDED;
1110   panel_->manager()->EndDragging(cancelled);
1111   return true;
1112 }
1113
1114 PanelFrameView* PanelView::GetFrameView() const {
1115   return static_cast<PanelFrameView*>(window_->non_client_view()->frame_view());
1116 }
1117
1118 bool PanelView::IsAnimatingBounds() const {
1119   if (bounds_animator_.get() && bounds_animator_->is_animating())
1120     return true;
1121   StackedPanelCollection* stack = panel_->stack();
1122   if (!stack)
1123     return false;
1124   return stack->IsAnimatingPanelBounds(panel_.get());
1125 }
1126
1127 #if defined(OS_WIN)
1128 void PanelView::UpdateWindowAttribute(int attribute_index,
1129                                       int attribute_value_to_set,
1130                                       int attribute_value_to_reset,
1131                                       bool update_frame) {
1132   HWND native_window = views::HWNDForWidget(window_);
1133   int value = ::GetWindowLong(native_window, attribute_index);
1134   int expected_value = value;
1135   if (attribute_value_to_set)
1136     expected_value |=  attribute_value_to_set;
1137   if (attribute_value_to_reset)
1138     expected_value &=  ~attribute_value_to_reset;
1139   if (value != expected_value)
1140     ::SetWindowLong(native_window, attribute_index, expected_value);
1141
1142   // Per MSDN, if any of the frame styles is changed, SetWindowPos with the
1143   // SWP_FRAMECHANGED flag must be called in order for the cached window data
1144   // to be updated properly.
1145   // http://msdn.microsoft.com/en-us/library/windows/desktop/ms633591(v=vs.85).aspx
1146   if (update_frame) {
1147     ::SetWindowPos(native_window, NULL, 0, 0, 0, 0,
1148                    SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE |
1149                        SWP_NOZORDER | SWP_NOACTIVATE);
1150   }
1151 }
1152 #endif