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