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 "base/command_line.h" 8 #include "base/file_util.h" 9 #include "base/path_service.h" 10 #include "base/threading/sequenced_worker_pool.h" 11 #include "chrome/app/chrome_command_ids.h" 12 #include "chrome/browser/extensions/extension_host.h" 13 #include "chrome/browser/favicon/favicon_tab_helper.h" 14 #include "chrome/browser/profiles/profile.h" 15 #include "chrome/browser/ui/views/extensions/extension_keybinding_registry_views.h" 16 #include "chrome/browser/ui/views/extensions/shell_window_frame_view.h" 17 #include "chrome/common/chrome_switches.h" 18 #include "chrome/common/extensions/extension.h" 19 #include "content/public/browser/browser_thread.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 "content/public/browser/web_contents_view.h" 24 #include "extensions/common/draggable_region.h" 25 #include "ui/views/controls/webview/webview.h" 26 #include "ui/views/widget/widget.h" 27 #include "ui/views/window/non_client_view.h" 28 29 #if defined(OS_WIN) 30 #include "base/strings/utf_string_conversions.h" 31 #include "chrome/browser/ui/web_applications/web_app_ui.h" 32 #include "chrome/browser/web_applications/web_app.h" 33 #include "chrome/browser/web_applications/web_app_win.h" 34 #include "ui/base/win/shell.h" 35 #include "ui/views/win/hwnd_util.h" 36 #endif 37 38 #if defined(USE_ASH) 39 #include "ash/screen_ash.h" 40 #include "ash/shell.h" 41 #include "ash/wm/custom_frame_view_ash.h" 42 #include "ash/wm/panels/panel_frame_view.h" 43 #include "ash/wm/window_properties.h" 44 #include "chrome/browser/ui/ash/ash_util.h" 45 #include "ui/aura/client/aura_constants.h" 46 #include "ui/aura/root_window.h" 47 #include "ui/aura/window.h" 48 #endif 49 50 using apps::ShellWindow; 51 52 namespace { 53 54 const int kMinPanelWidth = 100; 55 const int kMinPanelHeight = 100; 56 const int kDefaultPanelWidth = 200; 57 const int kDefaultPanelHeight = 300; 58 const int kResizeInsideBoundsSize = 5; 59 60 struct AcceleratorMapping { 61 ui::KeyboardCode keycode; 62 int modifiers; 63 int command_id; 64 }; 65 const AcceleratorMapping kAppWindowAcceleratorMap[] = { 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 }; 70 71 const std::map<ui::Accelerator, int>& GetAcceleratorTable() { 72 typedef std::map<ui::Accelerator, int> AcceleratorMap; 73 CR_DEFINE_STATIC_LOCAL(AcceleratorMap, accelerators, ()); 74 if (accelerators.empty()) { 75 for (size_t i = 0; i < arraysize(kAppWindowAcceleratorMap); ++i) { 76 ui::Accelerator accelerator(kAppWindowAcceleratorMap[i].keycode, 77 kAppWindowAcceleratorMap[i].modifiers); 78 accelerators[accelerator] = kAppWindowAcceleratorMap[i].command_id; 79 } 80 } 81 return accelerators; 82 } 83 84 #if defined(OS_WIN) 85 void CreateIconAndSetRelaunchDetails( 86 const base::FilePath web_app_path, 87 const base::FilePath icon_file, 88 const ShellIntegration::ShortcutInfo& shortcut_info, 89 const HWND hwnd) { 90 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); 91 92 // Set the relaunch data so "Pin this program to taskbar" has the app's 93 // information. 94 CommandLine command_line = ShellIntegration::CommandLineArgsForLauncher( 95 shortcut_info.url, 96 shortcut_info.extension_id, 97 shortcut_info.profile_path); 98 99 // TODO(benwells): Change this to use app_host.exe. 100 base::FilePath chrome_exe; 101 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) { 102 NOTREACHED(); 103 return; 104 } 105 command_line.SetProgram(chrome_exe); 106 ui::win::SetRelaunchDetailsForWindow(command_line.GetCommandLineString(), 107 shortcut_info.title, hwnd); 108 109 if (!base::PathExists(web_app_path) && 110 !file_util::CreateDirectory(web_app_path)) { 111 return; 112 } 113 ui::win::SetAppIconForWindow(icon_file.value(), hwnd); 114 web_app::internals::CheckAndSaveIcon(icon_file, shortcut_info.favicon); 115 } 116 #endif 117 118 } // namespace 119 120 NativeAppWindowViews::NativeAppWindowViews( 121 ShellWindow* shell_window, 122 const ShellWindow::CreateParams& create_params) 123 : shell_window_(shell_window), 124 web_view_(NULL), 125 window_(NULL), 126 is_fullscreen_(false), 127 frameless_(create_params.frame == ShellWindow::FRAME_NONE), 128 transparent_background_(create_params.transparent_background), 129 minimum_size_(create_params.minimum_size), 130 maximum_size_(create_params.maximum_size), 131 resizable_(create_params.resizable), 132 weak_ptr_factory_(this) { 133 Observe(web_contents()); 134 135 window_ = new views::Widget; 136 if (create_params.window_type == ShellWindow::WINDOW_TYPE_PANEL || 137 create_params.window_type == ShellWindow::WINDOW_TYPE_V1_PANEL) { 138 InitializePanelWindow(create_params); 139 } else { 140 InitializeDefaultWindow(create_params); 141 } 142 extension_keybinding_registry_.reset( 143 new ExtensionKeybindingRegistryViews( 144 profile(), 145 window_->GetFocusManager(), 146 extensions::ExtensionKeybindingRegistry::PLATFORM_APPS_ONLY, 147 shell_window_)); 148 149 OnViewWasResized(); 150 window_->AddObserver(this); 151 } 152 153 NativeAppWindowViews::~NativeAppWindowViews() { 154 web_view_->SetWebContents(NULL); 155 } 156 157 void NativeAppWindowViews::InitializeDefaultWindow( 158 const ShellWindow::CreateParams& create_params) { 159 views::Widget::InitParams init_params(views::Widget::InitParams::TYPE_WINDOW); 160 init_params.delegate = this; 161 init_params.remove_standard_frame = ShouldUseChromeStyleFrame(); 162 init_params.use_system_default_icon = true; 163 // TODO(erg): Conceptually, these are toplevel windows, but we theoretically 164 // could plumb context through to here in some cases. 165 init_params.top_level = true; 166 gfx::Rect window_bounds = create_params.bounds; 167 bool position_specified = 168 window_bounds.x() != INT_MIN && window_bounds.y() != INT_MIN; 169 if (position_specified && !window_bounds.IsEmpty()) 170 init_params.bounds = window_bounds; 171 window_->Init(init_params); 172 173 gfx::Rect adjusted_bounds = window_bounds; 174 adjusted_bounds.Inset(-GetFrameInsets()); 175 // Center window if no position was specified. 176 if (!position_specified) 177 window_->CenterWindow(adjusted_bounds.size()); 178 else if (!adjusted_bounds.IsEmpty() && adjusted_bounds != window_bounds) 179 window_->SetBounds(adjusted_bounds); 180 181 // Register accelarators supported by app windows. 182 // TODO(jeremya/stevenjb): should these be registered for panels too? 183 views::FocusManager* focus_manager = GetFocusManager(); 184 const std::map<ui::Accelerator, int>& accelerator_table = 185 GetAcceleratorTable(); 186 for (std::map<ui::Accelerator, int>::const_iterator iter = 187 accelerator_table.begin(); 188 iter != accelerator_table.end(); ++iter) { 189 focus_manager->RegisterAccelerator( 190 iter->first, ui::AcceleratorManager::kNormalPriority, this); 191 } 192 193 #if defined(OS_WIN) 194 string16 app_name = UTF8ToWide( 195 web_app::GenerateApplicationNameFromExtensionId(extension()->id())); 196 HWND hwnd = GetNativeAppWindowHWND(); 197 ui::win::SetAppIdForWindow(ShellIntegration::GetAppModelIdForProfile( 198 app_name, profile()->GetPath()), hwnd); 199 200 web_app::UpdateShortcutInfoAndIconForApp( 201 *extension(), profile(), 202 base::Bind(&NativeAppWindowViews::OnShortcutInfoLoaded, 203 weak_ptr_factory_.GetWeakPtr())); 204 #endif 205 } 206 207 #if defined(OS_WIN) 208 void NativeAppWindowViews::OnShortcutInfoLoaded( 209 const ShellIntegration::ShortcutInfo& shortcut_info) { 210 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 211 212 HWND hwnd = GetNativeAppWindowHWND(); 213 214 // Set window's icon to the one we're about to create/update in the web app 215 // path. The icon cache will refresh on icon creation. 216 base::FilePath web_app_path = web_app::GetWebAppDataDirectory( 217 shortcut_info.profile_path, shortcut_info.extension_id, 218 shortcut_info.url); 219 base::FilePath icon_file = web_app_path 220 .Append(web_app::internals::GetSanitizedFileName(shortcut_info.title)) 221 .ReplaceExtension(FILE_PATH_LITERAL(".ico")); 222 223 content::BrowserThread::PostBlockingPoolTask( 224 FROM_HERE, 225 base::Bind(&CreateIconAndSetRelaunchDetails, 226 web_app_path, icon_file, shortcut_info, hwnd)); 227 } 228 229 HWND NativeAppWindowViews::GetNativeAppWindowHWND() const { 230 return views::HWNDForWidget(GetWidget()->GetTopLevelWidget()); 231 } 232 #endif 233 234 void NativeAppWindowViews::InitializePanelWindow( 235 const ShellWindow::CreateParams& create_params) { 236 views::Widget::InitParams params(views::Widget::InitParams::TYPE_PANEL); 237 params.delegate = this; 238 239 preferred_size_ = gfx::Size(create_params.bounds.width(), 240 create_params.bounds.height()); 241 if (preferred_size_.width() == 0) 242 preferred_size_.set_width(kDefaultPanelWidth); 243 else if (preferred_size_.width() < kMinPanelWidth) 244 preferred_size_.set_width(kMinPanelWidth); 245 246 if (preferred_size_.height() == 0) 247 preferred_size_.set_height(kDefaultPanelHeight); 248 else if (preferred_size_.height() < kMinPanelHeight) 249 preferred_size_.set_height(kMinPanelHeight); 250 #if defined(USE_ASH) 251 if (ash::Shell::HasInstance()) { 252 // Open a new panel on the active root window where 253 // a current active/focused window is on. 254 aura::RootWindow* active = ash::Shell::GetActiveRootWindow(); 255 params.bounds = ash::ScreenAsh::ConvertRectToScreen( 256 active, gfx::Rect(preferred_size_)); 257 } else { 258 params.bounds = gfx::Rect(preferred_size_); 259 } 260 #else 261 params.bounds = gfx::Rect(preferred_size_); 262 #endif 263 // TODO(erg): Conceptually, these are toplevel windows, but we theoretically 264 // could plumb context through to here in some cases. 265 params.top_level = true; 266 window_->Init(params); 267 window_->set_focus_on_creation(create_params.focused); 268 269 #if defined(USE_ASH) 270 if (create_params.state == ui::SHOW_STATE_DETACHED) { 271 gfx::Rect window_bounds(create_params.bounds.x(), 272 create_params.bounds.y(), 273 preferred_size_.width(), 274 preferred_size_.height()); 275 aura::Window* native_window = GetNativeWindow(); 276 native_window->SetProperty(ash::internal::kPanelAttachedKey, false); 277 native_window->SetDefaultParentByRootWindow( 278 native_window->GetRootWindow(), native_window->GetBoundsInScreen()); 279 window_->SetBounds(window_bounds); 280 } 281 #else 282 // TODO(stevenjb): NativeAppWindow panels need to be implemented for other 283 // platforms. 284 #endif 285 } 286 287 // ui::BaseWindow implementation. 288 289 bool NativeAppWindowViews::IsActive() const { 290 return window_->IsActive(); 291 } 292 293 bool NativeAppWindowViews::IsMaximized() const { 294 return window_->IsMaximized(); 295 } 296 297 bool NativeAppWindowViews::IsMinimized() const { 298 return window_->IsMinimized(); 299 } 300 301 bool NativeAppWindowViews::IsFullscreen() const { 302 return window_->IsFullscreen(); 303 } 304 305 gfx::NativeWindow NativeAppWindowViews::GetNativeWindow() { 306 return window_->GetNativeWindow(); 307 } 308 309 gfx::Rect NativeAppWindowViews::GetRestoredBounds() const { 310 return window_->GetRestoredBounds(); 311 } 312 313 ui::WindowShowState NativeAppWindowViews::GetRestoredState() const { 314 if (IsMaximized()) 315 return ui::SHOW_STATE_MAXIMIZED; 316 #if defined(USE_ASH) 317 // On Ash, restore fullscreen. 318 if (IsFullscreen()) 319 return ui::SHOW_STATE_FULLSCREEN; 320 321 // Use kRestoreShowStateKey in case a window is minimized/hidden. 322 ui::WindowShowState restore_state = 323 window_->GetNativeWindow()->GetProperty( 324 aura::client::kRestoreShowStateKey); 325 // Whitelist states to return so that invalid and transient states 326 // are not saved and used to restore windows when they are recreated. 327 switch (restore_state) { 328 case ui::SHOW_STATE_NORMAL: 329 case ui::SHOW_STATE_MAXIMIZED: 330 case ui::SHOW_STATE_FULLSCREEN: 331 case ui::SHOW_STATE_DETACHED: 332 return restore_state; 333 334 case ui::SHOW_STATE_DEFAULT: 335 case ui::SHOW_STATE_MINIMIZED: 336 case ui::SHOW_STATE_INACTIVE: 337 case ui::SHOW_STATE_END: 338 return ui::SHOW_STATE_NORMAL; 339 } 340 #endif 341 return ui::SHOW_STATE_NORMAL; 342 } 343 344 gfx::Rect NativeAppWindowViews::GetBounds() const { 345 return window_->GetWindowBoundsInScreen(); 346 } 347 348 void NativeAppWindowViews::Show() { 349 if (window_->IsVisible()) { 350 window_->Activate(); 351 return; 352 } 353 354 window_->Show(); 355 } 356 357 void NativeAppWindowViews::ShowInactive() { 358 if (window_->IsVisible()) 359 return; 360 window_->ShowInactive(); 361 } 362 363 void NativeAppWindowViews::Hide() { 364 window_->Hide(); 365 } 366 367 void NativeAppWindowViews::Close() { 368 window_->Close(); 369 } 370 371 void NativeAppWindowViews::Activate() { 372 window_->Activate(); 373 } 374 375 void NativeAppWindowViews::Deactivate() { 376 window_->Deactivate(); 377 } 378 379 void NativeAppWindowViews::Maximize() { 380 window_->Maximize(); 381 } 382 383 void NativeAppWindowViews::Minimize() { 384 window_->Minimize(); 385 } 386 387 void NativeAppWindowViews::Restore() { 388 window_->Restore(); 389 } 390 391 void NativeAppWindowViews::SetBounds(const gfx::Rect& bounds) { 392 GetWidget()->SetBounds(bounds); 393 } 394 395 void NativeAppWindowViews::FlashFrame(bool flash) { 396 window_->FlashFrame(flash); 397 } 398 399 bool NativeAppWindowViews::IsAlwaysOnTop() const { 400 if (!shell_window_->window_type_is_panel()) 401 return false; 402 #if defined(USE_ASH) 403 return window_->GetNativeWindow()->GetProperty( 404 ash::internal::kPanelAttachedKey); 405 #else 406 return true; 407 #endif 408 } 409 410 gfx::Insets NativeAppWindowViews::GetFrameInsets() const { 411 if (frameless()) 412 return gfx::Insets(); 413 414 // The pretend client_bounds passed in need to be large enough to ensure that 415 // GetWindowBoundsForClientBounds() doesn't decide that it needs more than 416 // the specified amount of space to fit the window controls in, and return a 417 // number larger than the real frame insets. Most window controls are smaller 418 // than 1000x1000px, so this should be big enough. 419 gfx::Rect client_bounds = gfx::Rect(1000, 1000); 420 gfx::Rect window_bounds = 421 window_->non_client_view()->GetWindowBoundsForClientBounds( 422 client_bounds); 423 return window_bounds.InsetsFrom(client_bounds); 424 } 425 426 gfx::NativeView NativeAppWindowViews::GetHostView() const { 427 return window_->GetNativeView(); 428 } 429 430 gfx::Point NativeAppWindowViews::GetDialogPosition(const gfx::Size& size) { 431 gfx::Size shell_window_size = window_->GetWindowBoundsInScreen().size(); 432 return gfx::Point(shell_window_size.width() / 2 - size.width() / 2, 433 shell_window_size.height() / 2 - size.height() / 2); 434 } 435 436 void NativeAppWindowViews::AddObserver( 437 web_modal::WebContentsModalDialogHostObserver* observer) { 438 observer_list_.AddObserver(observer); 439 } 440 void NativeAppWindowViews::RemoveObserver( 441 web_modal::WebContentsModalDialogHostObserver* observer) { 442 observer_list_.RemoveObserver(observer); 443 } 444 445 // Private method. TODO(stevenjb): Move this below InitializePanelWindow() 446 // to match declaration order. 447 void NativeAppWindowViews::OnViewWasResized() { 448 // TODO(jeremya): this doesn't seem like a terribly elegant way to keep the 449 // window shape in sync. 450 #if defined(OS_WIN) && !defined(USE_AURA) 451 DCHECK(window_); 452 DCHECK(web_view_); 453 gfx::Size sz = web_view_->size(); 454 int height = sz.height(), width = sz.width(); 455 if (ShouldUseChromeStyleFrame()) { 456 // Set the window shape of the RWHV. 457 const int kCornerRadius = 1; 458 gfx::Path path; 459 if (window_->IsMaximized() || window_->IsFullscreen()) { 460 // Don't round the corners when the window is maximized or fullscreen. 461 path.addRect(0, 0, width, height); 462 } else { 463 if (frameless_) { 464 path.moveTo(0, kCornerRadius); 465 path.lineTo(kCornerRadius, 0); 466 path.lineTo(width - kCornerRadius, 0); 467 path.lineTo(width, kCornerRadius); 468 } else { 469 // Don't round the top corners in chrome-style frame mode. 470 path.moveTo(0, 0); 471 path.lineTo(width, 0); 472 } 473 path.lineTo(width, height - kCornerRadius - 1); 474 path.lineTo(width - kCornerRadius - 1, height); 475 path.lineTo(kCornerRadius + 1, height); 476 path.lineTo(0, height - kCornerRadius - 1); 477 path.close(); 478 } 479 SetWindowRgn(web_contents()->GetView()->GetNativeView(), 480 path.CreateNativeRegion(), 1); 481 } 482 483 SkRegion* rgn = new SkRegion; 484 if (!window_->IsFullscreen()) { 485 if (draggable_region()) 486 rgn->op(*draggable_region(), SkRegion::kUnion_Op); 487 if (!window_->IsMaximized()) { 488 if (frameless_) 489 rgn->op(0, 0, width, kResizeInsideBoundsSize, SkRegion::kUnion_Op); 490 rgn->op(0, 0, kResizeInsideBoundsSize, height, SkRegion::kUnion_Op); 491 rgn->op(width - kResizeInsideBoundsSize, 0, width, height, 492 SkRegion::kUnion_Op); 493 rgn->op(0, height - kResizeInsideBoundsSize, width, height, 494 SkRegion::kUnion_Op); 495 } 496 } 497 if (web_contents()->GetRenderViewHost()->GetView()) 498 web_contents()->GetRenderViewHost()->GetView()->SetClickthroughRegion(rgn); 499 #endif 500 501 FOR_EACH_OBSERVER(web_modal::WebContentsModalDialogHostObserver, 502 observer_list_, 503 OnPositionRequiresUpdate()); 504 } 505 506 bool NativeAppWindowViews::ShouldUseChromeStyleFrame() const { 507 return !CommandLine::ForCurrentProcess()->HasSwitch( 508 switches::kAppsUseNativeFrame) || frameless_; 509 } 510 511 // WidgetDelegate implementation. 512 513 void NativeAppWindowViews::OnWidgetMove() { 514 shell_window_->OnNativeWindowChanged(); 515 } 516 517 views::View* NativeAppWindowViews::GetInitiallyFocusedView() { 518 return web_view_; 519 } 520 521 bool NativeAppWindowViews::CanResize() const { 522 return resizable_ && 523 (maximum_size_.IsEmpty() || minimum_size_ != maximum_size_); 524 } 525 526 bool NativeAppWindowViews::CanMaximize() const { 527 return resizable_ && maximum_size_.IsEmpty(); 528 } 529 530 string16 NativeAppWindowViews::GetWindowTitle() const { 531 return shell_window_->GetTitle(); 532 } 533 534 bool NativeAppWindowViews::ShouldShowWindowTitle() const { 535 return shell_window_->window_type() == ShellWindow::WINDOW_TYPE_V1_PANEL; 536 } 537 538 gfx::ImageSkia NativeAppWindowViews::GetWindowAppIcon() { 539 gfx::Image app_icon = shell_window_->app_icon(); 540 if (app_icon.IsEmpty()) 541 return GetWindowIcon(); 542 else 543 return *app_icon.ToImageSkia(); 544 } 545 546 gfx::ImageSkia NativeAppWindowViews::GetWindowIcon() { 547 content::WebContents* web_contents = shell_window_->web_contents(); 548 if (web_contents) { 549 FaviconTabHelper* favicon_tab_helper = 550 FaviconTabHelper::FromWebContents(web_contents); 551 gfx::Image app_icon = favicon_tab_helper->GetFavicon(); 552 if (!app_icon.IsEmpty()) 553 return *app_icon.ToImageSkia(); 554 } 555 return gfx::ImageSkia(); 556 } 557 558 bool NativeAppWindowViews::ShouldShowWindowIcon() const { 559 return shell_window_->window_type() == ShellWindow::WINDOW_TYPE_V1_PANEL; 560 } 561 562 void NativeAppWindowViews::SaveWindowPlacement(const gfx::Rect& bounds, 563 ui::WindowShowState show_state) { 564 views::WidgetDelegate::SaveWindowPlacement(bounds, show_state); 565 shell_window_->OnNativeWindowChanged(); 566 } 567 568 void NativeAppWindowViews::DeleteDelegate() { 569 window_->RemoveObserver(this); 570 shell_window_->OnNativeClose(); 571 } 572 573 views::Widget* NativeAppWindowViews::GetWidget() { 574 return window_; 575 } 576 577 const views::Widget* NativeAppWindowViews::GetWidget() const { 578 return window_; 579 } 580 581 views::NonClientFrameView* NativeAppWindowViews::CreateNonClientFrameView( 582 views::Widget* widget) { 583 #if defined(USE_ASH) 584 if (chrome::IsNativeViewInAsh(widget->GetNativeView())) { 585 if (shell_window_->window_type_is_panel()) { 586 ash::PanelFrameView::FrameType frame_type = frameless_ ? 587 ash::PanelFrameView::FRAME_NONE : ash::PanelFrameView::FRAME_ASH; 588 return new ash::PanelFrameView(widget, frame_type); 589 } 590 if (!frameless_) { 591 ash::CustomFrameViewAsh* frame = new ash::CustomFrameViewAsh(); 592 frame->Init(widget); 593 return frame; 594 } 595 } 596 #endif 597 if (ShouldUseChromeStyleFrame()) { 598 ShellWindowFrameView* frame_view = new ShellWindowFrameView(this); 599 frame_view->Init(window_); 600 return frame_view; 601 } 602 return views::WidgetDelegateView::CreateNonClientFrameView(widget); 603 } 604 605 bool NativeAppWindowViews::ShouldDescendIntoChildForEventHandling( 606 gfx::NativeView child, 607 const gfx::Point& location) { 608 #if defined(USE_AURA) 609 if (child == web_view_->web_contents()->GetView()->GetNativeView()) { 610 // Shell window should claim mouse events that fall within the draggable 611 // region. 612 return !draggable_region_.get() || 613 !draggable_region_->contains(location.x(), location.y()); 614 } 615 #endif 616 617 return true; 618 } 619 620 // WidgetObserver implementation. 621 622 void NativeAppWindowViews::OnWidgetVisibilityChanged(views::Widget* widget, 623 bool visible) { 624 shell_window_->OnNativeWindowChanged(); 625 } 626 627 void NativeAppWindowViews::OnWidgetActivationChanged(views::Widget* widget, 628 bool active) { 629 shell_window_->OnNativeWindowChanged(); 630 if (active) 631 shell_window_->OnNativeWindowActivated(); 632 } 633 634 // WebContentsObserver implementation. 635 636 void NativeAppWindowViews::RenderViewCreated( 637 content::RenderViewHost* render_view_host) { 638 if (transparent_background_) { 639 // Use a background with transparency to trigger transparency in Webkit. 640 SkBitmap background; 641 background.setConfig(SkBitmap::kARGB_8888_Config, 1, 1); 642 background.allocPixels(); 643 background.eraseARGB(0x00, 0x00, 0x00, 0x00); 644 645 content::RenderWidgetHostView* view = render_view_host->GetView(); 646 DCHECK(view); 647 view->SetBackground(background); 648 } 649 } 650 651 // views::View implementation. 652 653 void NativeAppWindowViews::Layout() { 654 DCHECK(web_view_); 655 web_view_->SetBounds(0, 0, width(), height()); 656 OnViewWasResized(); 657 } 658 659 void NativeAppWindowViews::ViewHierarchyChanged( 660 const ViewHierarchyChangedDetails& details) { 661 if (details.is_add && details.child == this) { 662 web_view_ = new views::WebView(NULL); 663 AddChildView(web_view_); 664 web_view_->SetWebContents(web_contents()); 665 } 666 } 667 668 gfx::Size NativeAppWindowViews::GetPreferredSize() { 669 if (!preferred_size_.IsEmpty()) 670 return preferred_size_; 671 return views::View::GetPreferredSize(); 672 } 673 674 gfx::Size NativeAppWindowViews::GetMinimumSize() { 675 return minimum_size_; 676 } 677 678 gfx::Size NativeAppWindowViews::GetMaximumSize() { 679 return maximum_size_; 680 } 681 682 void NativeAppWindowViews::OnFocus() { 683 web_view_->RequestFocus(); 684 } 685 686 bool NativeAppWindowViews::AcceleratorPressed( 687 const ui::Accelerator& accelerator) { 688 const std::map<ui::Accelerator, int>& accelerator_table = 689 GetAcceleratorTable(); 690 std::map<ui::Accelerator, int>::const_iterator iter = 691 accelerator_table.find(accelerator); 692 DCHECK(iter != accelerator_table.end()); 693 int command_id = iter->second; 694 switch (command_id) { 695 case IDC_CLOSE_WINDOW: 696 Close(); 697 return true; 698 default: 699 NOTREACHED() << "Unknown accelerator sent to app window."; 700 } 701 return false; 702 } 703 704 // NativeAppWindow implementation. 705 706 void NativeAppWindowViews::SetFullscreen(bool fullscreen) { 707 // Fullscreen not supported by panels. 708 if (shell_window_->window_type_is_panel()) 709 return; 710 is_fullscreen_ = fullscreen; 711 window_->SetFullscreen(fullscreen); 712 // TODO(jeremya) we need to call RenderViewHost::ExitFullscreen() if we 713 // ever drop the window out of fullscreen in response to something that 714 // wasn't the app calling webkitCancelFullScreen(). 715 } 716 717 bool NativeAppWindowViews::IsFullscreenOrPending() const { 718 return is_fullscreen_; 719 } 720 721 bool NativeAppWindowViews::IsDetached() const { 722 if (!shell_window_->window_type_is_panel()) 723 return false; 724 #if defined(USE_ASH) 725 return !window_->GetNativeWindow()->GetProperty( 726 ash::internal::kPanelAttachedKey); 727 #else 728 return false; 729 #endif 730 } 731 732 views::View* NativeAppWindowViews::GetContentsView() { 733 return this; 734 } 735 736 void NativeAppWindowViews::UpdateWindowIcon() { 737 window_->UpdateWindowIcon(); 738 } 739 740 void NativeAppWindowViews::UpdateWindowTitle() { 741 window_->UpdateWindowTitle(); 742 } 743 744 void NativeAppWindowViews::UpdateDraggableRegions( 745 const std::vector<extensions::DraggableRegion>& regions) { 746 // Draggable region is not supported for non-frameless window. 747 if (!frameless_) 748 return; 749 750 draggable_region_.reset(ShellWindow::RawDraggableRegionsToSkRegion(regions)); 751 OnViewWasResized(); 752 } 753 754 void NativeAppWindowViews::HandleKeyboardEvent( 755 const content::NativeWebKeyboardEvent& event) { 756 unhandled_keyboard_event_handler_.HandleKeyboardEvent(event, 757 GetFocusManager()); 758 } 759 760 void NativeAppWindowViews::RenderViewHostChanged() { 761 OnViewWasResized(); 762 } 763