// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/ui/panels/panel.h" #include "base/logging.h" #include "base/message_loop/message_loop.h" #include "base/strings/utf_string_conversions.h" #include "chrome/app/chrome_command_ids.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/devtools/devtools_window.h" #include "chrome/browser/extensions/api/tabs/tabs_constants.h" #include "chrome/browser/extensions/api/tabs/tabs_windows_api.h" #include "chrome/browser/extensions/api/tabs/windows_event_router.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/extensions/extension_tab_util.h" #include "chrome/browser/extensions/image_loader.h" #include "chrome/browser/extensions/window_controller.h" #include "chrome/browser/extensions/window_controller_list.h" #include "chrome/browser/lifetime/application_lifetime.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/themes/theme_service.h" #include "chrome/browser/themes/theme_service_factory.h" #include "chrome/browser/ui/panels/native_panel.h" #include "chrome/browser/ui/panels/panel_collection.h" #include "chrome/browser/ui/panels/panel_host.h" #include "chrome/browser/ui/panels/panel_manager.h" #include "chrome/browser/ui/panels/stacked_panel_collection.h" #include "chrome/browser/web_applications/web_app.h" #include "chrome/common/extensions/manifest_handlers/icons_handler.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_source.h" #include "content/public/browser/notification_types.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/user_metrics.h" #include "content/public/browser/web_contents.h" #include "extensions/common/extension.h" #include "ui/gfx/image/image.h" #include "ui/gfx/rect.h" using content::RenderViewHost; using content::UserMetricsAction; namespace panel_internal { class PanelExtensionWindowController : public extensions::WindowController { public: PanelExtensionWindowController(Panel* panel, Profile* profile); virtual ~PanelExtensionWindowController(); // Overridden from extensions::WindowController. virtual int GetWindowId() const OVERRIDE; virtual std::string GetWindowTypeText() const OVERRIDE; virtual base::DictionaryValue* CreateWindowValueWithTabs( const extensions::Extension* extension) const OVERRIDE; virtual base::DictionaryValue* CreateTabValue( const extensions::Extension* extension, int tab_index) const OVERRIDE; virtual bool CanClose(Reason* reason) const OVERRIDE; virtual void SetFullscreenMode(bool is_fullscreen, const GURL& extension_url) const OVERRIDE; virtual bool IsVisibleToExtension( const extensions::Extension* extension) const OVERRIDE; private: Panel* panel_; // Weak pointer. Owns us. DISALLOW_COPY_AND_ASSIGN(PanelExtensionWindowController); }; PanelExtensionWindowController::PanelExtensionWindowController( Panel* panel, Profile* profile) : extensions::WindowController(panel, profile), panel_(panel) { extensions::WindowControllerList::GetInstance()->AddExtensionWindow(this); } PanelExtensionWindowController::~PanelExtensionWindowController() { extensions::WindowControllerList::GetInstance()->RemoveExtensionWindow(this); } int PanelExtensionWindowController::GetWindowId() const { return static_cast(panel_->session_id().id()); } std::string PanelExtensionWindowController::GetWindowTypeText() const { return extensions::tabs_constants::kWindowTypeValuePanel; } base::DictionaryValue* PanelExtensionWindowController::CreateWindowValueWithTabs( const extensions::Extension* extension) const { base::DictionaryValue* result = CreateWindowValue(); DCHECK(IsVisibleToExtension(extension)); DictionaryValue* tab_value = CreateTabValue(extension, 0); if (tab_value) { base::ListValue* tab_list = new ListValue(); tab_list->Append(tab_value); result->Set(extensions::tabs_constants::kTabsKey, tab_list); } return result; } base::DictionaryValue* PanelExtensionWindowController::CreateTabValue( const extensions::Extension* extension, int tab_index) const { if (tab_index > 0) return NULL; content::WebContents* web_contents = panel_->GetWebContents(); if (!web_contents) return NULL; DCHECK(IsVisibleToExtension(extension)); DictionaryValue* tab_value = new DictionaryValue(); tab_value->SetInteger(extensions::tabs_constants::kIdKey, SessionID::IdForTab(web_contents)); tab_value->SetInteger(extensions::tabs_constants::kIndexKey, 0); tab_value->SetInteger(extensions::tabs_constants::kWindowIdKey, SessionID::IdForWindowContainingTab(web_contents)); tab_value->SetString( extensions::tabs_constants::kUrlKey, web_contents->GetURL().spec()); tab_value->SetString(extensions::tabs_constants::kStatusKey, extensions::ExtensionTabUtil::GetTabStatusText( web_contents->IsLoading())); tab_value->SetBoolean( extensions::tabs_constants::kActiveKey, panel_->IsActive()); tab_value->SetBoolean(extensions::tabs_constants::kSelectedKey, true); tab_value->SetBoolean(extensions::tabs_constants::kHighlightedKey, true); tab_value->SetBoolean(extensions::tabs_constants::kPinnedKey, false); tab_value->SetString( extensions::tabs_constants::kTitleKey, web_contents->GetTitle()); tab_value->SetBoolean( extensions::tabs_constants::kIncognitoKey, web_contents->GetBrowserContext()->IsOffTheRecord()); return tab_value; } bool PanelExtensionWindowController::CanClose(Reason* reason) const { return true; } void PanelExtensionWindowController::SetFullscreenMode( bool is_fullscreen, const GURL& extension_url) const { // Do nothing. Panels cannot be fullscreen. } bool PanelExtensionWindowController::IsVisibleToExtension( const extensions::Extension* extension) const { return extension->id() == panel_->extension_id(); } } // namespace panel_internal Panel::~Panel() { DCHECK(!collection_); // Invoked by native panel destructor. Do not access native_panel_ here. chrome::EndKeepAlive(); // Remove shutdown prevention. } PanelManager* Panel::manager() const { return PanelManager::GetInstance(); } const std::string Panel::extension_id() const { return web_app::GetExtensionIdFromApplicationName(app_name_); } CommandUpdater* Panel::command_updater() { return &command_updater_; } Profile* Panel::profile() const { return profile_; } const extensions::Extension* Panel::GetExtension() const { ExtensionService* extension_service = extensions::ExtensionSystem::Get(profile())->extension_service(); if (!extension_service || !extension_service->is_ready()) return NULL; return extension_service->GetExtensionById(extension_id(), false); } content::WebContents* Panel::GetWebContents() const { return panel_host_.get() ? panel_host_->web_contents() : NULL; } void Panel::SetExpansionState(ExpansionState new_state) { if (expansion_state_ == new_state) return; native_panel_->PanelExpansionStateChanging(expansion_state_, new_state); expansion_state_ = new_state; manager()->OnPanelExpansionStateChanged(this); DCHECK(initialized_ && collection_ != NULL); native_panel_->PreventActivationByOS(collection_->IsPanelMinimized(this)); UpdateMinimizeRestoreButtonVisibility(); content::NotificationService::current()->Notify( chrome::NOTIFICATION_PANEL_CHANGED_EXPANSION_STATE, content::Source(this), content::NotificationService::NoDetails()); } bool Panel::IsDrawingAttention() const { return native_panel_->IsDrawingAttention(); } void Panel::FullScreenModeChanged(bool is_full_screen) { native_panel_->FullScreenModeChanged(is_full_screen); } int Panel::TitleOnlyHeight() const { return native_panel_->TitleOnlyHeight(); } bool Panel::CanShowMinimizeButton() const { return collection_ && collection_->CanShowMinimizeButton(this); } bool Panel::CanShowRestoreButton() const { return collection_ && collection_->CanShowRestoreButton(this); } bool Panel::IsActive() const { return native_panel_->IsPanelActive(); } bool Panel::IsMaximized() const { // Size of panels is managed by PanelManager, they are never 'zoomed'. return false; } bool Panel::IsMinimized() const { return !collection_ || collection_->IsPanelMinimized(this); } bool Panel::IsFullscreen() const { return false; } gfx::NativeWindow Panel::GetNativeWindow() { return native_panel_->GetNativePanelWindow(); } gfx::Rect Panel::GetRestoredBounds() const { gfx::Rect bounds = native_panel_->GetPanelBounds(); bounds.set_y(bounds.bottom() - full_size_.height()); bounds.set_x(bounds.right() - full_size_.width()); bounds.set_size(full_size_); return bounds; } ui::WindowShowState Panel::GetRestoredState() const { return ui::SHOW_STATE_NORMAL; } gfx::Rect Panel::GetBounds() const { return native_panel_->GetPanelBounds(); } void Panel::Show() { if (manager()->display_settings_provider()->is_full_screen() || !collection_) return; native_panel_->ShowPanel(); } void Panel::Hide() { // Not implemented. } void Panel::ShowInactive() { if (manager()->display_settings_provider()->is_full_screen() || !collection_) return; native_panel_->ShowPanelInactive(); } // Close() may be called multiple times if the panel window is not ready to // close on the first attempt. void Panel::Close() { native_panel_->ClosePanel(); } void Panel::Activate() { if (!collection_) return; collection_->ActivatePanel(this); native_panel_->ActivatePanel(); } void Panel::Deactivate() { native_panel_->DeactivatePanel(); } void Panel::Maximize() { Restore(); } void Panel::Minimize() { if (collection_) collection_->MinimizePanel(this); } bool Panel::IsMinimizedBySystem() const { return native_panel_->IsPanelMinimizedBySystem(); } bool Panel::IsShownOnActiveDesktop() const { return native_panel_->IsPanelShownOnActiveDesktop(); } void Panel::ShowShadow(bool show) { native_panel_->ShowShadow(show); } void Panel::Restore() { if (collection_) collection_->RestorePanel(this); } void Panel::SetBounds(const gfx::Rect& bounds) { // Ignore bounds position as the panel manager controls all positioning. if (!collection_) return; collection_->ResizePanelWindow(this, bounds.size()); SetAutoResizable(false); } void Panel::FlashFrame(bool draw_attention) { if (IsDrawingAttention() == draw_attention || !collection_) return; // Don't draw attention for an active panel. if (draw_attention && IsActive()) return; // Invoking native panel to draw attention must be done before informing the // panel collection because it needs to check internal state of the panel to // determine if the panel has been drawing attention. native_panel_->DrawAttention(draw_attention); collection_->OnPanelAttentionStateChanged(this); } bool Panel::IsAlwaysOnTop() const { return native_panel_->IsPanelAlwaysOnTop(); } void Panel::SetAlwaysOnTop(bool on_top) { native_panel_->SetPanelAlwaysOnTop(on_top); } void Panel::ExecuteCommandWithDisposition(int id, WindowOpenDisposition disposition) { DCHECK(command_updater_.IsCommandEnabled(id)) << "Invalid/disabled command " << id; if (!GetWebContents()) return; switch (id) { // Navigation case IDC_RELOAD: panel_host_->Reload(); break; case IDC_RELOAD_IGNORING_CACHE: panel_host_->ReloadIgnoringCache(); break; case IDC_STOP: panel_host_->StopLoading(); break; // Window management case IDC_CLOSE_WINDOW: content::RecordAction(UserMetricsAction("CloseWindow")); Close(); break; case IDC_EXIT: content::RecordAction(UserMetricsAction("Exit")); chrome::AttemptUserExit(); break; // Clipboard case IDC_COPY: content::RecordAction(UserMetricsAction("Copy")); native_panel_->PanelCopy(); break; case IDC_CUT: content::RecordAction(UserMetricsAction("Cut")); native_panel_->PanelCut(); break; case IDC_PASTE: content::RecordAction(UserMetricsAction("Paste")); native_panel_->PanelPaste(); break; // Zoom case IDC_ZOOM_PLUS: panel_host_->Zoom(content::PAGE_ZOOM_IN); break; case IDC_ZOOM_NORMAL: panel_host_->Zoom(content::PAGE_ZOOM_RESET); break; case IDC_ZOOM_MINUS: panel_host_->Zoom(content::PAGE_ZOOM_OUT); break; // DevTools case IDC_DEV_TOOLS: content::RecordAction(UserMetricsAction("DevTools_ToggleWindow")); DevToolsWindow::ToggleDevToolsWindow( GetWebContents()->GetRenderViewHost(), true, DevToolsToggleAction::Show()); break; case IDC_DEV_TOOLS_CONSOLE: content::RecordAction(UserMetricsAction("DevTools_ToggleConsole")); DevToolsWindow::ToggleDevToolsWindow( GetWebContents()->GetRenderViewHost(), true, DevToolsToggleAction::ShowConsole()); break; default: LOG(WARNING) << "Received unimplemented command: " << id; break; } } void Panel::Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) { switch (type) { case content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED: ConfigureAutoResize(content::Source(source).ptr()); break; case chrome::NOTIFICATION_EXTENSION_UNLOADED: if (content::Details( details)->extension->id() == extension_id()) Close(); break; case chrome::NOTIFICATION_APP_TERMINATING: Close(); break; default: NOTREACHED() << "Received unexpected notification " << type; } } void Panel::OnTitlebarClicked(panel::ClickModifier modifier) { if (collection_) collection_->OnPanelTitlebarClicked(this, modifier); // Normally the system activates a window when the titlebar is clicked. // However, we prevent system activation of minimized panels, thus the // activation may not have occurred. Also, some OSes (Windows) will // activate a minimized panel on mouse-down regardless of our attempts to // prevent system activation. Attention state is not cleared in that case. // See Panel::OnActiveStateChanged(). // Therefore, we ensure activation and clearing of attention state if the // panel has been expanded. If the panel is in a stack, the titlebar click // might minimize the panel and we do not want to activate it to make it // expand again. // These are no-ops if no changes are needed. if (IsMinimized()) return; Activate(); FlashFrame(false); } void Panel::OnMinimizeButtonClicked(panel::ClickModifier modifier) { if (collection_) collection_->OnMinimizeButtonClicked(this, modifier); } void Panel::OnRestoreButtonClicked(panel::ClickModifier modifier) { // Clicking the restore button has the same behavior as clicking the titlebar. OnTitlebarClicked(modifier); } void Panel::OnWindowSizeAvailable() { ConfigureAutoResize(GetWebContents()); } void Panel::OnNativePanelClosed() { // Ensure previously enqueued OnImageLoaded callbacks are ignored. image_loader_ptr_factory_.InvalidateWeakPtrs(); registrar_.RemoveAll(); manager()->OnPanelClosed(this); DCHECK(!collection_); } StackedPanelCollection* Panel::stack() const { return collection_ && collection_->type() == PanelCollection::STACKED ? static_cast(collection_) : NULL; } panel::Resizability Panel::CanResizeByMouse() const { if (!collection_) return panel::NOT_RESIZABLE; return collection_->GetPanelResizability(this); } void Panel::Initialize(const GURL& url, const gfx::Rect& bounds, bool always_on_top) { DCHECK(!initialized_); DCHECK(!collection_); // Cannot be added to a collection until fully created. DCHECK_EQ(EXPANDED, expansion_state_); DCHECK(!bounds.IsEmpty()); initialized_ = true; full_size_ = bounds.size(); native_panel_ = CreateNativePanel(this, bounds, always_on_top); extension_window_controller_.reset( new panel_internal::PanelExtensionWindowController(this, profile_)); InitCommandState(); // Set up hosting for web contents. panel_host_.reset(new PanelHost(this, profile_)); panel_host_->Init(url); content::WebContents* web_contents = GetWebContents(); // The contents might be NULL for most of our tests. if (web_contents) native_panel_->AttachWebContents(web_contents); // Close when the extension is unloaded or the browser is exiting. registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, content::Source(profile_)); registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING, content::NotificationService::AllSources()); registrar_.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED, content::Source( ThemeServiceFactory::GetForProfile(profile_))); // Prevent the browser process from shutting down while this window is open. chrome::StartKeepAlive(); UpdateAppIcon(); } void Panel::SetPanelBounds(const gfx::Rect& bounds) { if (bounds != native_panel_->GetPanelBounds()) native_panel_->SetPanelBounds(bounds); } void Panel::SetPanelBoundsInstantly(const gfx::Rect& bounds) { native_panel_->SetPanelBoundsInstantly(bounds); } void Panel::LimitSizeToWorkArea(const gfx::Rect& work_area) { int max_width = manager()->GetMaxPanelWidth(work_area); int max_height = manager()->GetMaxPanelHeight(work_area); // If the custom max size is used, ensure that it does not exceed the display // area. if (max_size_policy_ == CUSTOM_MAX_SIZE) { int current_max_width = max_size_.width(); if (current_max_width > max_width) max_width = std::min(current_max_width, work_area.width()); int current_max_height = max_size_.height(); if (current_max_height > max_height) max_height = std::min(current_max_height, work_area.height()); } SetSizeRange(min_size_, gfx::Size(max_width, max_height)); // Ensure that full size does not exceed max size. full_size_ = ClampSize(full_size_); } void Panel::SetAutoResizable(bool resizable) { if (auto_resizable_ == resizable) return; auto_resizable_ = resizable; content::WebContents* web_contents = GetWebContents(); if (auto_resizable_) { if (web_contents) EnableWebContentsAutoResize(web_contents); } else { if (web_contents) { registrar_.Remove(this, content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED, content::Source(web_contents)); // NULL might be returned if the tab has not been added. RenderViewHost* render_view_host = web_contents->GetRenderViewHost(); if (render_view_host) render_view_host->DisableAutoResize(full_size_); } } } void Panel::EnableWebContentsAutoResize(content::WebContents* web_contents) { DCHECK(web_contents); ConfigureAutoResize(web_contents); // We also need to know when the render view host changes in order // to turn on auto-resize notifications in the new render view host. if (!registrar_.IsRegistered( this, content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED, content::Source(web_contents))) { registrar_.Add( this, content::NOTIFICATION_RENDER_VIEW_HOST_CHANGED, content::Source(web_contents)); } } void Panel::OnContentsAutoResized(const gfx::Size& new_content_size) { DCHECK(auto_resizable_); if (!collection_) return; gfx::Size new_window_size = native_panel_->WindowSizeFromContentSize(new_content_size); // Ignore content auto resizes until window frame size is known. // This reduces extra resizes when panel is first shown. // After window frame size is known, it will trigger another content // auto resize. if (new_content_size == new_window_size) return; collection_->ResizePanelWindow(this, new_window_size); } void Panel::OnWindowResizedByMouse(const gfx::Rect& new_bounds) { if (collection_) collection_->OnPanelResizedByMouse(this, new_bounds); } void Panel::SetSizeRange(const gfx::Size& min_size, const gfx::Size& max_size) { if (min_size == min_size_ && max_size == max_size_) return; DCHECK(min_size.width() <= max_size.width()); DCHECK(min_size.height() <= max_size.height()); min_size_ = min_size; max_size_ = max_size; ConfigureAutoResize(GetWebContents()); } void Panel::IncreaseMaxSize(const gfx::Size& desired_panel_size) { gfx::Size new_max_size = max_size_; if (new_max_size.width() < desired_panel_size.width()) new_max_size.set_width(desired_panel_size.width()); if (new_max_size.height() < desired_panel_size.height()) new_max_size.set_height(desired_panel_size.height()); SetSizeRange(min_size_, new_max_size); } void Panel::HandleKeyboardEvent(const content::NativeWebKeyboardEvent& event) { native_panel_->HandlePanelKeyboardEvent(event); } void Panel::SetPreviewMode(bool in_preview) { DCHECK_NE(in_preview_mode_, in_preview); in_preview_mode_ = in_preview; } void Panel::UpdateMinimizeRestoreButtonVisibility() { native_panel_->UpdatePanelMinimizeRestoreButtonVisibility(); } gfx::Size Panel::ClampSize(const gfx::Size& size) const { // The panel width: // * cannot grow or shrink to go beyond [min_width, max_width] int new_width = size.width(); if (new_width > max_size_.width()) new_width = max_size_.width(); if (new_width < min_size_.width()) new_width = min_size_.width(); // The panel height: // * cannot grow or shrink to go beyond [min_height, max_height] int new_height = size.height(); if (new_height > max_size_.height()) new_height = max_size_.height(); if (new_height < min_size_.height()) new_height = min_size_.height(); return gfx::Size(new_width, new_height); } void Panel::OnActiveStateChanged(bool active) { // Clear attention state when an expanded panel becomes active. // On some systems (e.g. Win), mouse-down activates a panel regardless of // its expansion state. However, we don't want to clear draw attention if // contents are not visible. In that scenario, if the mouse-down results // in a mouse-click, draw attention will be cleared then. // See Panel::OnTitlebarClicked(). if (active && IsDrawingAttention() && !IsMinimized()) FlashFrame(false); if (collection_) collection_->OnPanelActiveStateChanged(this); // Send extension event about window changing active state. extensions::TabsWindowsAPI* tabs_windows_api = extensions::TabsWindowsAPI::Get(profile()); if (tabs_windows_api) { tabs_windows_api->windows_event_router()->OnActiveWindowChanged( active ? extension_window_controller_.get() : NULL); } content::NotificationService::current()->Notify( chrome::NOTIFICATION_PANEL_CHANGED_ACTIVE_STATUS, content::Source(this), content::NotificationService::NoDetails()); } void Panel::OnPanelStartUserResizing() { SetAutoResizable(false); SetPreviewMode(true); max_size_policy_ = CUSTOM_MAX_SIZE; } void Panel::OnPanelEndUserResizing() { SetPreviewMode(false); } bool Panel::ShouldCloseWindow() { return true; } void Panel::OnWindowClosing() { if (GetWebContents()) { native_panel_->DetachWebContents(GetWebContents()); panel_host_->DestroyWebContents(); } } bool Panel::ExecuteCommandIfEnabled(int id) { if (command_updater()->SupportsCommand(id) && command_updater()->IsCommandEnabled(id)) { ExecuteCommandWithDisposition(id, CURRENT_TAB); return true; } return false; } base::string16 Panel::GetWindowTitle() const { content::WebContents* contents = GetWebContents(); base::string16 title; // |contents| can be NULL during the window's creation. if (contents) { title = contents->GetTitle(); FormatTitleForDisplay(&title); } if (title.empty()) title = UTF8ToUTF16(app_name()); return title; } gfx::Image Panel::GetCurrentPageIcon() const { return panel_host_->GetPageIcon(); } void Panel::UpdateTitleBar() { native_panel_->UpdatePanelTitleBar(); } void Panel::LoadingStateChanged(bool is_loading) { command_updater_.UpdateCommandEnabled(IDC_STOP, is_loading); native_panel_->UpdatePanelLoadingAnimations(is_loading); UpdateTitleBar(); } void Panel::WebContentsFocused(content::WebContents* contents) { native_panel_->PanelWebContentsFocused(contents); } void Panel::MoveByInstantly(const gfx::Vector2d& delta_origin) { gfx::Rect bounds = GetBounds(); bounds.Offset(delta_origin); SetPanelBoundsInstantly(bounds); } void Panel::SetWindowCornerStyle(panel::CornerStyle corner_style) { native_panel_->SetWindowCornerStyle(corner_style); } void Panel::MinimizeBySystem() { native_panel_->MinimizePanelBySystem(); } Panel::Panel(Profile* profile, const std::string& app_name, const gfx::Size& min_size, const gfx::Size& max_size) : app_name_(app_name), profile_(profile), collection_(NULL), initialized_(false), min_size_(min_size), max_size_(max_size), max_size_policy_(DEFAULT_MAX_SIZE), auto_resizable_(false), in_preview_mode_(false), native_panel_(NULL), attention_mode_(USE_PANEL_ATTENTION), expansion_state_(EXPANDED), command_updater_(this), image_loader_ptr_factory_(this) { } void Panel::OnImageLoaded(const gfx::Image& image) { if (!image.IsEmpty()) { app_icon_ = image; native_panel_->UpdatePanelTitleBar(); } content::NotificationService::current()->Notify( chrome::NOTIFICATION_PANEL_APP_ICON_LOADED, content::Source(this), content::NotificationService::NoDetails()); } void Panel::InitCommandState() { // All supported commands whose state isn't set automagically some other way // (like Stop during a page load) must have their state initialized here, // otherwise they will be forever disabled. // Navigation commands command_updater_.UpdateCommandEnabled(IDC_RELOAD, true); command_updater_.UpdateCommandEnabled(IDC_RELOAD_IGNORING_CACHE, true); // Window management commands command_updater_.UpdateCommandEnabled(IDC_CLOSE_WINDOW, true); command_updater_.UpdateCommandEnabled(IDC_EXIT, true); // Zoom command_updater_.UpdateCommandEnabled(IDC_ZOOM_MENU, true); command_updater_.UpdateCommandEnabled(IDC_ZOOM_PLUS, true); command_updater_.UpdateCommandEnabled(IDC_ZOOM_NORMAL, true); command_updater_.UpdateCommandEnabled(IDC_ZOOM_MINUS, true); // Clipboard command_updater_.UpdateCommandEnabled(IDC_COPY, true); command_updater_.UpdateCommandEnabled(IDC_CUT, true); command_updater_.UpdateCommandEnabled(IDC_PASTE, true); // DevTools command_updater_.UpdateCommandEnabled(IDC_DEV_TOOLS, true); command_updater_.UpdateCommandEnabled(IDC_DEV_TOOLS_CONSOLE, true); } void Panel::ConfigureAutoResize(content::WebContents* web_contents) { if (!auto_resizable_ || !web_contents) return; // NULL might be returned if the tab has not been added. RenderViewHost* render_view_host = web_contents->GetRenderViewHost(); if (!render_view_host) return; render_view_host->EnableAutoResize( min_size_, native_panel_->ContentSizeFromWindowSize(max_size_)); } void Panel::UpdateAppIcon() { const extensions::Extension* extension = GetExtension(); if (!extension) return; extensions::ImageLoader* loader = extensions::ImageLoader::Get(profile()); loader->LoadImageAsync( extension, extensions::IconsInfo::GetIconResource( extension, extension_misc::EXTENSION_ICON_SMALL, ExtensionIconSet::MATCH_BIGGER), gfx::Size(extension_misc::EXTENSION_ICON_SMALL, extension_misc::EXTENSION_ICON_SMALL), base::Bind(&Panel::OnImageLoaded, image_loader_ptr_factory_.GetWeakPtr())); } // static void Panel::FormatTitleForDisplay(base::string16* title) { size_t current_index = 0; size_t match_index; while ((match_index = title->find(L'\n', current_index)) != base::string16::npos) { title->replace(match_index, 1, base::string16()); current_index = match_index; } }