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