chrome_native_app_window_views.cc revision 116680a4aac90f2aa7413d9095a592090648e557
1// Copyright 2014 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/apps/chrome_native_app_window_views.h" 6 7#include "apps/ui/views/app_window_frame_view.h" 8#include "base/command_line.h" 9#include "chrome/app/chrome_command_ids.h" 10#include "chrome/browser/app_mode/app_mode_utils.h" 11#include "chrome/browser/chrome_page_zoom.h" 12#include "chrome/browser/favicon/favicon_tab_helper.h" 13#include "chrome/browser/profiles/profile.h" 14#include "chrome/browser/ui/host_desktop.h" 15#include "chrome/browser/ui/views/apps/shaped_app_window_targeter.h" 16#include "chrome/browser/ui/views/extensions/extension_keybinding_registry_views.h" 17#include "chrome/browser/ui/views/frame/taskbar_decorator.h" 18#include "chrome/browser/web_applications/web_app.h" 19#include "chrome/common/chrome_switches.h" 20#include "extensions/common/extension.h" 21#include "ui/aura/window.h" 22#include "ui/base/hit_test.h" 23#include "ui/base/models/simple_menu_model.h" 24#include "ui/gfx/image/image_skia.h" 25#include "ui/views/controls/menu/menu_runner.h" 26#include "ui/views/controls/webview/webview.h" 27#include "ui/views/widget/widget.h" 28#include "ui/wm/core/easy_resize_window_targeter.h" 29#include "ui/wm/core/shadow_types.h" 30 31#if defined(OS_LINUX) 32#include "chrome/browser/shell_integration_linux.h" 33#endif 34 35#if defined(USE_ASH) 36#include "ash/ash_constants.h" 37#include "ash/ash_switches.h" 38#include "ash/frame/custom_frame_view_ash.h" 39#include "ash/screen_util.h" 40#include "ash/shell.h" 41#include "ash/wm/immersive_fullscreen_controller.h" 42#include "ash/wm/panels/panel_frame_view.h" 43#include "ash/wm/window_properties.h" 44#include "ash/wm/window_state.h" 45#include "ash/wm/window_state_delegate.h" 46#include "ash/wm/window_state_observer.h" 47#include "chrome/browser/ui/ash/ash_util.h" 48#include "chrome/browser/ui/ash/multi_user/multi_user_context_menu.h" 49#include "ui/aura/client/aura_constants.h" 50#include "ui/aura/client/window_tree_client.h" 51#include "ui/aura/window_observer.h" 52#endif 53 54using apps::AppWindow; 55 56namespace { 57 58const int kMinPanelWidth = 100; 59const int kMinPanelHeight = 100; 60const int kDefaultPanelWidth = 200; 61const int kDefaultPanelHeight = 300; 62 63struct AcceleratorMapping { 64 ui::KeyboardCode keycode; 65 int modifiers; 66 int command_id; 67}; 68 69const AcceleratorMapping kAppWindowAcceleratorMap[] = { 70 { ui::VKEY_W, ui::EF_CONTROL_DOWN, IDC_CLOSE_WINDOW }, 71 { ui::VKEY_W, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, IDC_CLOSE_WINDOW }, 72 { ui::VKEY_F4, ui::EF_ALT_DOWN, IDC_CLOSE_WINDOW }, 73}; 74 75// These accelerators will only be available in kiosk mode. These allow the 76// user to manually zoom app windows. This is only necessary in kiosk mode 77// (in normal mode, the user can zoom via the screen magnifier). 78// TODO(xiyuan): Write a test for kiosk accelerators. 79const AcceleratorMapping kAppWindowKioskAppModeAcceleratorMap[] = { 80 { ui::VKEY_OEM_MINUS, ui::EF_CONTROL_DOWN, IDC_ZOOM_MINUS }, 81 { ui::VKEY_OEM_MINUS, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, 82 IDC_ZOOM_MINUS }, 83 { ui::VKEY_SUBTRACT, ui::EF_CONTROL_DOWN, IDC_ZOOM_MINUS }, 84 { ui::VKEY_OEM_PLUS, ui::EF_CONTROL_DOWN, IDC_ZOOM_PLUS }, 85 { ui::VKEY_OEM_PLUS, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, IDC_ZOOM_PLUS }, 86 { ui::VKEY_ADD, ui::EF_CONTROL_DOWN, IDC_ZOOM_PLUS }, 87 { ui::VKEY_0, ui::EF_CONTROL_DOWN, IDC_ZOOM_NORMAL }, 88 { ui::VKEY_NUMPAD0, ui::EF_CONTROL_DOWN, IDC_ZOOM_NORMAL }, 89}; 90 91void AddAcceleratorsFromMapping(const AcceleratorMapping mapping[], 92 size_t mapping_length, 93 std::map<ui::Accelerator, int>* accelerators) { 94 for (size_t i = 0; i < mapping_length; ++i) { 95 ui::Accelerator accelerator(mapping[i].keycode, mapping[i].modifiers); 96 (*accelerators)[accelerator] = mapping[i].command_id; 97 } 98} 99 100const std::map<ui::Accelerator, int>& GetAcceleratorTable() { 101 typedef std::map<ui::Accelerator, int> AcceleratorMap; 102 CR_DEFINE_STATIC_LOCAL(AcceleratorMap, accelerators, ()); 103 if (accelerators.empty()) { 104 AddAcceleratorsFromMapping( 105 kAppWindowAcceleratorMap, 106 arraysize(kAppWindowAcceleratorMap), 107 &accelerators); 108 109 // Add accelerators for kiosk mode. 110 if (chrome::IsRunningInForcedAppMode()) { 111 AddAcceleratorsFromMapping( 112 kAppWindowKioskAppModeAcceleratorMap, 113 arraysize(kAppWindowKioskAppModeAcceleratorMap), 114 &accelerators); 115 } 116 } 117 return accelerators; 118} 119 120#if defined(USE_ASH) 121// This class handles a user's fullscreen request (Shift+F4/F4). 122class NativeAppWindowStateDelegate : public ash::wm::WindowStateDelegate, 123 public ash::wm::WindowStateObserver, 124 public aura::WindowObserver { 125 public: 126 NativeAppWindowStateDelegate(AppWindow* app_window, 127 apps::NativeAppWindow* native_app_window) 128 : app_window_(app_window), 129 window_state_( 130 ash::wm::GetWindowState(native_app_window->GetNativeWindow())) { 131 // Add a window state observer to exit fullscreen properly in case 132 // fullscreen is exited without going through AppWindow::Restore(). This 133 // is the case when exiting immersive fullscreen via the "Restore" window 134 // control. 135 // TODO(pkotwicz): This is a hack. Remove ASAP. http://crbug.com/319048 136 window_state_->AddObserver(this); 137 window_state_->window()->AddObserver(this); 138 } 139 virtual ~NativeAppWindowStateDelegate() { 140 if (window_state_) { 141 window_state_->RemoveObserver(this); 142 window_state_->window()->RemoveObserver(this); 143 } 144 } 145 146 private: 147 // Overridden from ash::wm::WindowStateDelegate. 148 virtual bool ToggleFullscreen(ash::wm::WindowState* window_state) OVERRIDE { 149 // Windows which cannot be maximized should not be fullscreened. 150 DCHECK(window_state->IsFullscreen() || window_state->CanMaximize()); 151 if (window_state->IsFullscreen()) 152 app_window_->Restore(); 153 else if (window_state->CanMaximize()) 154 app_window_->OSFullscreen(); 155 return true; 156 } 157 158 // Overridden from ash::wm::WindowStateObserver: 159 virtual void OnPostWindowStateTypeChange( 160 ash::wm::WindowState* window_state, 161 ash::wm::WindowStateType old_type) OVERRIDE { 162 // Since the window state might get set by a window manager, it is possible 163 // to come here before the application set its |BaseWindow|. 164 if (!window_state->IsFullscreen() && !window_state->IsMinimized() && 165 app_window_->GetBaseWindow() && 166 app_window_->GetBaseWindow()->IsFullscreenOrPending()) { 167 app_window_->Restore(); 168 // Usually OnNativeWindowChanged() is called when the window bounds are 169 // changed as a result of a state type change. Because the change in state 170 // type has already occurred, we need to call OnNativeWindowChanged() 171 // explicitly. 172 app_window_->OnNativeWindowChanged(); 173 } 174 } 175 176 // Overridden from aura::WindowObserver: 177 virtual void OnWindowDestroying(aura::Window* window) OVERRIDE { 178 window_state_->RemoveObserver(this); 179 window_state_->window()->RemoveObserver(this); 180 window_state_ = NULL; 181 } 182 183 // Not owned. 184 AppWindow* app_window_; 185 ash::wm::WindowState* window_state_; 186 187 DISALLOW_COPY_AND_ASSIGN(NativeAppWindowStateDelegate); 188}; 189#endif // USE_ASH 190 191} // namespace 192 193ChromeNativeAppWindowViews::ChromeNativeAppWindowViews() 194 : is_fullscreen_(false), 195 has_frame_color_(false), 196 active_frame_color_(SK_ColorBLACK), 197 inactive_frame_color_(SK_ColorBLACK) { 198} 199 200ChromeNativeAppWindowViews::~ChromeNativeAppWindowViews() {} 201 202void ChromeNativeAppWindowViews::OnBeforeWidgetInit( 203 views::Widget::InitParams* init_params, 204 views::Widget* widget) {} 205 206void ChromeNativeAppWindowViews::InitializeDefaultWindow( 207 const AppWindow::CreateParams& create_params) { 208 std::string app_name = web_app::GenerateApplicationNameFromExtensionId( 209 app_window()->extension_id()); 210 211 views::Widget::InitParams init_params(views::Widget::InitParams::TYPE_WINDOW); 212 init_params.delegate = this; 213 init_params.remove_standard_frame = IsFrameless() || has_frame_color_; 214 init_params.use_system_default_icon = true; 215 if (create_params.transparent_background) 216 init_params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; 217 init_params.keep_on_top = create_params.always_on_top; 218 219#if defined(OS_LINUX) && !defined(OS_CHROMEOS) 220 // Set up a custom WM_CLASS for app windows. This allows task switchers in 221 // X11 environments to distinguish them from main browser windows. 222 init_params.wm_class_name = web_app::GetWMClassFromAppName(app_name); 223 init_params.wm_class_class = shell_integration_linux::GetProgramClassName(); 224 const char kX11WindowRoleApp[] = "app"; 225 init_params.wm_role_name = std::string(kX11WindowRoleApp); 226#endif 227 228 OnBeforeWidgetInit(&init_params, widget()); 229 widget()->Init(init_params); 230 231 // The frame insets are required to resolve the bounds specifications 232 // correctly. So we set the window bounds and constraints now. 233 gfx::Insets frame_insets = GetFrameInsets(); 234 gfx::Rect window_bounds = create_params.GetInitialWindowBounds(frame_insets); 235 SetContentSizeConstraints(create_params.GetContentMinimumSize(frame_insets), 236 create_params.GetContentMaximumSize(frame_insets)); 237 if (!window_bounds.IsEmpty()) { 238 typedef apps::AppWindow::BoundsSpecification BoundsSpecification; 239 bool position_specified = 240 window_bounds.x() != BoundsSpecification::kUnspecifiedPosition && 241 window_bounds.y() != BoundsSpecification::kUnspecifiedPosition; 242 if (!position_specified) 243 widget()->CenterWindow(window_bounds.size()); 244 else 245 widget()->SetBounds(window_bounds); 246 } 247 248 if (IsFrameless() && 249 init_params.opacity == views::Widget::InitParams::TRANSLUCENT_WINDOW) { 250 // The given window is most likely not rectangular since it uses 251 // transparency and has no standard frame, don't show a shadow for it. 252 // TODO(skuhne): If we run into an application which should have a shadow 253 // but does not have, a new attribute has to be added. 254 wm::SetShadowType(widget()->GetNativeWindow(), wm::SHADOW_TYPE_NONE); 255 } 256 257 // Register accelarators supported by app windows. 258 // TODO(jeremya/stevenjb): should these be registered for panels too? 259 views::FocusManager* focus_manager = GetFocusManager(); 260 const std::map<ui::Accelerator, int>& accelerator_table = 261 GetAcceleratorTable(); 262 const bool is_kiosk_app_mode = chrome::IsRunningInForcedAppMode(); 263 264 // Ensures that kiosk mode accelerators are enabled when in kiosk mode (to be 265 // future proof). This is needed because GetAcceleratorTable() uses a static 266 // to store data and only checks kiosk mode once. If a platform app is 267 // launched before kiosk mode starts, the kiosk accelerators will not be 268 // registered. This DCHECK catches the case. 269 DCHECK(!is_kiosk_app_mode || 270 accelerator_table.size() == 271 arraysize(kAppWindowAcceleratorMap) + 272 arraysize(kAppWindowKioskAppModeAcceleratorMap)); 273 274 for (std::map<ui::Accelerator, int>::const_iterator iter = 275 accelerator_table.begin(); 276 iter != accelerator_table.end(); ++iter) { 277 if (is_kiosk_app_mode && !chrome::IsCommandAllowedInAppMode(iter->second)) 278 continue; 279 280 focus_manager->RegisterAccelerator( 281 iter->first, ui::AcceleratorManager::kNormalPriority, this); 282 } 283} 284 285void ChromeNativeAppWindowViews::InitializePanelWindow( 286 const AppWindow::CreateParams& create_params) { 287 views::Widget::InitParams params(views::Widget::InitParams::TYPE_PANEL); 288 params.delegate = this; 289 290 gfx::Rect initial_window_bounds = 291 create_params.GetInitialWindowBounds(gfx::Insets()); 292 preferred_size_ = gfx::Size(initial_window_bounds.width(), 293 initial_window_bounds.height()); 294 if (preferred_size_.width() == 0) 295 preferred_size_.set_width(kDefaultPanelWidth); 296 else if (preferred_size_.width() < kMinPanelWidth) 297 preferred_size_.set_width(kMinPanelWidth); 298 299 if (preferred_size_.height() == 0) 300 preferred_size_.set_height(kDefaultPanelHeight); 301 else if (preferred_size_.height() < kMinPanelHeight) 302 preferred_size_.set_height(kMinPanelHeight); 303#if defined(USE_ASH) 304 if (ash::Shell::HasInstance()) { 305 // Open a new panel on the target root. 306 aura::Window* target = ash::Shell::GetTargetRootWindow(); 307 params.bounds = ash::ScreenUtil::ConvertRectToScreen( 308 target, gfx::Rect(preferred_size_)); 309 } else { 310 params.bounds = gfx::Rect(preferred_size_); 311 } 312#else 313 params.bounds = gfx::Rect(preferred_size_); 314#endif 315 widget()->Init(params); 316 widget()->set_focus_on_creation(create_params.focused); 317 318#if defined(USE_ASH) 319 if (create_params.state == ui::SHOW_STATE_DETACHED) { 320 gfx::Rect window_bounds(initial_window_bounds.x(), 321 initial_window_bounds.y(), 322 preferred_size_.width(), 323 preferred_size_.height()); 324 aura::Window* native_window = GetNativeWindow(); 325 ash::wm::GetWindowState(native_window)->set_panel_attached(false); 326 aura::client::ParentWindowWithContext(native_window, 327 native_window->GetRootWindow(), 328 native_window->GetBoundsInScreen()); 329 widget()->SetBounds(window_bounds); 330 } 331#else 332 // TODO(stevenjb): NativeAppWindow panels need to be implemented for other 333 // platforms. 334#endif 335} 336 337views::NonClientFrameView* 338ChromeNativeAppWindowViews::CreateStandardDesktopAppFrame() { 339 return views::WidgetDelegateView::CreateNonClientFrameView(widget()); 340} 341 342apps::AppWindowFrameView* 343ChromeNativeAppWindowViews::CreateNonStandardAppFrame() { 344 apps::AppWindowFrameView* frame = 345 new apps::AppWindowFrameView(widget(), 346 this, 347 has_frame_color_, 348 active_frame_color_, 349 inactive_frame_color_); 350 frame->Init(); 351#if defined(USE_ASH) 352 // For Aura windows on the Ash desktop the sizes are different and the user 353 // can resize the window from slightly outside the bounds as well. 354 if (chrome::IsNativeWindowInAsh(widget()->GetNativeWindow())) { 355 frame->SetResizeSizes(ash::kResizeInsideBoundsSize, 356 ash::kResizeOutsideBoundsSize, 357 ash::kResizeAreaCornerSize); 358 } 359#endif 360 361#if !defined(OS_CHROMEOS) 362 // For non-Ash windows, install an easy resize window targeter, which ensures 363 // that the root window (not the app) receives mouse events on the edges. 364 if (chrome::GetHostDesktopTypeForNativeWindow(widget()->GetNativeWindow()) != 365 chrome::HOST_DESKTOP_TYPE_ASH) { 366 aura::Window* window = widget()->GetNativeWindow(); 367 int resize_inside = frame->resize_inside_bounds_size(); 368 gfx::Insets inset( 369 resize_inside, resize_inside, resize_inside, resize_inside); 370 // Add the EasyResizeWindowTargeter on the window, not its root window. The 371 // root window does not have a delegate, which is needed to handle the event 372 // in Linux. 373 window->SetEventTargeter(scoped_ptr<ui::EventTargeter>( 374 new wm::EasyResizeWindowTargeter(window, inset, inset))); 375 } 376#endif 377 378 return frame; 379} 380 381// ui::BaseWindow implementation. 382 383gfx::Rect ChromeNativeAppWindowViews::GetRestoredBounds() const { 384#if defined(USE_ASH) 385 gfx::Rect* bounds = widget()->GetNativeWindow()->GetProperty( 386 ash::kRestoreBoundsOverrideKey); 387 if (bounds && !bounds->IsEmpty()) 388 return *bounds; 389#endif 390 return widget()->GetRestoredBounds(); 391} 392 393ui::WindowShowState ChromeNativeAppWindowViews::GetRestoredState() const { 394#if !defined(USE_ASH) 395 if (IsMaximized()) 396 return ui::SHOW_STATE_MAXIMIZED; 397 if (IsFullscreen()) 398 return ui::SHOW_STATE_FULLSCREEN; 399#else 400 // Use kRestoreShowStateKey in case a window is minimized/hidden. 401 ui::WindowShowState restore_state = widget()->GetNativeWindow()->GetProperty( 402 aura::client::kRestoreShowStateKey); 403 if (widget()->GetNativeWindow()->GetProperty( 404 ash::kRestoreBoundsOverrideKey)) { 405 // If an override is given, we use that restore state (after filtering). 406 restore_state = widget()->GetNativeWindow()->GetProperty( 407 ash::kRestoreShowStateOverrideKey); 408 } else { 409 // Otherwise first normal states are checked. 410 if (IsMaximized()) 411 return ui::SHOW_STATE_MAXIMIZED; 412 if (IsFullscreen()) { 413 if (immersive_fullscreen_controller_.get() && 414 immersive_fullscreen_controller_->IsEnabled()) { 415 // Restore windows which were previously in immersive fullscreen to 416 // maximized. Restoring the window to a different fullscreen type 417 // makes for a bad experience. 418 return ui::SHOW_STATE_MAXIMIZED; 419 } 420 return ui::SHOW_STATE_FULLSCREEN; 421 } 422 } 423 // Whitelist states to return so that invalid and transient states 424 // are not saved and used to restore windows when they are recreated. 425 switch (restore_state) { 426 case ui::SHOW_STATE_NORMAL: 427 case ui::SHOW_STATE_MAXIMIZED: 428 case ui::SHOW_STATE_FULLSCREEN: 429 case ui::SHOW_STATE_DETACHED: 430 return restore_state; 431 432 case ui::SHOW_STATE_DEFAULT: 433 case ui::SHOW_STATE_MINIMIZED: 434 case ui::SHOW_STATE_INACTIVE: 435 case ui::SHOW_STATE_END: 436 return ui::SHOW_STATE_NORMAL; 437 } 438#endif // !defined(USE_ASH) 439 return ui::SHOW_STATE_NORMAL; 440} 441 442bool ChromeNativeAppWindowViews::IsAlwaysOnTop() const { 443 if (app_window()->window_type_is_panel()) { 444#if defined(USE_ASH) 445 return ash::wm::GetWindowState(widget()->GetNativeWindow()) 446 ->panel_attached(); 447#else 448 return true; 449#endif 450 } else { 451 return widget()->IsAlwaysOnTop(); 452 } 453} 454 455// views::ContextMenuController implementation. 456 457void ChromeNativeAppWindowViews::ShowContextMenuForView( 458 views::View* source, 459 const gfx::Point& p, 460 ui::MenuSourceType source_type) { 461#if defined(USE_ASH) && defined(OS_CHROMEOS) 462 scoped_ptr<ui::MenuModel> model = 463 CreateMultiUserContextMenu(app_window()->GetNativeWindow()); 464 if (!model.get()) 465 return; 466 467 // Only show context menu if point is in caption. 468 gfx::Point point_in_view_coords(p); 469 views::View::ConvertPointFromScreen(widget()->non_client_view(), 470 &point_in_view_coords); 471 int hit_test = 472 widget()->non_client_view()->NonClientHitTest(point_in_view_coords); 473 if (hit_test == HTCAPTION) { 474 menu_runner_.reset(new views::MenuRunner( 475 model.get(), 476 views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU)); 477 if (menu_runner_->RunMenuAt(source->GetWidget(), 478 NULL, 479 gfx::Rect(p, gfx::Size(0, 0)), 480 views::MENU_ANCHOR_TOPLEFT, 481 source_type) == 482 views::MenuRunner::MENU_DELETED) { 483 return; 484 } 485 } 486#endif 487} 488 489// views::WidgetDelegate implementation. 490 491gfx::ImageSkia ChromeNativeAppWindowViews::GetWindowAppIcon() { 492 gfx::Image app_icon = app_window()->app_icon(); 493 if (app_icon.IsEmpty()) 494 return GetWindowIcon(); 495 else 496 return *app_icon.ToImageSkia(); 497} 498 499gfx::ImageSkia ChromeNativeAppWindowViews::GetWindowIcon() { 500 content::WebContents* web_contents = app_window()->web_contents(); 501 if (web_contents) { 502 FaviconTabHelper* favicon_tab_helper = 503 FaviconTabHelper::FromWebContents(web_contents); 504 gfx::Image app_icon = favicon_tab_helper->GetFavicon(); 505 if (!app_icon.IsEmpty()) 506 return *app_icon.ToImageSkia(); 507 } 508 return gfx::ImageSkia(); 509} 510 511views::NonClientFrameView* ChromeNativeAppWindowViews::CreateNonClientFrameView( 512 views::Widget* widget) { 513#if defined(USE_ASH) 514 if (chrome::IsNativeViewInAsh(widget->GetNativeView())) { 515 // Set the delegate now because CustomFrameViewAsh sets the 516 // WindowStateDelegate if one is not already set. 517 ash::wm::GetWindowState(GetNativeWindow())->SetDelegate( 518 scoped_ptr<ash::wm::WindowStateDelegate>( 519 new NativeAppWindowStateDelegate(app_window(), this)).Pass()); 520 521 if (app_window()->window_type_is_panel()) { 522 ash::PanelFrameView::FrameType frame_type = IsFrameless() ? 523 ash::PanelFrameView::FRAME_NONE : ash::PanelFrameView::FRAME_ASH; 524 views::NonClientFrameView* frame_view = 525 new ash::PanelFrameView(widget, frame_type); 526 frame_view->set_context_menu_controller(this); 527 return frame_view; 528 } 529 530 if (IsFrameless()) 531 return CreateNonStandardAppFrame(); 532 533 ash::CustomFrameViewAsh* custom_frame_view = 534 new ash::CustomFrameViewAsh(widget); 535 // Non-frameless app windows can be put into immersive fullscreen. 536 immersive_fullscreen_controller_.reset( 537 new ash::ImmersiveFullscreenController()); 538 custom_frame_view->InitImmersiveFullscreenControllerForView( 539 immersive_fullscreen_controller_.get()); 540 custom_frame_view->GetHeaderView()->set_context_menu_controller(this); 541 return custom_frame_view; 542 } 543#endif 544 return (IsFrameless() || has_frame_color_) ? 545 CreateNonStandardAppFrame() : CreateStandardDesktopAppFrame(); 546} 547 548bool ChromeNativeAppWindowViews::WidgetHasHitTestMask() const { 549 return shape_ != NULL; 550} 551 552void ChromeNativeAppWindowViews::GetWidgetHitTestMask(gfx::Path* mask) const { 553 shape_->getBoundaryPath(mask); 554} 555 556// views::View implementation. 557 558gfx::Size ChromeNativeAppWindowViews::GetPreferredSize() const { 559 if (!preferred_size_.IsEmpty()) 560 return preferred_size_; 561 return NativeAppWindowViews::GetPreferredSize(); 562} 563 564bool ChromeNativeAppWindowViews::AcceleratorPressed( 565 const ui::Accelerator& accelerator) { 566 const std::map<ui::Accelerator, int>& accelerator_table = 567 GetAcceleratorTable(); 568 std::map<ui::Accelerator, int>::const_iterator iter = 569 accelerator_table.find(accelerator); 570 DCHECK(iter != accelerator_table.end()); 571 int command_id = iter->second; 572 switch (command_id) { 573 case IDC_CLOSE_WINDOW: 574 Close(); 575 return true; 576 case IDC_ZOOM_MINUS: 577 chrome_page_zoom::Zoom(web_view()->GetWebContents(), 578 content::PAGE_ZOOM_OUT); 579 return true; 580 case IDC_ZOOM_NORMAL: 581 chrome_page_zoom::Zoom(web_view()->GetWebContents(), 582 content::PAGE_ZOOM_RESET); 583 return true; 584 case IDC_ZOOM_PLUS: 585 chrome_page_zoom::Zoom(web_view()->GetWebContents(), 586 content::PAGE_ZOOM_IN); 587 return true; 588 default: 589 NOTREACHED() << "Unknown accelerator sent to app window."; 590 } 591 return NativeAppWindowViews::AcceleratorPressed(accelerator); 592} 593 594// NativeAppWindow implementation. 595 596void ChromeNativeAppWindowViews::SetFullscreen(int fullscreen_types) { 597 // Fullscreen not supported by panels. 598 if (app_window()->window_type_is_panel()) 599 return; 600 is_fullscreen_ = (fullscreen_types != AppWindow::FULLSCREEN_TYPE_NONE); 601 widget()->SetFullscreen(is_fullscreen_); 602 603#if defined(USE_ASH) 604 if (immersive_fullscreen_controller_.get()) { 605 // |immersive_fullscreen_controller_| should only be set if immersive 606 // fullscreen is the fullscreen type used by the OS. 607 immersive_fullscreen_controller_->SetEnabled( 608 ash::ImmersiveFullscreenController::WINDOW_TYPE_PACKAGED_APP, 609 (fullscreen_types & AppWindow::FULLSCREEN_TYPE_OS) != 0); 610 // Autohide the shelf instead of hiding the shelf completely when only in 611 // OS fullscreen. 612 ash::wm::WindowState* window_state = 613 ash::wm::GetWindowState(widget()->GetNativeWindow()); 614 window_state->set_hide_shelf_when_fullscreen(fullscreen_types != 615 AppWindow::FULLSCREEN_TYPE_OS); 616 DCHECK(ash::Shell::HasInstance()); 617 ash::Shell::GetInstance()->UpdateShelfVisibility(); 618 } 619#endif 620 621 // TODO(jeremya) we need to call RenderViewHost::ExitFullscreen() if we 622 // ever drop the window out of fullscreen in response to something that 623 // wasn't the app calling webkitCancelFullScreen(). 624} 625 626bool ChromeNativeAppWindowViews::IsFullscreenOrPending() const { 627 return is_fullscreen_; 628} 629 630bool ChromeNativeAppWindowViews::IsDetached() const { 631 if (!app_window()->window_type_is_panel()) 632 return false; 633#if defined(USE_ASH) 634 return !ash::wm::GetWindowState(widget()->GetNativeWindow()) 635 ->panel_attached(); 636#else 637 return false; 638#endif 639} 640 641void ChromeNativeAppWindowViews::UpdateBadgeIcon() { 642 const gfx::Image* icon = NULL; 643 if (!app_window()->badge_icon().IsEmpty()) { 644 icon = &app_window()->badge_icon(); 645 // chrome::DrawTaskbarDecoration can do interesting things with non-square 646 // bitmaps. 647 // TODO(benwells): Refactor chrome::DrawTaskbarDecoration to not be avatar 648 // specific, and lift this restriction. 649 if (icon->Width() != icon->Height()) { 650 LOG(ERROR) << "Attempt to set a non-square badge; request ignored."; 651 return; 652 } 653 } 654 chrome::DrawTaskbarDecoration(GetNativeWindow(), icon); 655} 656 657void ChromeNativeAppWindowViews::UpdateShape(scoped_ptr<SkRegion> region) { 658 bool had_shape = shape_; 659 shape_ = region.Pass(); 660 661 aura::Window* native_window = widget()->GetNativeWindow(); 662 if (shape_) { 663 widget()->SetShape(new SkRegion(*shape_)); 664 if (!had_shape) { 665 native_window->SetEventTargeter(scoped_ptr<ui::EventTargeter>( 666 new ShapedAppWindowTargeter(native_window, this))); 667 } 668 } else { 669 widget()->SetShape(NULL); 670 if (had_shape) 671 native_window->SetEventTargeter(scoped_ptr<ui::EventTargeter>()); 672 } 673} 674 675bool ChromeNativeAppWindowViews::HasFrameColor() const { 676 return has_frame_color_; 677} 678 679SkColor ChromeNativeAppWindowViews::ActiveFrameColor() const { 680 return active_frame_color_; 681} 682 683SkColor ChromeNativeAppWindowViews::InactiveFrameColor() const { 684 return inactive_frame_color_; 685} 686 687// NativeAppWindowViews implementation. 688 689void ChromeNativeAppWindowViews::InitializeWindow( 690 AppWindow* app_window, 691 const AppWindow::CreateParams& create_params) { 692 DCHECK(widget()); 693 has_frame_color_ = create_params.has_frame_color; 694 active_frame_color_ = create_params.active_frame_color; 695 inactive_frame_color_ = create_params.inactive_frame_color; 696 if (create_params.window_type == AppWindow::WINDOW_TYPE_PANEL || 697 create_params.window_type == AppWindow::WINDOW_TYPE_V1_PANEL) { 698 InitializePanelWindow(create_params); 699 } else { 700 InitializeDefaultWindow(create_params); 701 } 702 extension_keybinding_registry_.reset(new ExtensionKeybindingRegistryViews( 703 Profile::FromBrowserContext(app_window->browser_context()), 704 widget()->GetFocusManager(), 705 extensions::ExtensionKeybindingRegistry::PLATFORM_APPS_ONLY, 706 NULL)); 707} 708