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