diff options
author | atwilson@chromium.org <atwilson@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-11-10 19:31:12 +0000 |
---|---|---|
committer | atwilson@chromium.org <atwilson@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-11-10 19:31:12 +0000 |
commit | da58f5b14586b6db155a22b8b1c8d4de8cf6d8f5 (patch) | |
tree | bbdc8f01e3a24d791abc41ce5404e3202e803def | |
parent | 4f35e4b08dbd1ed5221402a5aba29a69ef27fa58 (diff) | |
download | chromium_src-da58f5b14586b6db155a22b8b1c8d4de8cf6d8f5.zip chromium_src-da58f5b14586b6db155a22b8b1c8d4de8cf6d8f5.tar.gz chromium_src-da58f5b14586b6db155a22b8b1c8d4de8cf6d8f5.tar.bz2 |
Added support for displaying BackgroundContents in the task manager.
Previously BackgroundContents would get created, but since they were
not TabContents, they would not show up.
BUG=62105
TEST=TaskManagerBrowserTest.NoticeBGContentsChanges
Review URL: http://codereview.chromium.org/4760001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@65690 0039d316-1c4b-4281-b951-d872f2087c98
18 files changed, 578 insertions, 138 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index a5dc74c..2cdf309 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -3501,6 +3501,9 @@ each locale. --> <message name="IDS_TASK_MANAGER_TAB_PREFIX" desc="The prefix for a Task Manager Tab row"> Tab: <ph name="TAB_NAME">$1<ex>Google</ex></ph> </message> + <message name="IDS_TASK_MANAGER_BACKGROUND_PREFIX" desc="The prefix for a Task Manager background page row"> + Background Page: <ph name="BACKGROUND_PAGE_URL">$1<ex>http://www.google.com</ex></ph> + </message> <message name="IDS_TASK_MANAGER_PLUGIN_PREFIX" desc="The prefix for a Task Manager plugin row"> Plug-in: <ph name="PLUGIN_NAME">$1<ex>Unknown Plug-in</ex></ph> (<ph name="PLUGIN_VERSION">$2<ex>3.4.255.1b</ex></ph>) </message> diff --git a/chrome/browser/background_contents_service.cc b/chrome/browser/background_contents_service.cc index 8517d97..10b98cb 100644 --- a/chrome/browser/background_contents_service.cc +++ b/chrome/browser/background_contents_service.cc @@ -58,6 +58,16 @@ BackgroundContentsService::~BackgroundContentsService() { DCHECK(contents_map_.empty()); } +std::vector<BackgroundContents*> +BackgroundContentsService::GetBackgroundContents() const +{ + std::vector<BackgroundContents*> contents; + for (BackgroundContentsMap::const_iterator it = contents_map_.begin(); + it != contents_map_.end(); ++it) + contents.push_back(it->second.contents); + return contents; +} + void BackgroundContentsService::StartObserving(Profile* profile) { // On startup, load our background pages after extension-apps have loaded. registrar_.Add(this, NotificationType::EXTENSIONS_READY, @@ -65,9 +75,8 @@ void BackgroundContentsService::StartObserving(Profile* profile) { // Track the lifecycle of all BackgroundContents in the system to allow us // to store an up-to-date list of the urls. Start tracking contents when they - // have been opened, and stop tracking them when they are closed by script. - registrar_.Add(this, NotificationType::BACKGROUND_CONTENTS_OPENED, - Source<Profile>(profile)); + // have been opened via CreateBackgroundContents(), and stop tracking them + // when they are closed by script. registrar_.Add(this, NotificationType::BACKGROUND_CONTENTS_CLOSED, Source<Profile>(profile)); @@ -87,8 +96,8 @@ void BackgroundContentsService::StartObserving(Profile* profile) { } void BackgroundContentsService::Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details) { + const NotificationSource& source, + const NotificationDetails& details) { switch (type.value) { case NotificationType::EXTENSIONS_READY: LoadBackgroundContentsFromPrefs(Source<Profile>(source).ptr()); @@ -96,10 +105,6 @@ void BackgroundContentsService::Observe(NotificationType type, case NotificationType::BACKGROUND_CONTENTS_DELETED: BackgroundContentsShutdown(Details<BackgroundContents>(details).ptr()); break; - case NotificationType::BACKGROUND_CONTENTS_OPENED: - BackgroundContentsOpened( - Details<BackgroundContentsOpenedDetails>(details).ptr()); - break; case NotificationType::BACKGROUND_CONTENTS_CLOSED: DCHECK(IsTracked(Details<BackgroundContents>(details).ptr())); UnregisterBackgroundContents(Details<BackgroundContents>(details).ptr()); @@ -127,6 +132,8 @@ void BackgroundContentsService::LoadBackgroundContentsFromPrefs( prefs_->GetDictionary(prefs::kRegisteredBackgroundContents); if (!contents) return; + ExtensionsService* extensions_service = profile->GetExtensionsService(); + DCHECK(extensions_service); for (DictionaryValue::key_iterator it = contents->begin_keys(); it != contents->end_keys(); ++it) { DictionaryValue* dict; @@ -135,14 +142,28 @@ void BackgroundContentsService::LoadBackgroundContentsFromPrefs( std::string url; dict->GetString(kUrlKey, &url); dict->GetString(kFrameNameKey, &frame_name); - CreateBackgroundContents(profile, - GURL(url), - frame_name, - UTF8ToUTF16(*it)); + + // Check to make sure that the parent extension is still enabled. + const Extension* extension = extensions_service->GetExtensionById( + *it, false); + + if (!extension) { + // We should never reach here - it should not be possible for an app + // to become uninstalled without the associated BackgroundContents being + // unregistered via the EXTENSIONS_UNLOADED notification, unless there's a + // crash before we could save our prefs. + NOTREACHED() << "No extension found for BackgroundContents - id = " + << *it; + return; + } + LoadBackgroundContents(profile, + GURL(url), + frame_name, + UTF8ToUTF16(*it)); } } -void BackgroundContentsService::CreateBackgroundContents( +void BackgroundContentsService::LoadBackgroundContents( Profile* profile, const GURL& url, const string16& frame_name, @@ -155,32 +176,12 @@ void BackgroundContentsService::CreateBackgroundContents( DCHECK(url.is_valid()); DVLOG(1) << "Loading background content url: " << url; - // Check to make sure that the parent extension is still enabled. - ExtensionsService* extensions_service = profile->GetExtensionsService(); - const Extension* extension = extensions_service->GetExtensionById( - UTF16ToASCII(application_id), false); - - if (!extension) { - // We should never reach here - it should not be possible for an application - // to become uninstalled without the associated BackgroundContents being - // unregistered via the EXTENSIONS_UNLOADED notification, unless there's a - // crash before we could save our prefs. - NOTREACHED() << "No extension found for BackgroundContents - id = " - << application_id; - return; - } - - BackgroundContents* contents = new BackgroundContents( + BackgroundContents* contents = CreateBackgroundContents( SiteInstance::CreateSiteInstanceForURL(profile, url), MSG_ROUTING_NONE, - this); - - // TODO(atwilson): Change this to send a BACKGROUND_CONTENTS_CREATED - // notification when we have a listener outside of BackgroundContentsService. - BackgroundContentsOpenedDetails details = {contents, - frame_name, - application_id}; - BackgroundContentsOpened(&details); + profile, + frame_name, + application_id); RenderViewHost* render_view_host = contents->render_view_host(); // TODO(atwilson): Create RenderViews asynchronously to avoid increasing @@ -189,6 +190,27 @@ void BackgroundContentsService::CreateBackgroundContents( render_view_host->NavigateToURL(url); } +BackgroundContents* BackgroundContentsService::CreateBackgroundContents( + SiteInstance* site, + int routing_id, + Profile* profile, + const string16& frame_name, + const string16& application_id) { + BackgroundContents* contents = new BackgroundContents(site, routing_id, this); + + // Register the BackgroundContents internally, then send out a notification + // to external listeners. + BackgroundContentsOpenedDetails details = {contents, + frame_name, + application_id}; + BackgroundContentsOpened(&details); + NotificationService::current()->Notify( + NotificationType::BACKGROUND_CONTENTS_OPENED, + Source<Profile>(profile), + Details<BackgroundContentsOpenedDetails>(&details)); + return contents; +} + void BackgroundContentsService::RegisterBackgroundContents( BackgroundContents* background_contents) { DCHECK(IsTracked(background_contents)); diff --git a/chrome/browser/background_contents_service.h b/chrome/browser/background_contents_service.h index 08165d8..7fe16ac 100644 --- a/chrome/browser/background_contents_service.h +++ b/chrome/browser/background_contents_service.h @@ -7,6 +7,7 @@ #pragma once #include <map> +#include <vector> #include "base/gtest_prod_util.h" #include "chrome/browser/tab_contents/background_contents.h" @@ -45,6 +46,9 @@ class BackgroundContentsService : private NotificationObserver, // or NULL if none. BackgroundContents* GetAppBackgroundContents(const string16& appid); + // Returns all currently opened BackgroundContents (used by the task manager). + std::vector<BackgroundContents*> GetBackgroundContents() const; + static void RegisterUserPrefs(PrefService* prefs); // BackgroundContents::Delegate implementation. @@ -53,12 +57,33 @@ class BackgroundContentsService : private NotificationObserver, const gfx::Rect& initial_pos, bool user_gesture); + // Gets the parent application id for the passed BackgroundContents. Returns + // an empty string if no parent application found (e.g. passed + // BackgroundContents has already shut down). + const string16& GetParentApplicationId(BackgroundContents* contents) const; + + // Creates a new BackgroundContents using the passed |site| and + // the |route_id| and begins tracking the object internally so it can be + // shutdown if the parent application is uninstalled. + // A BACKGROUND_CONTENTS_OPENED notification will be generated with the passed + // |frame_name| and |application_id| values, using the passed |profile| as the + // Source.. + BackgroundContents* CreateBackgroundContents(SiteInstance* site, + int route_id, + Profile* profile, + const string16& frame_name, + const string16& application_id); + private: friend class BackgroundContentsServiceTest; + friend class MockBackgroundContents; + friend class TaskManagerBrowserTest; FRIEND_TEST_ALL_PREFIXES(BackgroundContentsServiceTest, BackgroundContentsCreateDestroy); FRIEND_TEST_ALL_PREFIXES(BackgroundContentsServiceTest, TestApplicationIDLinkage); + FRIEND_TEST_ALL_PREFIXES(TaskManagerBrowserTest, + NoticeBGContentsChanges); // Registers for various notifications. void StartObserving(Profile* profile); @@ -71,13 +96,13 @@ class BackgroundContentsService : private NotificationObserver, // Loads all registered BackgroundContents at startup. void LoadBackgroundContentsFromPrefs(Profile* profile); - // Creates a single BackgroundContents associated with the specified |appid|. - // The BackgroundContents frame will be given the name specified by - // |frame_name| and navigated to the passed URL. - void CreateBackgroundContents(Profile* profile, - const GURL& url, - const string16& frame_name, - const string16& appid); + // Creates a single BackgroundContents associated with the specified |appid|, + // creates an associated RenderView with the name specified by |frame_name|, + // and navigates to the passed |url|. + void LoadBackgroundContents(Profile* profile, + const GURL& url, + const string16& frame_name, + const string16& appid); // Invoked when a new BackgroundContents is opened. void BackgroundContentsOpened(BackgroundContentsOpenedDetails* details); @@ -101,11 +126,6 @@ class BackgroundContentsService : private NotificationObserver, // Returns true if this BackgroundContents is in the contents_list_. bool IsTracked(BackgroundContents* contents) const; - // Gets the parent application id for the passed BackgroundContents. Returns - // an empty string if no parent application found (e.g. passed - // BackgroundContents has already shut down). - const string16& GetParentApplicationId(BackgroundContents* contents) const; - // PrefService used to store list of background pages (or NULL if this is // running under an off-the-record profile). PrefService* prefs_; diff --git a/chrome/browser/background_contents_service_unittest.cc b/chrome/browser/background_contents_service_unittest.cc index 23ba929..566b74b 100644 --- a/chrome/browser/background_contents_service_unittest.cc +++ b/chrome/browser/background_contents_service_unittest.cc @@ -57,14 +57,11 @@ class MockBackgroundContents : public BackgroundContents { profile_(profile) { } - void SendOpenedNotification() { + void SendOpenedNotification(BackgroundContentsService* service) { string16 frame_name = ASCIIToUTF16("background"); BackgroundContentsOpenedDetails details = { this, frame_name, appid_ }; - NotificationService::current()->Notify( - NotificationType::BACKGROUND_CONTENTS_OPENED, - Source<Profile>(profile_), - Details<BackgroundContentsOpenedDetails>(&details)); + service->BackgroundContentsOpened(&details); } virtual void Navigate(GURL url) { @@ -114,7 +111,7 @@ TEST_F(BackgroundContentsServiceTest, BackgroundContentsCreateDestroy) { BackgroundContentsService service(&profile, command_line_.get()); MockBackgroundContents* contents = new MockBackgroundContents(&profile); EXPECT_FALSE(service.IsTracked(contents)); - contents->SendOpenedNotification(); + contents->SendOpenedNotification(&service); EXPECT_TRUE(service.IsTracked(contents)); delete contents; EXPECT_FALSE(service.IsTracked(contents)); @@ -131,7 +128,7 @@ TEST_F(BackgroundContentsServiceTest, BackgroundContentsUrlAdded) { scoped_ptr<MockBackgroundContents> contents( new MockBackgroundContents(&profile)); EXPECT_EQ(0U, GetPrefs(&profile)->size()); - contents->SendOpenedNotification(); + contents->SendOpenedNotification(&service); contents->Navigate(url); EXPECT_EQ(1U, GetPrefs(&profile)->size()); @@ -154,7 +151,7 @@ TEST_F(BackgroundContentsServiceTest, BackgroundContentsUrlAddedAndClosed) { GURL url("http://a/"); MockBackgroundContents* contents = new MockBackgroundContents(&profile); EXPECT_EQ(0U, GetPrefs(&profile)->size()); - contents->SendOpenedNotification(); + contents->SendOpenedNotification(&service); contents->Navigate(url); EXPECT_EQ(1U, GetPrefs(&profile)->size()); EXPECT_EQ(url.spec(), GetPrefURLForApp(&profile, contents->appid())); @@ -174,7 +171,7 @@ TEST_F(BackgroundContentsServiceTest, RestartBackgroundContents) { { scoped_ptr<MockBackgroundContents> contents(new MockBackgroundContents( &profile, "appid")); - contents->SendOpenedNotification(); + contents->SendOpenedNotification(&service); contents->Navigate(url); EXPECT_EQ(1U, GetPrefs(&profile)->size()); EXPECT_EQ(url.spec(), GetPrefURLForApp(&profile, contents->appid())); @@ -187,7 +184,7 @@ TEST_F(BackgroundContentsServiceTest, RestartBackgroundContents) { // URL again. scoped_ptr<MockBackgroundContents> contents(new MockBackgroundContents( &profile, "appid")); - contents->SendOpenedNotification(); + contents->SendOpenedNotification(&service); contents->Navigate(url); EXPECT_EQ(1U, GetPrefs(&profile)->size()); } @@ -206,9 +203,9 @@ TEST_F(BackgroundContentsServiceTest, TestApplicationIDLinkage) { "appid"); scoped_ptr<MockBackgroundContents> contents2( new MockBackgroundContents(&profile, "appid2")); - contents->SendOpenedNotification(); + contents->SendOpenedNotification(&service); EXPECT_EQ(contents, service.GetAppBackgroundContents(contents->appid())); - contents2->SendOpenedNotification(); + contents2->SendOpenedNotification(&service); EXPECT_EQ(contents2.get(), service.GetAppBackgroundContents( contents2->appid())); EXPECT_EQ(0U, GetPrefs(&profile)->size()); diff --git a/chrome/browser/profile.cc b/chrome/browser/profile.cc index 4b11fadb..626e602 100644 --- a/chrome/browser/profile.cc +++ b/chrome/browser/profile.cc @@ -204,7 +204,7 @@ class OffTheRecordProfileImpl : public Profile, return GetOriginalProfile()->GetExtensionsService(); } - virtual BackgroundContentsService* GetBackgroundContentsService() { + virtual BackgroundContentsService* GetBackgroundContentsService() const { return background_contents_service_.get(); } diff --git a/chrome/browser/profile.h b/chrome/browser/profile.h index 9c958e0..7d4e8f5 100644 --- a/chrome/browser/profile.h +++ b/chrome/browser/profile.h @@ -436,7 +436,7 @@ class Profile { virtual DesktopNotificationService* GetDesktopNotificationService() = 0; // Returns the service that manages BackgroundContents for this profile. - virtual BackgroundContentsService* GetBackgroundContentsService() = 0; + virtual BackgroundContentsService* GetBackgroundContentsService() const = 0; // Returns the StatusTray, which provides an API for displaying status icons // in the system status tray. Returns NULL if status icons are not supported diff --git a/chrome/browser/profile_impl.cc b/chrome/browser/profile_impl.cc index 28ee7d1..034af7b 100644 --- a/chrome/browser/profile_impl.cc +++ b/chrome/browser/profile_impl.cc @@ -620,7 +620,7 @@ ExtensionsService* ProfileImpl::GetExtensionsService() { return extensions_service_.get(); } -BackgroundContentsService* ProfileImpl::GetBackgroundContentsService() { +BackgroundContentsService* ProfileImpl::GetBackgroundContentsService() const { return background_contents_service_.get(); } diff --git a/chrome/browser/profile_impl.h b/chrome/browser/profile_impl.h index 5b0906c..a027406 100644 --- a/chrome/browser/profile_impl.h +++ b/chrome/browser/profile_impl.h @@ -105,7 +105,7 @@ class ProfileImpl : public Profile, virtual void ReinitializeSpellCheckHost(bool force); virtual WebKitContext* GetWebKitContext(); virtual DesktopNotificationService* GetDesktopNotificationService(); - virtual BackgroundContentsService* GetBackgroundContentsService(); + virtual BackgroundContentsService* GetBackgroundContentsService() const; virtual StatusTray* GetStatusTray(); virtual void MarkAsCleanShutdown(); virtual void InitExtensions(); diff --git a/chrome/browser/renderer_host/render_view_host_delegate.cc b/chrome/browser/renderer_host/render_view_host_delegate.cc index 523fd8f..2ca69c4 100644 --- a/chrome/browser/renderer_host/render_view_host_delegate.cc +++ b/chrome/browser/renderer_host/render_view_host_delegate.cc @@ -97,6 +97,10 @@ TabContents* RenderViewHostDelegate::GetAsTabContents() { return NULL; } +BackgroundContents* RenderViewHostDelegate::GetAsBackgroundContents() { + return NULL; +} + GURL RenderViewHostDelegate::GetAlternateErrorPageURL() const { return GURL(); } diff --git a/chrome/browser/renderer_host/render_view_host_delegate.h b/chrome/browser/renderer_host/render_view_host_delegate.h index a4e88a3..670b5f1 100644 --- a/chrome/browser/renderer_host/render_view_host_delegate.h +++ b/chrome/browser/renderer_host/render_view_host_delegate.h @@ -24,6 +24,7 @@ class AutomationResourceRoutingDelegate; +class BackgroundContents; struct BookmarkDragData; class BookmarkNode; struct ContextMenuParams; @@ -679,6 +680,10 @@ class RenderViewHostDelegate { // not a TabContents, returns NULL. virtual TabContents* GetAsTabContents(); + // Return this object cast to a BackgroundContents, if it is one. If the + // object is not a BackgroundContents, returns NULL. + virtual BackgroundContents* GetAsBackgroundContents(); + // Return id number of browser window which this object is attached to. If no // browser window is attached to, just return -1. virtual int GetBrowserWindowID() const = 0; diff --git a/chrome/browser/tab_contents/background_contents.cc b/chrome/browser/tab_contents/background_contents.cc index 41735d8..413810e 100644 --- a/chrome/browser/tab_contents/background_contents.cc +++ b/chrome/browser/tab_contents/background_contents.cc @@ -218,3 +218,15 @@ void BackgroundContents::ShowCreatedWidget(int route_id, void BackgroundContents::ShowCreatedFullscreenWidget(int route_id) { NOTIMPLEMENTED(); } + +// static +BackgroundContents* +BackgroundContents::GetBackgroundContentsByID(int render_process_id, + int render_view_id) { + RenderViewHost* render_view_host = + RenderViewHost::FromID(render_process_id, render_view_id); + if (!render_view_host) + return NULL; + + return render_view_host->delegate()->GetAsBackgroundContents(); +} diff --git a/chrome/browser/tab_contents/background_contents.h b/chrome/browser/tab_contents/background_contents.h index 63a30d9..81ae92d 100644 --- a/chrome/browser/tab_contents/background_contents.h +++ b/chrome/browser/tab_contents/background_contents.h @@ -58,6 +58,7 @@ class BackgroundContents : public RenderViewHostDelegate, RenderViewHost* render_view_host() { return render_view_host_; } // RenderViewHostDelegate implementation. + virtual BackgroundContents* GetAsBackgroundContents() { return this; } virtual RenderViewHostDelegate::View* GetViewDelegate() { return this; } virtual const GURL& GetURL() const { return url_; } virtual ViewType::Type GetRenderViewType() const; @@ -135,6 +136,13 @@ class BackgroundContents : public RenderViewHostDelegate, const std::string& value); virtual void ClearInspectorSettings(); + // Helper to find the BackgroundContents that originated the given request. + // Can be NULL if the page has been closed or some other error occurs. + // Should only be called from the UI thread, since it accesses + // BackgroundContents. + static BackgroundContents* GetBackgroundContentsByID(int render_process_id, + int render_view_id); + protected: // Exposed for testing. BackgroundContents(); diff --git a/chrome/browser/tab_contents/render_view_host_delegate_helper.cc b/chrome/browser/tab_contents/render_view_host_delegate_helper.cc index 15ff1ca..8c8754d 100644 --- a/chrome/browser/tab_contents/render_view_host_delegate_helper.cc +++ b/chrome/browser/tab_contents/render_view_host_delegate_helper.cc @@ -67,18 +67,8 @@ RenderViewHostDelegateViewHelper::MaybeCreateBackgroundContents( return NULL; // Passed all the checks, so this should be created as a BackgroundContents. - BackgroundContents* contents = new BackgroundContents( - site, - route_id, - profile->GetBackgroundContentsService()); - string16 appid = ASCIIToUTF16(extension->id()); - BackgroundContentsOpenedDetails details = { contents, frame_name, appid }; - NotificationService::current()->Notify( - NotificationType::BACKGROUND_CONTENTS_OPENED, - Source<Profile>(profile), - Details<BackgroundContentsOpenedDetails>(&details)); - - return contents; + return profile->GetBackgroundContentsService()->CreateBackgroundContents( + site, route_id, profile, frame_name, ASCIIToUTF16(extension->id())); } TabContents* RenderViewHostDelegateViewHelper::CreateNewWindow( diff --git a/chrome/browser/task_manager/task_manager.cc b/chrome/browser/task_manager/task_manager.cc index 2711925..2080b34 100644 --- a/chrome/browser/task_manager/task_manager.cc +++ b/chrome/browser/task_manager/task_manager.cc @@ -82,6 +82,10 @@ TaskManagerModel::TaskManagerModel(TaskManager* task_manager) new TaskManagerTabContentsResourceProvider(task_manager); wc_provider->AddRef(); providers_.push_back(wc_provider); + TaskManagerBackgroundContentsResourceProvider* bc_provider = + new TaskManagerBackgroundContentsResourceProvider(task_manager); + bc_provider->AddRef(); + providers_.push_back(bc_provider); TaskManagerChildProcessResourceProvider* child_process_provider = new TaskManagerChildProcessResourceProvider(task_manager); child_process_provider->AddRef(); diff --git a/chrome/browser/task_manager/task_manager_browsertest.cc b/chrome/browser/task_manager/task_manager_browsertest.cc index 9e3c181..7230254 100644 --- a/chrome/browser/task_manager/task_manager_browsertest.cc +++ b/chrome/browser/task_manager/task_manager_browsertest.cc @@ -7,6 +7,7 @@ #include "app/l10n_util.h" #include "base/file_path.h" #include "base/utf_string_conversions.h" +#include "chrome/browser/background_contents_service.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_navigator.h" #include "chrome/browser/browser_process.h" @@ -16,6 +17,7 @@ #include "chrome/browser/notifications/desktop_notification_service.h" #include "chrome/browser/notifications/notification_test_util.h" #include "chrome/browser/notifications/notification_ui_manager.h" +#include "chrome/browser/profile.h" #include "chrome/browser/tab_contents/infobar_delegate.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/tabs/tab_strip_model.h" @@ -118,6 +120,34 @@ IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, NoticeTabContentsChanges) { WaitForResourceChange(2); } +IN_PROC_BROWSER_TEST_F(TaskManagerBrowserTest, NoticeBGContentsChanges) { + EXPECT_EQ(0, model()->ResourceCount()); + + // Show the task manager. This populates the model, and helps with debugging + // (you see the task manager). + browser()->window()->ShowTaskManager(); + + // Browser and the New Tab Page. + EXPECT_EQ(2, model()->ResourceCount()); + + // Open a new background contents and make sure we notice that. + GURL url(ui_test_utils::GetTestUrl(FilePath(FilePath::kCurrentDirectory), + FilePath(kTitle1File))); + + BackgroundContentsService* service = + browser()->profile()->GetBackgroundContentsService(); + string16 application_id(ASCIIToUTF16("test_app_id")); + service->LoadBackgroundContents(browser()->profile(), + url, + ASCIIToUTF16("background_page"), + application_id); + WaitForResourceChange(3); + + // Close the background contents and verify that we notice. + service->ShutdownAssociatedBackgroundContents(application_id); + WaitForResourceChange(2); +} + #if defined(OS_WIN) // http://crbug.com/31663 #define MAYBE_NoticeExtensionChanges DISABLED_NoticeExtensionChanges diff --git a/chrome/browser/task_manager/task_manager_resource_providers.cc b/chrome/browser/task_manager/task_manager_resource_providers.cc index 0c3d5d8..13cd82f 100644 --- a/chrome/browser/task_manager/task_manager_resource_providers.cc +++ b/chrome/browser/task_manager/task_manager_resource_providers.cc @@ -15,13 +15,16 @@ #include "base/stl_util-inl.h" #include "base/string_util.h" #include "base/thread.h" +#include "base/utf_string_conversions.h" #include "chrome/app/chrome_command_ids.h" +#include "chrome/browser/background_contents_service.h" #include "chrome/browser/browser_child_process_host.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_thread.h" #include "chrome/browser/extensions/extension_host.h" #include "chrome/browser/extensions/extension_process_manager.h" +#include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/notifications/balloon_collection.h" #include "chrome/browser/notifications/balloon_host.h" #include "chrome/browser/notifications/notification_ui_manager.h" @@ -29,8 +32,9 @@ #include "chrome/browser/renderer_host/render_process_host.h" #include "chrome/browser/renderer_host/render_view_host.h" #include "chrome/browser/renderer_host/resource_message_filter.h" -#include "chrome/browser/tab_contents/tab_util.h" +#include "chrome/browser/tab_contents/background_contents.h" #include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/tab_contents/tab_util.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/notification_service.h" #include "chrome/common/render_messages.h" @@ -46,20 +50,20 @@ #include "gfx/icon_util.h" #endif // defined(OS_WIN) + //////////////////////////////////////////////////////////////////////////////// -// TaskManagerTabContentsResource class +// TaskManagerRendererResource class //////////////////////////////////////////////////////////////////////////////// - -TaskManagerTabContentsResource::TaskManagerTabContentsResource( - TabContents* tab_contents) - : tab_contents_(tab_contents), +TaskManagerRendererResource::TaskManagerRendererResource( + base::ProcessHandle process, RenderViewHost* render_view_host) + : process_(process), + render_view_host_(render_view_host), pending_stats_update_(false), v8_memory_allocated_(0), v8_memory_used_(0), pending_v8_memory_allocated_update_(false) { - // We cache the process as when the TabContents is closed the process - // becomes NULL and the TaskManager still needs it. - process_ = tab_contents_->GetRenderProcessHost()->GetHandle(); + // We cache the process and pid as when a Tab/BackgroundContents is closed the + // process reference becomes NULL and the TaskManager still needs it. pid_ = base::GetProcId(process_); stats_.images.size = 0; stats_.cssStyleSheets.size = 0; @@ -68,76 +72,92 @@ TaskManagerTabContentsResource::TaskManagerTabContentsResource( stats_.fonts.size = 0; } -TaskManagerTabContentsResource::~TaskManagerTabContentsResource() { -} - -std::wstring TaskManagerTabContentsResource::GetTitle() const { - // Fall back on the URL if there's no title. - std::wstring tab_title(UTF16ToWideHack(tab_contents_->GetTitle())); - if (tab_title.empty()) { - tab_title = UTF8ToWide(tab_contents_->GetURL().spec()); - // Force URL to be LTR. - tab_title = UTF16ToWide(base::i18n::GetDisplayStringInLTRDirectionality( - WideToUTF16(tab_title))); - } else { - // Since the tab_title will be concatenated with - // IDS_TASK_MANAGER_TAB_PREFIX, we need to explicitly set the tab_title to - // be LTR format if there is no strong RTL charater in it. Otherwise, if - // IDS_TASK_MANAGER_TAB_PREFIX is an RTL word, the concatenated result - // might be wrong. For example, http://mail.yahoo.com, whose title is - // "Yahoo! Mail: The best web-based Email!", without setting it explicitly - // as LTR format, the concatenated result will be "!Yahoo! Mail: The best - // web-based Email :BAT", in which the capital letters "BAT" stands for - // the Hebrew word for "tab". - base::i18n::AdjustStringForLocaleDirection(tab_title, &tab_title); - } - - return l10n_util::GetStringF(IDS_TASK_MANAGER_TAB_PREFIX, tab_title); +TaskManagerRendererResource::~TaskManagerRendererResource() { } -void TaskManagerTabContentsResource::Refresh() { +void TaskManagerRendererResource::Refresh() { if (!pending_stats_update_) { - tab_contents_->render_view_host()->Send(new ViewMsg_GetCacheResourceStats); + render_view_host_->Send(new ViewMsg_GetCacheResourceStats); pending_stats_update_ = true; } if (!pending_v8_memory_allocated_update_) { - tab_contents_->render_view_host()->Send(new ViewMsg_GetV8HeapStats); + render_view_host_->Send(new ViewMsg_GetV8HeapStats); pending_v8_memory_allocated_update_ = true; } } WebKit::WebCache::ResourceTypeStats - TaskManagerTabContentsResource::GetWebCoreCacheStats() const { +TaskManagerRendererResource::GetWebCoreCacheStats() const { return stats_; } -size_t TaskManagerTabContentsResource::GetV8MemoryAllocated() const { +size_t TaskManagerRendererResource::GetV8MemoryAllocated() const { return v8_memory_allocated_; } -size_t TaskManagerTabContentsResource::GetV8MemoryUsed() const { +size_t TaskManagerRendererResource::GetV8MemoryUsed() const { return v8_memory_used_; } -void TaskManagerTabContentsResource::NotifyResourceTypeStats( +void TaskManagerRendererResource::NotifyResourceTypeStats( const WebKit::WebCache::ResourceTypeStats& stats) { stats_ = stats; pending_stats_update_ = false; } -void TaskManagerTabContentsResource::NotifyV8HeapStats( +void TaskManagerRendererResource::NotifyV8HeapStats( size_t v8_memory_allocated, size_t v8_memory_used) { v8_memory_allocated_ = v8_memory_allocated; v8_memory_used_ = v8_memory_used; pending_v8_memory_allocated_update_ = false; } -SkBitmap TaskManagerTabContentsResource::GetIcon() const { - return tab_contents_->GetFavIcon(); +base::ProcessHandle TaskManagerRendererResource::GetProcess() const { + return process_; } -base::ProcessHandle TaskManagerTabContentsResource::GetProcess() const { - return process_; +//////////////////////////////////////////////////////////////////////////////// +// TaskManagerTabContentsResource class +//////////////////////////////////////////////////////////////////////////////// + +TaskManagerTabContentsResource::TaskManagerTabContentsResource( + TabContents* tab_contents) + : TaskManagerRendererResource( + tab_contents->GetRenderProcessHost()->GetHandle(), + tab_contents->render_view_host()), + tab_contents_(tab_contents) { +} + +TaskManagerTabContentsResource::~TaskManagerTabContentsResource() { +} + +std::wstring TaskManagerTabContentsResource::GetTitle() const { + // Fall back on the URL if there's no title. + std::wstring tab_title(UTF16ToWideHack(tab_contents_->GetTitle())); + if (tab_title.empty()) { + tab_title = UTF8ToWide(tab_contents_->GetURL().spec()); + // Force URL to be LTR. + tab_title = UTF16ToWide(base::i18n::GetDisplayStringInLTRDirectionality( + WideToUTF16(tab_title))); + } else { + // Since the tab_title will be concatenated with + // IDS_TASK_MANAGER_TAB_PREFIX, we need to explicitly set the tab_title to + // be LTR format if there is no strong RTL charater in it. Otherwise, if + // IDS_TASK_MANAGER_TAB_PREFIX is an RTL word, the concatenated result + // might be wrong. For example, http://mail.yahoo.com, whose title is + // "Yahoo! Mail: The best web-based Email!", without setting it explicitly + // as LTR format, the concatenated result will be "!Yahoo! Mail: The best + // web-based Email :BAT", in which the capital letters "BAT" stands for + // the Hebrew word for "tab". + base::i18n::AdjustStringForLocaleDirection(tab_title, &tab_title); + } + + return l10n_util::GetStringF(IDS_TASK_MANAGER_TAB_PREFIX, tab_title); +} + + +SkBitmap TaskManagerTabContentsResource::GetIcon() const { + return tab_contents_->GetFavIcon(); } TabContents* TaskManagerTabContentsResource::GetTabContents() const { @@ -183,11 +203,11 @@ TaskManager::Resource* TaskManagerTabContentsResourceProvider::GetResource( std::map<TabContents*, TaskManagerTabContentsResource*>::iterator res_iter = resources_.find(tab_contents); - if (res_iter == resources_.end()) + if (res_iter == resources_.end()) { // Can happen if the tab was closed while a network request was being // performed. return NULL; - + } return res_iter->second; } @@ -316,6 +336,243 @@ void TaskManagerTabContentsResourceProvider::Observe(NotificationType type, } //////////////////////////////////////////////////////////////////////////////// +// TaskManagerBackgroundContentsResource class +//////////////////////////////////////////////////////////////////////////////// + +SkBitmap* TaskManagerBackgroundContentsResource::default_icon_ = NULL; + +TaskManagerBackgroundContentsResource::TaskManagerBackgroundContentsResource( + BackgroundContents* background_contents, + const std::wstring& application_name) + : TaskManagerRendererResource( + background_contents->render_view_host()->process()->GetHandle(), + background_contents->render_view_host()), + background_contents_(background_contents), + application_name_(application_name) { + // Just use the same icon that other extension resources do. + // TODO(atwilson): Use the favicon when that's available. + if (!default_icon_) { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + default_icon_ = rb.GetBitmapNamed(IDR_PLUGIN); + } + // Ensure that the string has the appropriate direction markers (see comment + // in TaskManagerTabContentsResource::GetTitle()). + base::i18n::AdjustStringForLocaleDirection(application_name_, + &application_name_); +} + +TaskManagerBackgroundContentsResource::~TaskManagerBackgroundContentsResource( + ) { +} + +std::wstring TaskManagerBackgroundContentsResource::GetTitle() const { + std::wstring title = application_name_; + + if (title.empty()) { + // No title (can't locate the parent app for some reason) so just display + // the URL (properly forced to be LTR). + title = UTF16ToWide(base::i18n::GetDisplayStringInLTRDirectionality( + UTF8ToUTF16(background_contents_->GetURL().spec()))); + } + return l10n_util::GetStringF(IDS_TASK_MANAGER_BACKGROUND_PREFIX, title); +} + + +SkBitmap TaskManagerBackgroundContentsResource::GetIcon() const { + return *default_icon_; +} + +//////////////////////////////////////////////////////////////////////////////// +// TaskManagerBackgroundContentsResourceProvider class +//////////////////////////////////////////////////////////////////////////////// + +TaskManagerBackgroundContentsResourceProvider:: + TaskManagerBackgroundContentsResourceProvider(TaskManager* task_manager) + : updating_(false), + task_manager_(task_manager) { +} + +TaskManagerBackgroundContentsResourceProvider:: + ~TaskManagerBackgroundContentsResourceProvider() { +} + +TaskManager::Resource* +TaskManagerBackgroundContentsResourceProvider::GetResource( + int origin_pid, + int render_process_host_id, + int routing_id) { + + BackgroundContents* contents = BackgroundContents::GetBackgroundContentsByID( + render_process_host_id, routing_id); + if (!contents) // This resource no longer exists. + return NULL; + + base::ProcessHandle process_handle = + contents->render_view_host()->process()->GetHandle(); + if (!process_handle) // Process crashed. + return NULL; + + int pid = base::GetProcId(process_handle); + if (pid != origin_pid) + return NULL; + + std::map<BackgroundContents*, + TaskManagerBackgroundContentsResource*>::iterator res_iter = + resources_.find(contents); + if (res_iter == resources_.end()) + // Can happen if the page went away while a network request was being + // performed. + return NULL; + + return res_iter->second; +} + +void TaskManagerBackgroundContentsResourceProvider::StartUpdating() { + DCHECK(!updating_); + updating_ = true; + + // Add all the existing BackgroundContents from every profile. + ProfileManager* profile_manager = g_browser_process->profile_manager(); + for (ProfileManager::const_iterator it = profile_manager->begin(); + it != profile_manager->end(); ++it) { + BackgroundContentsService* background_contents_service = + (*it)->GetBackgroundContentsService(); + ExtensionsService* extensions_service = (*it)->GetExtensionsService(); + std::vector<BackgroundContents*> contents = + background_contents_service->GetBackgroundContents(); + for (std::vector<BackgroundContents*>::iterator iterator = contents.begin(); + iterator != contents.end(); ++iterator) { + std::wstring application_name; + // Lookup the name from the parent extension. + if (extensions_service) { + const string16& application_id = + background_contents_service->GetParentApplicationId(*iterator); + const Extension* extension = extensions_service->GetExtensionById( + UTF16ToUTF8(application_id), false); + if (extension) + application_name = UTF8ToWide(extension->name()); + } + Add(*iterator, application_name); + } + } + + // Then we register for notifications to get new BackgroundContents. + registrar_.Add(this, NotificationType::BACKGROUND_CONTENTS_OPENED, + NotificationService::AllSources()); + registrar_.Add(this, NotificationType::BACKGROUND_CONTENTS_NAVIGATED, + NotificationService::AllSources()); + registrar_.Add(this, NotificationType::BACKGROUND_CONTENTS_DELETED, + NotificationService::AllSources()); +} + +void TaskManagerBackgroundContentsResourceProvider::StopUpdating() { + DCHECK(updating_); + updating_ = false; + + // Unregister for notifications + registrar_.Remove(this, NotificationType::BACKGROUND_CONTENTS_OPENED, + NotificationService::AllSources()); + registrar_.Remove(this, NotificationType::BACKGROUND_CONTENTS_NAVIGATED, + NotificationService::AllSources()); + registrar_.Remove(this, NotificationType::BACKGROUND_CONTENTS_DELETED, + NotificationService::AllSources()); + + // Delete all the resources. + STLDeleteContainerPairSecondPointers(resources_.begin(), resources_.end()); + + resources_.clear(); +} + +void TaskManagerBackgroundContentsResourceProvider::AddToTaskManager( + BackgroundContents* background_contents, + const std::wstring& application_name) { + TaskManagerBackgroundContentsResource* resource = + new TaskManagerBackgroundContentsResource(background_contents, + application_name); + resources_[background_contents] = resource; + task_manager_->AddResource(resource); +} + +void TaskManagerBackgroundContentsResourceProvider::Add( + BackgroundContents* contents, const std::wstring& application_name) { + if (!updating_) + return; + + // Don't add contents whose process is dead. + if (!contents->render_view_host()->process()->GetHandle()) + return; + + // Should never add the same BackgroundContents twice. + DCHECK(resources_.find(contents) == resources_.end()); + AddToTaskManager(contents, application_name); +} + +void TaskManagerBackgroundContentsResourceProvider::Remove( + BackgroundContents* contents) { + if (!updating_) + return; + std::map<BackgroundContents*, + TaskManagerBackgroundContentsResource*>::iterator iter = + resources_.find(contents); + DCHECK(iter != resources_.end()); + + // Remove the resource from the Task Manager. + TaskManagerBackgroundContentsResource* resource = iter->second; + task_manager_->RemoveResource(resource); + // And from the provider. + resources_.erase(iter); + // Finally, delete the resource. + delete resource; +} + +void TaskManagerBackgroundContentsResourceProvider::Observe( + NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + switch (type.value) { + case NotificationType::BACKGROUND_CONTENTS_OPENED: { + // Get the name from the parent application. If no parent application is + // found, just pass an empty string - BackgroundContentsResource::GetTitle + // will display the URL instead in this case. This should never happen + // except in rare cases when an extension is being unloaded or chrome is + // exiting while the task manager is displayed. + std::wstring application_name; + ExtensionsService* service = + Source<Profile>(source)->GetExtensionsService(); + if (service) { + std::string application_id = UTF16ToUTF8( + Details<BackgroundContentsOpenedDetails>(details)->application_id); + const Extension* extension = + service->GetExtensionById(application_id, false); + // Extension can be NULL when running unit tests. + if (extension) + application_name = UTF8ToWide(extension->name()); + } + Add(Details<BackgroundContentsOpenedDetails>(details)->contents, + application_name); + break; + } + case NotificationType::BACKGROUND_CONTENTS_NAVIGATED: { + BackgroundContents* contents = Details<BackgroundContents>(details).ptr(); + // Should never get a NAVIGATED before OPENED. + DCHECK(resources_.find(contents) != resources_.end()); + // Preserve the application name. + std::wstring application_name( + resources_.find(contents)->second->application_name()); + Remove(contents); + Add(contents, application_name); + break; + } + case NotificationType::BACKGROUND_CONTENTS_DELETED: + Remove(Details<BackgroundContents>(details).ptr()); + break; + default: + NOTREACHED() << "Unexpected notification."; + return; + } +} + +//////////////////////////////////////////////////////////////////////////////// // TaskManagerChildProcessResource class //////////////////////////////////////////////////////////////////////////////// SkBitmap* TaskManagerChildProcessResource::default_icon_ = NULL; diff --git a/chrome/browser/task_manager/task_manager_resource_providers.h b/chrome/browser/task_manager/task_manager_resource_providers.h index 5145a7e..62fbb53 100644 --- a/chrome/browser/task_manager/task_manager_resource_providers.h +++ b/chrome/browser/task_manager/task_manager_resource_providers.h @@ -17,33 +17,33 @@ #include "chrome/common/notification_registrar.h" #include "third_party/WebKit/WebKit/chromium/public/WebCache.h" +class BackgroundContents; class BalloonHost; class Extension; class ExtensionHost; +class RenderViewHost; class TabContents; // These file contains the resource providers used in the task manager. -class TaskManagerTabContentsResource : public TaskManager::Resource { +// Base class for various types of render process resources that provides common +// functionality like stats tracking. +class TaskManagerRendererResource : public TaskManager::Resource { public: - explicit TaskManagerTabContentsResource(TabContents* tab_contents); - ~TaskManagerTabContentsResource(); + TaskManagerRendererResource(base::ProcessHandle process, + RenderViewHost* render_view_host); + virtual ~TaskManagerRendererResource(); - // TaskManagerResource methods: - std::wstring GetTitle() const; - SkBitmap GetIcon() const; + // TaskManager::Resource methods: base::ProcessHandle GetProcess() const; Type GetType() const { return RENDERER; } - TabContents* GetTabContents() const; - virtual bool ReportsCacheStats() const { return true; } virtual WebKit::WebCache::ResourceTypeStats GetWebCoreCacheStats() const; - virtual bool ReportsV8MemoryStats() const { return true; } virtual size_t GetV8MemoryAllocated() const; virtual size_t GetV8MemoryUsed() const; - // TabContents always provide the network usage. + // RenderResources always provide the network usage. bool SupportNetworkUsage() const { return true; } void SetSupportNetworkUsage() { } @@ -56,9 +56,11 @@ class TaskManagerTabContentsResource : public TaskManager::Resource { size_t v8_memory_used); private: - TabContents* tab_contents_; base::ProcessHandle process_; int pid_; + + // RenderViewHost we use to fetch stats. + RenderViewHost* render_view_host_; // The stats_ field holds information about resource usage in the renderer // process and so it is updated asynchronously by the Refresh() call. WebKit::WebCache::ResourceTypeStats stats_; @@ -70,6 +72,22 @@ class TaskManagerTabContentsResource : public TaskManager::Resource { size_t v8_memory_used_; bool pending_v8_memory_allocated_update_; + DISALLOW_COPY_AND_ASSIGN(TaskManagerRendererResource); +}; + +class TaskManagerTabContentsResource : public TaskManagerRendererResource { + public: + explicit TaskManagerTabContentsResource(TabContents* tab_contents); + ~TaskManagerTabContentsResource(); + + // TaskManager::Resource methods: + std::wstring GetTitle() const; + SkBitmap GetIcon() const; + TabContents* GetTabContents() const; + + private: + TabContents* tab_contents_; + DISALLOW_COPY_AND_ASSIGN(TaskManagerTabContentsResource); }; @@ -114,6 +132,76 @@ class TaskManagerTabContentsResourceProvider DISALLOW_COPY_AND_ASSIGN(TaskManagerTabContentsResourceProvider); }; +class TaskManagerBackgroundContentsResource + : public TaskManagerRendererResource { + public: + TaskManagerBackgroundContentsResource( + BackgroundContents* background_contents, + const std::wstring& application_name); + ~TaskManagerBackgroundContentsResource(); + + // TaskManager::Resource methods: + std::wstring GetTitle() const; + const std::wstring& application_name() const { return application_name_; } + SkBitmap GetIcon() const; + + private: + BackgroundContents* background_contents_; + + std::wstring application_name_; + + // The icon painted for BackgroundContents. + // TODO(atwilson): Use the favicon when there's a way to get the favicon for + // BackgroundContents. + static SkBitmap* default_icon_; + + DISALLOW_COPY_AND_ASSIGN(TaskManagerBackgroundContentsResource); +}; + +class TaskManagerBackgroundContentsResourceProvider + : public TaskManager::ResourceProvider, + public NotificationObserver { + public: + explicit TaskManagerBackgroundContentsResourceProvider( + TaskManager* task_manager); + + virtual TaskManager::Resource* GetResource(int origin_pid, + int render_process_host_id, + int routing_id); + virtual void StartUpdating(); + virtual void StopUpdating(); + + // NotificationObserver method: + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + private: + virtual ~TaskManagerBackgroundContentsResourceProvider(); + + void Add(BackgroundContents* background_contents, const std::wstring& title); + void Remove(BackgroundContents* background_contents); + + void AddToTaskManager(BackgroundContents* background_contents, + const std::wstring& title); + + // Whether we are currently reporting to the task manager. Used to ignore + // notifications sent after StopUpdating(). + bool updating_; + + TaskManager* task_manager_; + + // Maps the actual resources (the BackgroundContents) to the Task Manager + // resources. + std::map<BackgroundContents*, TaskManagerBackgroundContentsResource*> + resources_; + + // A scoped container for notification registries. + NotificationRegistrar registrar_; + + DISALLOW_COPY_AND_ASSIGN(TaskManagerBackgroundContentsResourceProvider); +}; + class TaskManagerChildProcessResource : public TaskManager::Resource { public: explicit TaskManagerChildProcessResource(const ChildProcessInfo& child_proc); diff --git a/chrome/test/testing_profile.h b/chrome/test/testing_profile.h index 7afcf01..503c25c 100644 --- a/chrome/test/testing_profile.h +++ b/chrome/test/testing_profile.h @@ -274,7 +274,7 @@ class TestingProfile : public Profile { virtual NTPResourceCache* GetNTPResourceCache(); virtual DesktopNotificationService* GetDesktopNotificationService(); - virtual BackgroundContentsService* GetBackgroundContentsService() { + virtual BackgroundContentsService* GetBackgroundContentsService() const { return NULL; } virtual StatusTray* GetStatusTray() { |