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