panel.cc revision 868fa2fe829687343ffae624259930155e16dbd8
1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/ui/panels/panel.h" 6 7#include "base/logging.h" 8#include "base/message_loop.h" 9#include "base/strings/utf_string_conversions.h" 10#include "chrome/app/chrome_command_ids.h" 11#include "chrome/browser/devtools/devtools_window.h" 12#include "chrome/browser/extensions/api/tabs/tabs_constants.h" 13#include "chrome/browser/extensions/api/tabs/tabs_windows_api.h" 14#include "chrome/browser/extensions/api/tabs/windows_event_router.h" 15#include "chrome/browser/extensions/extension_service.h" 16#include "chrome/browser/extensions/extension_system.h" 17#include "chrome/browser/extensions/extension_tab_util.h" 18#include "chrome/browser/extensions/image_loader.h" 19#include "chrome/browser/extensions/window_controller.h" 20#include "chrome/browser/extensions/window_controller_list.h" 21#include "chrome/browser/lifetime/application_lifetime.h" 22#include "chrome/browser/profiles/profile.h" 23#include "chrome/browser/themes/theme_service.h" 24#include "chrome/browser/themes/theme_service_factory.h" 25#include "chrome/browser/ui/panels/native_panel.h" 26#include "chrome/browser/ui/panels/panel_collection.h" 27#include "chrome/browser/ui/panels/panel_host.h" 28#include "chrome/browser/ui/panels/panel_manager.h" 29#include "chrome/browser/ui/panels/stacked_panel_collection.h" 30#include "chrome/browser/web_applications/web_app.h" 31#include "chrome/common/chrome_notification_types.h" 32#include "chrome/common/extensions/extension.h" 33#include "chrome/common/extensions/manifest_handlers/icons_handler.h" 34#include "content/public/browser/notification_service.h" 35#include "content/public/browser/notification_source.h" 36#include "content/public/browser/notification_types.h" 37#include "content/public/browser/render_view_host.h" 38#include "content/public/browser/user_metrics.h" 39#include "content/public/browser/web_contents.h" 40#include "ui/gfx/image/image.h" 41#include "ui/gfx/rect.h" 42 43using content::RenderViewHost; 44using content::UserMetricsAction; 45 46namespace panel_internal { 47 48class PanelExtensionWindowController : public extensions::WindowController { 49 public: 50 PanelExtensionWindowController(Panel* panel, Profile* profile); 51 virtual ~PanelExtensionWindowController(); 52 53 // Overridden from extensions::WindowController. 54 virtual int GetWindowId() const OVERRIDE; 55 virtual std::string GetWindowTypeText() const OVERRIDE; 56 virtual base::DictionaryValue* CreateWindowValueWithTabs( 57 const extensions::Extension* extension) const OVERRIDE; 58 virtual base::DictionaryValue* CreateTabValue( 59 const extensions::Extension* extension, int tab_index) const OVERRIDE; 60 virtual bool CanClose(Reason* reason) const OVERRIDE; 61 virtual void SetFullscreenMode(bool is_fullscreen, 62 const GURL& extension_url) const OVERRIDE; 63 virtual bool IsVisibleToExtension( 64 const extensions::Extension* extension) const OVERRIDE; 65 66 private: 67 Panel* panel_; // Weak pointer. Owns us. 68 DISALLOW_COPY_AND_ASSIGN(PanelExtensionWindowController); 69}; 70 71PanelExtensionWindowController::PanelExtensionWindowController( 72 Panel* panel, Profile* profile) 73 : extensions::WindowController(panel, profile), 74 panel_(panel) { 75 extensions::WindowControllerList::GetInstance()->AddExtensionWindow(this); 76} 77 78PanelExtensionWindowController::~PanelExtensionWindowController() { 79 extensions::WindowControllerList::GetInstance()->RemoveExtensionWindow(this); 80} 81 82int PanelExtensionWindowController::GetWindowId() const { 83 return static_cast<int>(panel_->session_id().id()); 84} 85 86std::string PanelExtensionWindowController::GetWindowTypeText() const { 87 return extensions::tabs_constants::kWindowTypeValuePanel; 88} 89 90base::DictionaryValue* 91PanelExtensionWindowController::CreateWindowValueWithTabs( 92 const extensions::Extension* extension) const { 93 base::DictionaryValue* result = CreateWindowValue(); 94 95 DCHECK(IsVisibleToExtension(extension)); 96 DictionaryValue* tab_value = CreateTabValue(extension, 0); 97 if (tab_value) { 98 base::ListValue* tab_list = new ListValue(); 99 tab_list->Append(tab_value); 100 result->Set(extensions::tabs_constants::kTabsKey, tab_list); 101 } 102 return result; 103} 104 105base::DictionaryValue* PanelExtensionWindowController::CreateTabValue( 106 const extensions::Extension* extension, int tab_index) const { 107 if (tab_index > 0) 108 return NULL; 109 110 content::WebContents* web_contents = panel_->GetWebContents(); 111 if (!web_contents) 112 return NULL; 113 114 DCHECK(IsVisibleToExtension(extension)); 115 DictionaryValue* tab_value = new DictionaryValue(); 116 tab_value->SetInteger(extensions::tabs_constants::kIdKey, 117 SessionID::IdForTab(web_contents)); 118 tab_value->SetInteger(extensions::tabs_constants::kIndexKey, 0); 119 tab_value->SetInteger(extensions::tabs_constants::kWindowIdKey, 120 SessionID::IdForWindowContainingTab(web_contents)); 121 tab_value->SetString( 122 extensions::tabs_constants::kUrlKey, web_contents->GetURL().spec()); 123 tab_value->SetString(extensions::tabs_constants::kStatusKey, 124 ExtensionTabUtil::GetTabStatusText( 125 web_contents->IsLoading())); 126 tab_value->SetBoolean( 127 extensions::tabs_constants::kActiveKey, panel_->IsActive()); 128 tab_value->SetBoolean(extensions::tabs_constants::kSelectedKey, true); 129 tab_value->SetBoolean(extensions::tabs_constants::kHighlightedKey, true); 130 tab_value->SetBoolean(extensions::tabs_constants::kPinnedKey, false); 131 tab_value->SetString( 132 extensions::tabs_constants::kTitleKey, web_contents->GetTitle()); 133 tab_value->SetBoolean( 134 extensions::tabs_constants::kIncognitoKey, 135 web_contents->GetBrowserContext()->IsOffTheRecord()); 136 return tab_value; 137} 138 139bool PanelExtensionWindowController::CanClose(Reason* reason) const { 140 return true; 141} 142 143void PanelExtensionWindowController::SetFullscreenMode( 144 bool is_fullscreen, const GURL& extension_url) const { 145 // Do nothing. Panels cannot be fullscreen. 146} 147 148bool PanelExtensionWindowController::IsVisibleToExtension( 149 const extensions::Extension* extension) const { 150 return extension->id() == panel_->extension_id(); 151} 152 153} // namespace internal 154 155Panel::~Panel() { 156 DCHECK(!collection_); 157 // Invoked by native panel destructor. Do not access native_panel_ here. 158 chrome::EndKeepAlive(); // Remove shutdown prevention. 159} 160 161PanelManager* Panel::manager() const { 162 return PanelManager::GetInstance(); 163} 164 165const std::string Panel::extension_id() const { 166 return web_app::GetExtensionIdFromApplicationName(app_name_); 167} 168 169CommandUpdater* Panel::command_updater() { 170 return &command_updater_; 171} 172 173Profile* Panel::profile() const { 174 return profile_; 175} 176 177const extensions::Extension* Panel::GetExtension() const { 178 ExtensionService* extension_service = 179 extensions::ExtensionSystem::Get(profile())->extension_service(); 180 if (!extension_service || !extension_service->is_ready()) 181 return NULL; 182 return extension_service->GetExtensionById(extension_id(), false); 183} 184 185content::WebContents* Panel::GetWebContents() const { 186 return panel_host_.get() ? panel_host_->web_contents() : NULL; 187} 188 189void Panel::SetExpansionState(ExpansionState new_state) { 190 if (expansion_state_ == new_state) 191 return; 192 native_panel_->PanelExpansionStateChanging(expansion_state_, new_state); 193 expansion_state_ = new_state; 194 195 manager()->OnPanelExpansionStateChanged(this); 196 197 DCHECK(initialized_ && collection_ != NULL); 198 native_panel_->PreventActivationByOS(collection_->IsPanelMinimized(this)); 199 UpdateMinimizeRestoreButtonVisibility(); 200 201 content::NotificationService::current()->Notify( 202 chrome::NOTIFICATION_PANEL_CHANGED_EXPANSION_STATE, 203 content::Source<Panel>(this), 204 content::NotificationService::NoDetails()); 205} 206 207bool Panel::IsDrawingAttention() const { 208 return native_panel_->IsDrawingAttention(); 209} 210 211void Panel::FullScreenModeChanged(bool is_full_screen) { 212 native_panel_->FullScreenModeChanged(is_full_screen); 213} 214 215int Panel::TitleOnlyHeight() const { 216 return native_panel_->TitleOnlyHeight(); 217} 218 219bool Panel::CanShowMinimizeButton() const { 220 return collection_ && collection_->CanShowMinimizeButton(this); 221} 222 223bool Panel::CanShowRestoreButton() const { 224 return collection_ && collection_->CanShowRestoreButton(this); 225} 226 227bool Panel::IsActive() const { 228 return native_panel_->IsPanelActive(); 229} 230 231bool Panel::IsMaximized() const { 232 // Size of panels is managed by PanelManager, they are never 'zoomed'. 233 return false; 234} 235 236bool Panel::IsMinimized() const { 237 return !collection_ || collection_->IsPanelMinimized(this); 238} 239 240bool Panel::IsFullscreen() const { 241 return false; 242} 243 244gfx::NativeWindow Panel::GetNativeWindow() { 245 return native_panel_->GetNativePanelWindow(); 246} 247 248gfx::Rect Panel::GetRestoredBounds() const { 249 gfx::Rect bounds = native_panel_->GetPanelBounds(); 250 bounds.set_y(bounds.bottom() - full_size_.height()); 251 bounds.set_x(bounds.right() - full_size_.width()); 252 bounds.set_size(full_size_); 253 return bounds; 254} 255 256ui::WindowShowState Panel::GetRestoredState() const { 257 return ui::SHOW_STATE_NORMAL; 258} 259 260gfx::Rect Panel::GetBounds() const { 261 return native_panel_->GetPanelBounds(); 262} 263 264void Panel::Show() { 265 if (manager()->display_settings_provider()->is_full_screen() || !collection_) 266 return; 267 268 native_panel_->ShowPanel(); 269} 270 271void Panel::Hide() { 272 // Not implemented. 273} 274 275void Panel::ShowInactive() { 276 if (manager()->display_settings_provider()->is_full_screen() || !collection_) 277 return; 278 279 native_panel_->ShowPanelInactive(); 280} 281 282// Close() may be called multiple times if the panel window is not ready to 283// close on the first attempt. 284void Panel::Close() { 285 native_panel_->ClosePanel(); 286} 287 288void Panel::Activate() { 289 if (!collection_) 290 return; 291 292 collection_->ActivatePanel(this); 293 native_panel_->ActivatePanel(); 294} 295 296void Panel::Deactivate() { 297 native_panel_->DeactivatePanel(); 298} 299 300void Panel::Maximize() { 301 Restore(); 302} 303 304void Panel::Minimize() { 305 if (collection_) 306 collection_->MinimizePanel(this); 307} 308 309bool Panel::IsMinimizedBySystem() const { 310 return native_panel_->IsPanelMinimizedBySystem(); 311} 312 313void Panel::ShowShadow(bool show) { 314 native_panel_->ShowShadow(show); 315} 316 317void Panel::Restore() { 318 if (collection_) 319 collection_->RestorePanel(this); 320} 321 322void Panel::SetBounds(const gfx::Rect& bounds) { 323 // Ignore bounds position as the panel manager controls all positioning. 324 if (!collection_) 325 return; 326 collection_->ResizePanelWindow(this, bounds.size()); 327 SetAutoResizable(false); 328} 329 330void Panel::FlashFrame(bool draw_attention) { 331 if (IsDrawingAttention() == draw_attention || !collection_) 332 return; 333 334 // Don't draw attention for an active panel. 335 if (draw_attention && IsActive()) 336 return; 337 338 // Invoking native panel to draw attention must be done before informing the 339 // panel collection because it needs to check internal state of the panel to 340 // determine if the panel has been drawing attention. 341 native_panel_->DrawAttention(draw_attention); 342 collection_->OnPanelAttentionStateChanged(this); 343} 344 345bool Panel::IsAlwaysOnTop() const { 346 return native_panel_->IsPanelAlwaysOnTop(); 347} 348 349void Panel::ExecuteCommandWithDisposition(int id, 350 WindowOpenDisposition disposition) { 351 DCHECK(command_updater_.IsCommandEnabled(id)) << "Invalid/disabled command " 352 << id; 353 354 if (!GetWebContents()) 355 return; 356 357 switch (id) { 358 // Navigation 359 case IDC_RELOAD: 360 panel_host_->Reload(); 361 break; 362 case IDC_RELOAD_IGNORING_CACHE: 363 panel_host_->ReloadIgnoringCache(); 364 break; 365 case IDC_STOP: 366 panel_host_->StopLoading(); 367 break; 368 369 // Window management 370 case IDC_CLOSE_WINDOW: 371 content::RecordAction(UserMetricsAction("CloseWindow")); 372 Close(); 373 break; 374 case IDC_EXIT: 375 content::RecordAction(UserMetricsAction("Exit")); 376 chrome::AttemptUserExit(); 377 break; 378 379 // Clipboard 380 case IDC_COPY: 381 content::RecordAction(UserMetricsAction("Copy")); 382 native_panel_->PanelCopy(); 383 break; 384 case IDC_CUT: 385 content::RecordAction(UserMetricsAction("Cut")); 386 native_panel_->PanelCut(); 387 break; 388 case IDC_PASTE: 389 content::RecordAction(UserMetricsAction("Paste")); 390 native_panel_->PanelPaste(); 391 break; 392 393 // Zoom 394 case IDC_ZOOM_PLUS: 395 panel_host_->Zoom(content::PAGE_ZOOM_IN); 396 break; 397 case IDC_ZOOM_NORMAL: 398 panel_host_->Zoom(content::PAGE_ZOOM_RESET); 399 break; 400 case IDC_ZOOM_MINUS: 401 panel_host_->Zoom(content::PAGE_ZOOM_OUT); 402 break; 403 404 // DevTools 405 case IDC_DEV_TOOLS: 406 content::RecordAction(UserMetricsAction("DevTools_ToggleWindow")); 407 DevToolsWindow::ToggleDevToolsWindow( 408 GetWebContents()->GetRenderViewHost(), 409 true, 410 DEVTOOLS_TOGGLE_ACTION_SHOW); 411 break; 412 case IDC_DEV_TOOLS_CONSOLE: 413 content::RecordAction(UserMetricsAction("DevTools_ToggleConsole")); 414 DevToolsWindow::ToggleDevToolsWindow( 415 GetWebContents()->GetRenderViewHost(), 416 true, 417 DEVTOOLS_TOGGLE_ACTION_SHOW_CONSOLE); 418 break; 419 420 default: 421 LOG(WARNING) << "Received unimplemented command: " << id; 422 break; 423 } 424} 425 426void Panel::Observe(int type, 427 const content::NotificationSource& source, 428 const content::NotificationDetails& details) { 429 switch (type) { 430 case content::NOTIFICATION_WEB_CONTENTS_SWAPPED: 431 ConfigureAutoResize(content::Source<content::WebContents>(source).ptr()); 432 break; 433 case chrome::NOTIFICATION_EXTENSION_UNLOADED: 434 if (content::Details<extensions::UnloadedExtensionInfo>( 435 details)->extension->id() == extension_id()) 436 Close(); 437 break; 438 case chrome::NOTIFICATION_APP_TERMINATING: 439 Close(); 440 break; 441 default: 442 NOTREACHED() << "Received unexpected notification " << type; 443 } 444} 445 446void Panel::OnTitlebarClicked(panel::ClickModifier modifier) { 447 if (collection_) 448 collection_->OnPanelTitlebarClicked(this, modifier); 449 450 // Normally the system activates a window when the titlebar is clicked. 451 // However, we prevent system activation of minimized panels, thus the 452 // activation may not have occurred. Also, some OSes (Windows) will 453 // activate a minimized panel on mouse-down regardless of our attempts to 454 // prevent system activation. Attention state is not cleared in that case. 455 // See Panel::OnActiveStateChanged(). 456 // Therefore, we ensure activation and clearing of attention state if the 457 // panel has been expanded. If the panel is in a stack, the titlebar click 458 // might minimize the panel and we do not want to activate it to make it 459 // expand again. 460 // These are no-ops if no changes are needed. 461 if (IsMinimized()) 462 return; 463 Activate(); 464 FlashFrame(false); 465} 466 467void Panel::OnMinimizeButtonClicked(panel::ClickModifier modifier) { 468 if (collection_) 469 collection_->OnMinimizeButtonClicked(this, modifier); 470} 471 472void Panel::OnRestoreButtonClicked(panel::ClickModifier modifier) { 473 // Clicking the restore button has the same behavior as clicking the titlebar. 474 OnTitlebarClicked(modifier); 475} 476 477void Panel::OnWindowSizeAvailable() { 478 ConfigureAutoResize(GetWebContents()); 479} 480 481void Panel::OnNativePanelClosed() { 482 // Ensure previously enqueued OnImageLoaded callbacks are ignored. 483 image_loader_ptr_factory_.InvalidateWeakPtrs(); 484 registrar_.RemoveAll(); 485 manager()->OnPanelClosed(this); 486 DCHECK(!collection_); 487} 488 489StackedPanelCollection* Panel::stack() const { 490 return collection_ && collection_->type() == PanelCollection::STACKED ? 491 static_cast<StackedPanelCollection*>(collection_) : NULL; 492} 493 494panel::Resizability Panel::CanResizeByMouse() const { 495 if (!collection_) 496 return panel::NOT_RESIZABLE; 497 498 return collection_->GetPanelResizability(this); 499} 500 501void Panel::Initialize(const GURL& url, 502 const gfx::Rect& bounds, 503 bool always_on_top) { 504 DCHECK(!initialized_); 505 DCHECK(!collection_); // Cannot be added to a collection until fully created. 506 DCHECK_EQ(EXPANDED, expansion_state_); 507 DCHECK(!bounds.IsEmpty()); 508 initialized_ = true; 509 full_size_ = bounds.size(); 510 native_panel_ = CreateNativePanel(this, bounds, always_on_top); 511 512 extension_window_controller_.reset( 513 new panel_internal::PanelExtensionWindowController(this, profile_)); 514 515 InitCommandState(); 516 517 // Set up hosting for web contents. 518 panel_host_.reset(new PanelHost(this, profile_)); 519 panel_host_->Init(url); 520 content::WebContents* web_contents = GetWebContents(); 521 // The contents might be NULL for most of our tests. 522 if (web_contents) 523 native_panel_->AttachWebContents(web_contents); 524 525 // Close when the extension is unloaded or the browser is exiting. 526 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, 527 content::Source<Profile>(profile_)); 528 registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING, 529 content::NotificationService::AllSources()); 530 registrar_.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED, 531 content::Source<ThemeService>( 532 ThemeServiceFactory::GetForProfile(profile_))); 533 534 // Prevent the browser process from shutting down while this window is open. 535 chrome::StartKeepAlive(); 536 537 UpdateAppIcon(); 538} 539 540void Panel::SetPanelBounds(const gfx::Rect& bounds) { 541 if (bounds != native_panel_->GetPanelBounds()) 542 native_panel_->SetPanelBounds(bounds); 543} 544 545void Panel::SetPanelBoundsInstantly(const gfx::Rect& bounds) { 546 native_panel_->SetPanelBoundsInstantly(bounds); 547} 548 549void Panel::LimitSizeToWorkArea(const gfx::Rect& work_area) { 550 int max_width = manager()->GetMaxPanelWidth(work_area); 551 int max_height = manager()->GetMaxPanelHeight(work_area); 552 553 // If the custom max size is used, ensure that it does not exceed the display 554 // area. 555 if (max_size_policy_ == CUSTOM_MAX_SIZE) { 556 int current_max_width = max_size_.width(); 557 if (current_max_width > max_width) 558 max_width = std::min(current_max_width, work_area.width()); 559 int current_max_height = max_size_.height(); 560 if (current_max_height > max_height) 561 max_height = std::min(current_max_height, work_area.height()); 562 } 563 564 SetSizeRange(min_size_, gfx::Size(max_width, max_height)); 565 566 // Ensure that full size does not exceed max size. 567 full_size_ = ClampSize(full_size_); 568} 569 570void Panel::SetAutoResizable(bool resizable) { 571 if (auto_resizable_ == resizable) 572 return; 573 574 auto_resizable_ = resizable; 575 content::WebContents* web_contents = GetWebContents(); 576 if (auto_resizable_) { 577 if (web_contents) 578 EnableWebContentsAutoResize(web_contents); 579 } else { 580 if (web_contents) { 581 registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_SWAPPED, 582 content::Source<content::WebContents>(web_contents)); 583 584 // NULL might be returned if the tab has not been added. 585 RenderViewHost* render_view_host = web_contents->GetRenderViewHost(); 586 if (render_view_host) 587 render_view_host->DisableAutoResize(full_size_); 588 } 589 } 590} 591 592void Panel::EnableWebContentsAutoResize(content::WebContents* web_contents) { 593 DCHECK(web_contents); 594 ConfigureAutoResize(web_contents); 595 596 // We also need to know when the render view host changes in order 597 // to turn on auto-resize notifications in the new render view host. 598 if (!registrar_.IsRegistered( 599 this, content::NOTIFICATION_WEB_CONTENTS_SWAPPED, 600 content::Source<content::WebContents>(web_contents))) { 601 registrar_.Add( 602 this, 603 content::NOTIFICATION_WEB_CONTENTS_SWAPPED, 604 content::Source<content::WebContents>(web_contents)); 605 } 606} 607 608void Panel::OnContentsAutoResized(const gfx::Size& new_content_size) { 609 DCHECK(auto_resizable_); 610 if (!collection_) 611 return; 612 613 gfx::Size new_window_size = 614 native_panel_->WindowSizeFromContentSize(new_content_size); 615 616 // Ignore content auto resizes until window frame size is known. 617 // This reduces extra resizes when panel is first shown. 618 // After window frame size is known, it will trigger another content 619 // auto resize. 620 if (new_content_size == new_window_size) 621 return; 622 623 collection_->ResizePanelWindow(this, new_window_size); 624} 625 626void Panel::OnWindowResizedByMouse(const gfx::Rect& new_bounds) { 627 if (collection_) 628 collection_->OnPanelResizedByMouse(this, new_bounds); 629} 630 631void Panel::SetSizeRange(const gfx::Size& min_size, const gfx::Size& max_size) { 632 if (min_size == min_size_ && max_size == max_size_) 633 return; 634 635 DCHECK(min_size.width() <= max_size.width()); 636 DCHECK(min_size.height() <= max_size.height()); 637 min_size_ = min_size; 638 max_size_ = max_size; 639 640 ConfigureAutoResize(GetWebContents()); 641} 642 643void Panel::IncreaseMaxSize(const gfx::Size& desired_panel_size) { 644 gfx::Size new_max_size = max_size_; 645 if (new_max_size.width() < desired_panel_size.width()) 646 new_max_size.set_width(desired_panel_size.width()); 647 if (new_max_size.height() < desired_panel_size.height()) 648 new_max_size.set_height(desired_panel_size.height()); 649 650 SetSizeRange(min_size_, new_max_size); 651} 652 653void Panel::HandleKeyboardEvent(const content::NativeWebKeyboardEvent& event) { 654 native_panel_->HandlePanelKeyboardEvent(event); 655} 656 657void Panel::SetAlwaysOnTop(bool on_top) { 658 native_panel_->SetPanelAlwaysOnTop(on_top); 659} 660 661void Panel::SetPreviewMode(bool in_preview) { 662 DCHECK_NE(in_preview_mode_, in_preview); 663 in_preview_mode_ = in_preview; 664} 665 666void Panel::EnableResizeByMouse(bool enable) { 667 DCHECK(native_panel_); 668 native_panel_->EnableResizeByMouse(enable); 669} 670 671void Panel::UpdateMinimizeRestoreButtonVisibility() { 672 native_panel_->UpdatePanelMinimizeRestoreButtonVisibility(); 673} 674 675gfx::Size Panel::ClampSize(const gfx::Size& size) const { 676 // The panel width: 677 // * cannot grow or shrink to go beyond [min_width, max_width] 678 int new_width = size.width(); 679 if (new_width > max_size_.width()) 680 new_width = max_size_.width(); 681 if (new_width < min_size_.width()) 682 new_width = min_size_.width(); 683 684 // The panel height: 685 // * cannot grow or shrink to go beyond [min_height, max_height] 686 int new_height = size.height(); 687 if (new_height > max_size_.height()) 688 new_height = max_size_.height(); 689 if (new_height < min_size_.height()) 690 new_height = min_size_.height(); 691 692 return gfx::Size(new_width, new_height); 693} 694 695void Panel::OnActiveStateChanged(bool active) { 696 // Clear attention state when an expanded panel becomes active. 697 // On some systems (e.g. Win), mouse-down activates a panel regardless of 698 // its expansion state. However, we don't want to clear draw attention if 699 // contents are not visible. In that scenario, if the mouse-down results 700 // in a mouse-click, draw attention will be cleared then. 701 // See Panel::OnTitlebarClicked(). 702 if (active && IsDrawingAttention() && !IsMinimized()) 703 FlashFrame(false); 704 705 if (collection_) 706 collection_->OnPanelActiveStateChanged(this); 707 708 // Send extension event about window changing active state. 709 extensions::TabsWindowsAPI* tabs_windows_api = 710 extensions::TabsWindowsAPI::Get(profile()); 711 if (tabs_windows_api) { 712 tabs_windows_api->windows_event_router()->OnActiveWindowChanged( 713 active ? extension_window_controller_.get() : NULL); 714 } 715 716 content::NotificationService::current()->Notify( 717 chrome::NOTIFICATION_PANEL_CHANGED_ACTIVE_STATUS, 718 content::Source<Panel>(this), 719 content::NotificationService::NoDetails()); 720} 721 722void Panel::OnPanelStartUserResizing() { 723 SetAutoResizable(false); 724 SetPreviewMode(true); 725 max_size_policy_ = CUSTOM_MAX_SIZE; 726} 727 728void Panel::OnPanelEndUserResizing() { 729 SetPreviewMode(false); 730} 731 732bool Panel::ShouldCloseWindow() { 733 return true; 734} 735 736void Panel::OnWindowClosing() { 737 if (GetWebContents()) { 738 native_panel_->DetachWebContents(GetWebContents()); 739 panel_host_->DestroyWebContents(); 740 } 741} 742 743bool Panel::ExecuteCommandIfEnabled(int id) { 744 if (command_updater()->SupportsCommand(id) && 745 command_updater()->IsCommandEnabled(id)) { 746 ExecuteCommandWithDisposition(id, CURRENT_TAB); 747 return true; 748 } 749 return false; 750} 751 752string16 Panel::GetWindowTitle() const { 753 content::WebContents* contents = GetWebContents(); 754 string16 title; 755 756 // |contents| can be NULL during the window's creation. 757 if (contents) { 758 title = contents->GetTitle(); 759 FormatTitleForDisplay(&title); 760 } 761 762 if (title.empty()) 763 title = UTF8ToUTF16(app_name()); 764 765 return title; 766} 767 768gfx::Image Panel::GetCurrentPageIcon() const { 769 return panel_host_->GetPageIcon(); 770} 771 772void Panel::UpdateTitleBar() { 773 native_panel_->UpdatePanelTitleBar(); 774} 775 776void Panel::LoadingStateChanged(bool is_loading) { 777 command_updater_.UpdateCommandEnabled(IDC_STOP, is_loading); 778 native_panel_->UpdatePanelLoadingAnimations(is_loading); 779 UpdateTitleBar(); 780} 781 782void Panel::WebContentsFocused(content::WebContents* contents) { 783 native_panel_->PanelWebContentsFocused(contents); 784} 785 786void Panel::MoveByInstantly(const gfx::Vector2d& delta_origin) { 787 gfx::Rect bounds = GetBounds(); 788 bounds.Offset(delta_origin); 789 SetPanelBoundsInstantly(bounds); 790} 791 792void Panel::SetWindowCornerStyle(panel::CornerStyle corner_style) { 793 native_panel_->SetWindowCornerStyle(corner_style); 794} 795 796void Panel::MinimizeBySystem() { 797 native_panel_->MinimizePanelBySystem(); 798} 799 800Panel::Panel(Profile* profile, const std::string& app_name, 801 const gfx::Size& min_size, const gfx::Size& max_size) 802 : app_name_(app_name), 803 profile_(profile), 804 collection_(NULL), 805 initialized_(false), 806 min_size_(min_size), 807 max_size_(max_size), 808 max_size_policy_(DEFAULT_MAX_SIZE), 809 auto_resizable_(false), 810 in_preview_mode_(false), 811 native_panel_(NULL), 812 attention_mode_(USE_PANEL_ATTENTION), 813 expansion_state_(EXPANDED), 814 command_updater_(this), 815 image_loader_ptr_factory_(this) { 816} 817 818void Panel::OnImageLoaded(const gfx::Image& image) { 819 if (!image.IsEmpty()) { 820 app_icon_ = image; 821 native_panel_->UpdatePanelTitleBar(); 822 } 823 824 content::NotificationService::current()->Notify( 825 chrome::NOTIFICATION_PANEL_APP_ICON_LOADED, 826 content::Source<Panel>(this), 827 content::NotificationService::NoDetails()); 828} 829 830void Panel::InitCommandState() { 831 // All supported commands whose state isn't set automagically some other way 832 // (like Stop during a page load) must have their state initialized here, 833 // otherwise they will be forever disabled. 834 835 // Navigation commands 836 command_updater_.UpdateCommandEnabled(IDC_RELOAD, true); 837 command_updater_.UpdateCommandEnabled(IDC_RELOAD_IGNORING_CACHE, true); 838 839 // Window management commands 840 command_updater_.UpdateCommandEnabled(IDC_CLOSE_WINDOW, true); 841 command_updater_.UpdateCommandEnabled(IDC_EXIT, true); 842 843 // Zoom 844 command_updater_.UpdateCommandEnabled(IDC_ZOOM_MENU, true); 845 command_updater_.UpdateCommandEnabled(IDC_ZOOM_PLUS, true); 846 command_updater_.UpdateCommandEnabled(IDC_ZOOM_NORMAL, true); 847 command_updater_.UpdateCommandEnabled(IDC_ZOOM_MINUS, true); 848 849 // Clipboard 850 command_updater_.UpdateCommandEnabled(IDC_COPY, true); 851 command_updater_.UpdateCommandEnabled(IDC_CUT, true); 852 command_updater_.UpdateCommandEnabled(IDC_PASTE, true); 853 854 // DevTools 855 command_updater_.UpdateCommandEnabled(IDC_DEV_TOOLS, true); 856 command_updater_.UpdateCommandEnabled(IDC_DEV_TOOLS_CONSOLE, true); 857} 858 859void Panel::ConfigureAutoResize(content::WebContents* web_contents) { 860 if (!auto_resizable_ || !web_contents) 861 return; 862 863 // NULL might be returned if the tab has not been added. 864 RenderViewHost* render_view_host = web_contents->GetRenderViewHost(); 865 if (!render_view_host) 866 return; 867 868 render_view_host->EnableAutoResize( 869 min_size_, 870 native_panel_->ContentSizeFromWindowSize(max_size_)); 871} 872 873void Panel::UpdateAppIcon() { 874 const extensions::Extension* extension = GetExtension(); 875 if (!extension) 876 return; 877 878 extensions::ImageLoader* loader = extensions::ImageLoader::Get(profile()); 879 loader->LoadImageAsync( 880 extension, 881 extensions::IconsInfo::GetIconResource( 882 extension, 883 extension_misc::EXTENSION_ICON_SMALL, 884 ExtensionIconSet::MATCH_BIGGER), 885 gfx::Size(extension_misc::EXTENSION_ICON_SMALL, 886 extension_misc::EXTENSION_ICON_SMALL), 887 base::Bind(&Panel::OnImageLoaded, 888 image_loader_ptr_factory_.GetWeakPtr())); 889} 890 891// static 892void Panel::FormatTitleForDisplay(string16* title) { 893 size_t current_index = 0; 894 size_t match_index; 895 while ((match_index = title->find(L'\n', current_index)) != string16::npos) { 896 title->replace(match_index, 1, string16()); 897 current_index = match_index; 898 } 899} 900