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