chrome_native_app_window_views.cc revision 010d83a9304c5a91596085d917d248abff47903a
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 // TODO(erg): Conceptually, these are toplevel windows, but we theoretically 216 // could plumb context through to here in some cases. 217 init_params.top_level = true; 218 if (create_params.transparent_background) 219 init_params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; 220 init_params.keep_on_top = create_params.always_on_top; 221 222#if defined(OS_LINUX) && !defined(OS_CHROMEOS) 223 // Set up a custom WM_CLASS for app windows. This allows task switchers in 224 // X11 environments to distinguish them from main browser windows. 225 init_params.wm_class_name = web_app::GetWMClassFromAppName(app_name); 226 init_params.wm_class_class = ShellIntegrationLinux::GetProgramClassName(); 227 const char kX11WindowRoleApp[] = "app"; 228 init_params.wm_role_name = std::string(kX11WindowRoleApp); 229#endif 230 231 OnBeforeWidgetInit(&init_params, widget()); 232 widget()->Init(init_params); 233 234 // The frame insets are required to resolve the bounds specifications 235 // correctly. So we set the window bounds and constraints now. 236 gfx::Insets frame_insets = GetFrameInsets(); 237 gfx::Rect window_bounds = create_params.GetInitialWindowBounds(frame_insets); 238 SetContentSizeConstraints(create_params.GetContentMinimumSize(frame_insets), 239 create_params.GetContentMaximumSize(frame_insets)); 240 if (!window_bounds.IsEmpty()) { 241 typedef apps::AppWindow::BoundsSpecification BoundsSpecification; 242 bool position_specified = 243 window_bounds.x() != BoundsSpecification::kUnspecifiedPosition && 244 window_bounds.y() != BoundsSpecification::kUnspecifiedPosition; 245 if (!position_specified) 246 widget()->CenterWindow(window_bounds.size()); 247 else 248 widget()->SetBounds(window_bounds); 249 } 250 251 if (IsFrameless() && 252 init_params.opacity == views::Widget::InitParams::TRANSLUCENT_WINDOW && 253 !create_params.resizable) { 254 // The given window is most likely not rectangular since it uses 255 // transparency, has no standard frame and the user cannot resize it using 256 // the OS supplied methods. Therefore we do not use a shadow for it. 257 // TODO(skuhne): If we run into an application which should have a shadow 258 // but does not have, a new attribute has to be added. 259 wm::SetShadowType(widget()->GetNativeWindow(), wm::SHADOW_TYPE_NONE); 260 } 261 262 // Register accelarators supported by app windows. 263 // TODO(jeremya/stevenjb): should these be registered for panels too? 264 views::FocusManager* focus_manager = GetFocusManager(); 265 const std::map<ui::Accelerator, int>& accelerator_table = 266 GetAcceleratorTable(); 267 const bool is_kiosk_app_mode = chrome::IsRunningInForcedAppMode(); 268 269 // Ensures that kiosk mode accelerators are enabled when in kiosk mode (to be 270 // future proof). This is needed because GetAcceleratorTable() uses a static 271 // to store data and only checks kiosk mode once. If a platform app is 272 // launched before kiosk mode starts, the kiosk accelerators will not be 273 // registered. This DCHECK catches the case. 274 DCHECK(!is_kiosk_app_mode || 275 accelerator_table.size() == 276 arraysize(kAppWindowAcceleratorMap) + 277 arraysize(kAppWindowKioskAppModeAcceleratorMap)); 278 279 for (std::map<ui::Accelerator, int>::const_iterator iter = 280 accelerator_table.begin(); 281 iter != accelerator_table.end(); ++iter) { 282 if (is_kiosk_app_mode && !chrome::IsCommandAllowedInAppMode(iter->second)) 283 continue; 284 285 focus_manager->RegisterAccelerator( 286 iter->first, ui::AcceleratorManager::kNormalPriority, this); 287 } 288} 289 290void ChromeNativeAppWindowViews::InitializePanelWindow( 291 const AppWindow::CreateParams& create_params) { 292 views::Widget::InitParams params(views::Widget::InitParams::TYPE_PANEL); 293 params.delegate = this; 294 295 gfx::Rect initial_window_bounds = 296 create_params.GetInitialWindowBounds(gfx::Insets()); 297 preferred_size_ = gfx::Size(initial_window_bounds.width(), 298 initial_window_bounds.height()); 299 if (preferred_size_.width() == 0) 300 preferred_size_.set_width(kDefaultPanelWidth); 301 else if (preferred_size_.width() < kMinPanelWidth) 302 preferred_size_.set_width(kMinPanelWidth); 303 304 if (preferred_size_.height() == 0) 305 preferred_size_.set_height(kDefaultPanelHeight); 306 else if (preferred_size_.height() < kMinPanelHeight) 307 preferred_size_.set_height(kMinPanelHeight); 308#if defined(USE_ASH) 309 if (ash::Shell::HasInstance()) { 310 // Open a new panel on the target root. 311 aura::Window* target = ash::Shell::GetTargetRootWindow(); 312 params.bounds = ash::ScreenUtil::ConvertRectToScreen( 313 target, gfx::Rect(preferred_size_)); 314 } else { 315 params.bounds = gfx::Rect(preferred_size_); 316 } 317#else 318 params.bounds = gfx::Rect(preferred_size_); 319#endif 320 // TODO(erg): Conceptually, these are toplevel windows, but we theoretically 321 // could plumb context through to here in some cases. 322 params.top_level = true; 323 widget()->Init(params); 324 widget()->set_focus_on_creation(create_params.focused); 325 326#if defined(USE_ASH) 327 if (create_params.state == ui::SHOW_STATE_DETACHED) { 328 gfx::Rect window_bounds(initial_window_bounds.x(), 329 initial_window_bounds.y(), 330 preferred_size_.width(), 331 preferred_size_.height()); 332 aura::Window* native_window = GetNativeWindow(); 333 ash::wm::GetWindowState(native_window)->set_panel_attached(false); 334 aura::client::ParentWindowWithContext(native_window, 335 native_window->GetRootWindow(), 336 native_window->GetBoundsInScreen()); 337 widget()->SetBounds(window_bounds); 338 } 339#else 340 // TODO(stevenjb): NativeAppWindow panels need to be implemented for other 341 // platforms. 342#endif 343} 344 345views::NonClientFrameView* 346ChromeNativeAppWindowViews::CreateStandardDesktopAppFrame() { 347 return views::WidgetDelegateView::CreateNonClientFrameView(widget()); 348} 349 350apps::AppWindowFrameView* 351ChromeNativeAppWindowViews::CreateNonStandardAppFrame() { 352 apps::AppWindowFrameView* frame = 353 new apps::AppWindowFrameView(widget(), 354 this, 355 has_frame_color_, 356 active_frame_color_, 357 inactive_frame_color_); 358 frame->Init(); 359#if defined(USE_ASH) 360 // For Aura windows on the Ash desktop the sizes are different and the user 361 // can resize the window from slightly outside the bounds as well. 362 if (chrome::IsNativeWindowInAsh(widget()->GetNativeWindow())) { 363 frame->SetResizeSizes(ash::kResizeInsideBoundsSize, 364 ash::kResizeOutsideBoundsSize, 365 ash::kResizeAreaCornerSize); 366 } 367#endif 368 369#if !defined(OS_CHROMEOS) 370 // For non-Ash windows, install an easy resize window targeter, which ensures 371 // that the root window (not the app) receives mouse events on the edges. 372 if (chrome::GetHostDesktopTypeForNativeWindow(widget()->GetNativeWindow()) != 373 chrome::HOST_DESKTOP_TYPE_ASH) { 374 aura::Window* window = widget()->GetNativeWindow(); 375 int resize_inside = frame->resize_inside_bounds_size(); 376 gfx::Insets inset( 377 resize_inside, resize_inside, resize_inside, resize_inside); 378 // Add the EasyResizeWindowTargeter on the window, not its root window. The 379 // root window does not have a delegate, which is needed to handle the event 380 // in Linux. 381 window->SetEventTargeter(scoped_ptr<ui::EventTargeter>( 382 new wm::EasyResizeWindowTargeter(window, inset, inset))); 383 } 384#endif 385 386 return frame; 387} 388 389// ui::BaseWindow implementation. 390 391gfx::Rect ChromeNativeAppWindowViews::GetRestoredBounds() const { 392#if defined(USE_ASH) 393 gfx::Rect* bounds = widget()->GetNativeWindow()->GetProperty( 394 ash::kRestoreBoundsOverrideKey); 395 if (bounds && !bounds->IsEmpty()) 396 return *bounds; 397#endif 398 return widget()->GetRestoredBounds(); 399} 400 401ui::WindowShowState ChromeNativeAppWindowViews::GetRestoredState() const { 402#if !defined(USE_ASH) 403 if (IsMaximized()) 404 return ui::SHOW_STATE_MAXIMIZED; 405 if (IsFullscreen()) 406 return ui::SHOW_STATE_FULLSCREEN; 407#else 408 // Use kRestoreShowStateKey in case a window is minimized/hidden. 409 ui::WindowShowState restore_state = widget()->GetNativeWindow()->GetProperty( 410 aura::client::kRestoreShowStateKey); 411 if (widget()->GetNativeWindow()->GetProperty( 412 ash::kRestoreBoundsOverrideKey)) { 413 // If an override is given, we use that restore state (after filtering). 414 restore_state = widget()->GetNativeWindow()->GetProperty( 415 ash::kRestoreShowStateOverrideKey); 416 } else { 417 // Otherwise first normal states are checked. 418 if (IsMaximized()) 419 return ui::SHOW_STATE_MAXIMIZED; 420 if (IsFullscreen()) { 421 if (immersive_fullscreen_controller_.get() && 422 immersive_fullscreen_controller_->IsEnabled()) { 423 // Restore windows which were previously in immersive fullscreen to 424 // maximized. Restoring the window to a different fullscreen type 425 // makes for a bad experience. 426 return ui::SHOW_STATE_MAXIMIZED; 427 } 428 return ui::SHOW_STATE_FULLSCREEN; 429 } 430 } 431 // Whitelist states to return so that invalid and transient states 432 // are not saved and used to restore windows when they are recreated. 433 switch (restore_state) { 434 case ui::SHOW_STATE_NORMAL: 435 case ui::SHOW_STATE_MAXIMIZED: 436 case ui::SHOW_STATE_FULLSCREEN: 437 case ui::SHOW_STATE_DETACHED: 438 return restore_state; 439 440 case ui::SHOW_STATE_DEFAULT: 441 case ui::SHOW_STATE_MINIMIZED: 442 case ui::SHOW_STATE_INACTIVE: 443 case ui::SHOW_STATE_END: 444 return ui::SHOW_STATE_NORMAL; 445 } 446#endif // !defined(USE_ASH) 447 return ui::SHOW_STATE_NORMAL; 448} 449 450bool ChromeNativeAppWindowViews::IsAlwaysOnTop() const { 451 if (app_window()->window_type_is_panel()) { 452#if defined(USE_ASH) 453 return ash::wm::GetWindowState(widget()->GetNativeWindow()) 454 ->panel_attached(); 455#else 456 return true; 457#endif 458 } else { 459 return widget()->IsAlwaysOnTop(); 460 } 461} 462 463// views::ContextMenuController implementation. 464 465void ChromeNativeAppWindowViews::ShowContextMenuForView( 466 views::View* source, 467 const gfx::Point& p, 468 ui::MenuSourceType source_type) { 469#if defined(USE_ASH) && defined(OS_CHROMEOS) 470 scoped_ptr<ui::MenuModel> model = 471 CreateMultiUserContextMenu(app_window()->GetNativeWindow()); 472 if (!model.get()) 473 return; 474 475 // Only show context menu if point is in caption. 476 gfx::Point point_in_view_coords(p); 477 views::View::ConvertPointFromScreen(widget()->non_client_view(), 478 &point_in_view_coords); 479 int hit_test = 480 widget()->non_client_view()->NonClientHitTest(point_in_view_coords); 481 if (hit_test == HTCAPTION) { 482 menu_runner_.reset(new views::MenuRunner(model.get())); 483 if (menu_runner_->RunMenuAt(source->GetWidget(), 484 NULL, 485 gfx::Rect(p, gfx::Size(0, 0)), 486 views::MENU_ANCHOR_TOPLEFT, 487 source_type, 488 views::MenuRunner::HAS_MNEMONICS | 489 views::MenuRunner::CONTEXT_MENU) == 490 views::MenuRunner::MENU_DELETED) { 491 return; 492 } 493 } 494#endif 495} 496 497// views::WidgetDelegate implementation. 498 499gfx::ImageSkia ChromeNativeAppWindowViews::GetWindowAppIcon() { 500 gfx::Image app_icon = app_window()->app_icon(); 501 if (app_icon.IsEmpty()) 502 return GetWindowIcon(); 503 else 504 return *app_icon.ToImageSkia(); 505} 506 507gfx::ImageSkia ChromeNativeAppWindowViews::GetWindowIcon() { 508 content::WebContents* web_contents = app_window()->web_contents(); 509 if (web_contents) { 510 FaviconTabHelper* favicon_tab_helper = 511 FaviconTabHelper::FromWebContents(web_contents); 512 gfx::Image app_icon = favicon_tab_helper->GetFavicon(); 513 if (!app_icon.IsEmpty()) 514 return *app_icon.ToImageSkia(); 515 } 516 return gfx::ImageSkia(); 517} 518 519views::NonClientFrameView* ChromeNativeAppWindowViews::CreateNonClientFrameView( 520 views::Widget* widget) { 521#if defined(USE_ASH) 522 if (chrome::IsNativeViewInAsh(widget->GetNativeView())) { 523 // Set the delegate now because CustomFrameViewAsh sets the 524 // WindowStateDelegate if one is not already set. 525 ash::wm::GetWindowState(GetNativeWindow())->SetDelegate( 526 scoped_ptr<ash::wm::WindowStateDelegate>( 527 new NativeAppWindowStateDelegate(app_window(), this)).Pass()); 528 529 if (app_window()->window_type_is_panel()) { 530 ash::PanelFrameView::FrameType frame_type = IsFrameless() ? 531 ash::PanelFrameView::FRAME_NONE : ash::PanelFrameView::FRAME_ASH; 532 views::NonClientFrameView* frame_view = 533 new ash::PanelFrameView(widget, frame_type); 534 frame_view->set_context_menu_controller(this); 535 return frame_view; 536 } 537 538 if (IsFrameless()) 539 return CreateNonStandardAppFrame(); 540 541 ash::CustomFrameViewAsh* custom_frame_view = 542 new ash::CustomFrameViewAsh(widget); 543#if defined(OS_CHROMEOS) 544 // Non-frameless app windows can be put into immersive fullscreen. 545 // TODO(pkotwicz): Investigate if immersive fullscreen can be enabled for 546 // Windows Ash. 547 immersive_fullscreen_controller_.reset( 548 new ash::ImmersiveFullscreenController()); 549 custom_frame_view->InitImmersiveFullscreenControllerForView( 550 immersive_fullscreen_controller_.get()); 551#endif 552 custom_frame_view->GetHeaderView()->set_context_menu_controller(this); 553 return custom_frame_view; 554 } 555#endif 556#if defined(OS_LINUX) && !defined(OS_CHROMEOS) 557 // Linux always uses the non standard frame view because the OS draws the 558 // frame (if a frame is needed). 559 return CreateNonStandardAppFrame(); 560#else 561 if (IsFrameless() || has_frame_color_) 562 return CreateNonStandardAppFrame(); 563#endif 564 return CreateStandardDesktopAppFrame(); 565} 566 567bool ChromeNativeAppWindowViews::WidgetHasHitTestMask() const { 568 return shape_ != NULL; 569} 570 571void ChromeNativeAppWindowViews::GetWidgetHitTestMask(gfx::Path* mask) const { 572 shape_->getBoundaryPath(mask); 573} 574 575// views::View implementation. 576 577gfx::Size ChromeNativeAppWindowViews::GetPreferredSize() { 578 if (!preferred_size_.IsEmpty()) 579 return preferred_size_; 580 return NativeAppWindowViews::GetPreferredSize(); 581} 582 583bool ChromeNativeAppWindowViews::AcceleratorPressed( 584 const ui::Accelerator& accelerator) { 585 const std::map<ui::Accelerator, int>& accelerator_table = 586 GetAcceleratorTable(); 587 std::map<ui::Accelerator, int>::const_iterator iter = 588 accelerator_table.find(accelerator); 589 DCHECK(iter != accelerator_table.end()); 590 int command_id = iter->second; 591 switch (command_id) { 592 case IDC_CLOSE_WINDOW: 593 Close(); 594 return true; 595 case IDC_ZOOM_MINUS: 596 chrome_page_zoom::Zoom(web_view()->GetWebContents(), 597 content::PAGE_ZOOM_OUT); 598 return true; 599 case IDC_ZOOM_NORMAL: 600 chrome_page_zoom::Zoom(web_view()->GetWebContents(), 601 content::PAGE_ZOOM_RESET); 602 return true; 603 case IDC_ZOOM_PLUS: 604 chrome_page_zoom::Zoom(web_view()->GetWebContents(), 605 content::PAGE_ZOOM_IN); 606 return true; 607 default: 608 NOTREACHED() << "Unknown accelerator sent to app window."; 609 } 610 return NativeAppWindowViews::AcceleratorPressed(accelerator); 611} 612 613// NativeAppWindow implementation. 614 615void ChromeNativeAppWindowViews::SetFullscreen(int fullscreen_types) { 616 // Fullscreen not supported by panels. 617 if (app_window()->window_type_is_panel()) 618 return; 619 is_fullscreen_ = (fullscreen_types != AppWindow::FULLSCREEN_TYPE_NONE); 620 widget()->SetFullscreen(is_fullscreen_); 621 622#if defined(USE_ASH) 623 if (immersive_fullscreen_controller_.get()) { 624 // |immersive_fullscreen_controller_| should only be set if immersive 625 // fullscreen is the fullscreen type used by the OS. 626 immersive_fullscreen_controller_->SetEnabled( 627 ash::ImmersiveFullscreenController::WINDOW_TYPE_PACKAGED_APP, 628 (fullscreen_types & AppWindow::FULLSCREEN_TYPE_OS) != 0); 629 // Autohide the shelf instead of hiding the shelf completely when only in 630 // OS fullscreen. 631 ash::wm::WindowState* window_state = 632 ash::wm::GetWindowState(widget()->GetNativeWindow()); 633 window_state->set_hide_shelf_when_fullscreen(fullscreen_types != 634 AppWindow::FULLSCREEN_TYPE_OS); 635 DCHECK(ash::Shell::HasInstance()); 636 ash::Shell::GetInstance()->UpdateShelfVisibility(); 637 } 638#endif 639 640 // TODO(jeremya) we need to call RenderViewHost::ExitFullscreen() if we 641 // ever drop the window out of fullscreen in response to something that 642 // wasn't the app calling webkitCancelFullScreen(). 643} 644 645bool ChromeNativeAppWindowViews::IsFullscreenOrPending() const { 646 return is_fullscreen_; 647} 648 649bool ChromeNativeAppWindowViews::IsDetached() const { 650 if (!app_window()->window_type_is_panel()) 651 return false; 652#if defined(USE_ASH) 653 return !ash::wm::GetWindowState(widget()->GetNativeWindow()) 654 ->panel_attached(); 655#else 656 return false; 657#endif 658} 659 660void ChromeNativeAppWindowViews::UpdateBadgeIcon() { 661 const gfx::Image* icon = NULL; 662 if (!app_window()->badge_icon().IsEmpty()) { 663 icon = &app_window()->badge_icon(); 664 // chrome::DrawTaskbarDecoration can do interesting things with non-square 665 // bitmaps. 666 // TODO(benwells): Refactor chrome::DrawTaskbarDecoration to not be avatar 667 // specific, and lift this restriction. 668 if (icon->Width() != icon->Height()) { 669 LOG(ERROR) << "Attempt to set a non-square badge; request ignored."; 670 return; 671 } 672 } 673 chrome::DrawTaskbarDecoration(GetNativeWindow(), icon); 674} 675 676void ChromeNativeAppWindowViews::UpdateShape(scoped_ptr<SkRegion> region) { 677 bool had_shape = shape_; 678 shape_ = region.Pass(); 679 680 aura::Window* native_window = widget()->GetNativeWindow(); 681 if (shape_) { 682 native_window->layer()->SetAlphaShape( 683 make_scoped_ptr(new SkRegion(*shape_))); 684 if (!had_shape) { 685 native_window->SetEventTargeter(scoped_ptr<ui::EventTargeter>( 686 new ShapedAppWindowTargeter(native_window, this))); 687 } 688 } else { 689 widget()->SetShape(NULL); 690 if (had_shape) 691 native_window->SetEventTargeter(scoped_ptr<ui::EventTargeter>()); 692 } 693} 694 695bool ChromeNativeAppWindowViews::HasFrameColor() const { 696 return has_frame_color_; 697} 698 699SkColor ChromeNativeAppWindowViews::ActiveFrameColor() const { 700 return active_frame_color_; 701} 702 703SkColor ChromeNativeAppWindowViews::InactiveFrameColor() const { 704 return inactive_frame_color_; 705} 706 707// NativeAppWindowViews implementation. 708 709void ChromeNativeAppWindowViews::InitializeWindow( 710 AppWindow* app_window, 711 const AppWindow::CreateParams& create_params) { 712 DCHECK(widget()); 713 has_frame_color_ = create_params.has_frame_color; 714 active_frame_color_ = create_params.active_frame_color; 715 inactive_frame_color_ = create_params.inactive_frame_color; 716 if (create_params.window_type == AppWindow::WINDOW_TYPE_PANEL || 717 create_params.window_type == AppWindow::WINDOW_TYPE_V1_PANEL) { 718 InitializePanelWindow(create_params); 719 } else { 720 InitializeDefaultWindow(create_params); 721 } 722 extension_keybinding_registry_.reset(new ExtensionKeybindingRegistryViews( 723 Profile::FromBrowserContext(app_window->browser_context()), 724 widget()->GetFocusManager(), 725 extensions::ExtensionKeybindingRegistry::PLATFORM_APPS_ONLY, 726 NULL)); 727} 728