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