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