summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoratwilson@chromium.org <atwilson@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-06 22:02:04 +0000
committeratwilson@chromium.org <atwilson@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-06 22:02:04 +0000
commitccb55cf5b25fd67078e51b924cf6e694d7374376 (patch)
treeb04cd09754ea2cdc2ee6d333166de81688a97c8e
parent42695f09883e29aa167b79ad75263e3d611dce2d (diff)
downloadchromium_src-ccb55cf5b25fd67078e51b924cf6e694d7374376.zip
chromium_src-ccb55cf5b25fd67078e51b924cf6e694d7374376.tar.gz
chromium_src-ccb55cf5b25fd67078e51b924cf6e694d7374376.tar.bz2
Initial implementation of status tray functionality (mac-only, currently).
Added Mac implementation of StatusIcon, and added a simple click callback that displays the "extensions" tab when clicked on, to allow us to dogfood long-lived extensions. BUG=37375 Review URL: http://codereview.chromium.org/661454 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@40847 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/app/theme/theme_resources.grd2
-rw-r--r--chrome/browser/browser.cc11
-rw-r--r--chrome/browser/browser.h5
-rw-r--r--chrome/browser/browser_init.cc12
-rw-r--r--chrome/browser/browser_process.h5
-rw-r--r--chrome/browser/browser_process_impl.cc6
-rw-r--r--chrome/browser/browser_process_impl.h11
-rw-r--r--chrome/browser/cocoa/status_icons/status_icon_mac.h48
-rw-r--r--chrome/browser/cocoa/status_icons/status_icon_mac.mm95
-rw-r--r--chrome/browser/cocoa/status_icons/status_icon_mac_unittest.mm57
-rw-r--r--chrome/browser/status_icons/status_icon.h39
-rw-r--r--chrome/browser/status_icons/status_tray.cc40
-rw-r--r--chrome/browser/status_icons/status_tray.h47
-rw-r--r--chrome/browser/status_icons/status_tray_manager.cc63
-rw-r--r--chrome/browser/status_icons/status_tray_manager.h30
-rw-r--r--chrome/browser/status_icons/status_tray_unittest.cc56
-rwxr-xr-xchrome/chrome_browser.gypi7
-rw-r--r--chrome/chrome_tests.gypi2
-rw-r--r--chrome/test/testing_browser_process.h4
19 files changed, 534 insertions, 6 deletions
diff --git a/chrome/app/theme/theme_resources.grd b/chrome/app/theme/theme_resources.grd
index 783af7e..766d7f0 100644
--- a/chrome/app/theme/theme_resources.grd
+++ b/chrome/app/theme/theme_resources.grd
@@ -337,6 +337,7 @@
<include name="IDR_WIZARD_ICON_RTL" file="google_chrome/wizard_icon_rtl.png" type="BINDATA" />
<include name="IDR_PRODUCT_ICON_32" file="google_chrome/google_chrome_icon_32.png" type="BINDATA" />
<include name="IDR_INFOBAR_TRANSLATE" file="google_chrome/infobar_translate.png" type="BINDATA" />
+ <include name="IDR_STATUS_TRAY_ICON" file="google_chrome/product_logo_22.png" type="BINDATA" />
</if>
<if expr="not pp_ifdef('_google_chrome')">
@@ -352,6 +353,7 @@
<include name="IDR_WIZARD_ICON_RTL" file="chromium/wizard_icon_rtl.png" type="BINDATA" />
<include name="IDR_PRODUCT_ICON_32" file="chromium/chromium_icon_32.png" type="BINDATA" />
<include name="IDR_INFOBAR_TRANSLATE" file="chromium/infobar_translate.png" type="BINDATA" />
+ <include name="IDR_STATUS_TRAY_ICON" file="chromium/product_logo_22.png" type="BINDATA" />
</if>
<if expr="pp_ifdef('chromeos') or pp_ifdef('toolkit_views')">
diff --git a/chrome/browser/browser.cc b/chrome/browser/browser.cc
index 40d1072..344a660 100644
--- a/chrome/browser/browser.cc
+++ b/chrome/browser/browser.cc
@@ -406,19 +406,20 @@ void Browser::OpenDownloadsWindow(Profile* profile) {
}
// static
-void Browser::OpenExtensionsWindow(Profile* profile) {
+void Browser::OpenHelpWindow(Profile* profile) {
Browser* browser = Browser::Create(profile);
- browser->ShowExtensionsTab();
+ browser->OpenHelpTab();
browser->window()->Show();
}
+#endif
// static
-void Browser::OpenHelpWindow(Profile* profile) {
+void Browser::OpenExtensionsWindow(Profile* profile) {
Browser* browser = Browser::Create(profile);
- browser->OpenHelpTab();
+ browser->ShowExtensionsTab();
browser->window()->Show();
}
-#endif
+
///////////////////////////////////////////////////////////////////////////////
// Browser, State Storage and Retrieval for UI:
diff --git a/chrome/browser/browser.h b/chrome/browser/browser.h
index 991f9d4..f8abf95 100644
--- a/chrome/browser/browser.h
+++ b/chrome/browser/browser.h
@@ -192,10 +192,13 @@ class Browser : public TabStripModelDelegate,
// no windows).
static void OpenHistoryWindow(Profile* profile);
static void OpenDownloadsWindow(Profile* profile);
- static void OpenExtensionsWindow(Profile* profile);
static void OpenHelpWindow(Profile* profile);
#endif
+ // Opens a window with the extensions tab in it - needed by long-lived
+ // extensions which may run with no windows open.
+ static void OpenExtensionsWindow(Profile* profile);
+
// State Storage and Retrieval for UI ///////////////////////////////////////
// Save and restore the window position.
diff --git a/chrome/browser/browser_init.cc b/chrome/browser/browser_init.cc
index fe1866f..9f92ad8 100644
--- a/chrome/browser/browser_init.cc
+++ b/chrome/browser/browser_init.cc
@@ -33,6 +33,7 @@
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/tab_contents/tab_contents_view.h"
#include "chrome/browser/net/url_fixer_upper.h"
+#include "chrome/browser/status_icons/status_tray_manager.h"
#include "chrome/browser/user_data_manager.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
@@ -389,6 +390,17 @@ bool BrowserInit::LaunchBrowser(
lib->AddObserver(observe);
}
#endif
+#if defined(OS_MACOSX)
+ // TODO(atwilson): Status tray UI is currently only supported on the mac
+ // (http://crbug.com/37375).
+ if (command_line.HasSwitch(switches::kLongLivedExtensions)) {
+ // Create status icons
+ StatusTrayManager* tray = g_browser_process->status_tray_manager();
+ if (tray)
+ tray->Init(profile);
+ }
+#endif
+
return true;
}
diff --git a/chrome/browser/browser_process.h b/chrome/browser/browser_process.h
index 52b7578..675b477 100644
--- a/chrome/browser/browser_process.h
+++ b/chrome/browser/browser_process.h
@@ -29,6 +29,7 @@ class PrefService;
class ProfileManager;
class DebuggerWrapper;
class ResourceDispatcherHost;
+class StatusTrayManager;
class SuspendController;
class ThumbnailGenerator;
class WebAppInstallerService;
@@ -76,6 +77,10 @@ class BrowserProcess {
// Returns the manager for desktop notifications.
virtual NotificationUIManager* notification_ui_manager() = 0;
+ // Returns the status tray manager (provides APIs for manipulating status
+ // icons).
+ virtual StatusTrayManager* status_tray_manager() = 0;
+
// Returns the thread that we perform I/O coordination on (network requests,
// communication with renderers, etc.
// NOTE: You should ONLY use this to pass to IPC or other objects which must
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc
index f3cb9700..c510336 100644
--- a/chrome/browser/browser_process_impl.cc
+++ b/chrome/browser/browser_process_impl.cc
@@ -38,6 +38,7 @@
#include "chrome/browser/renderer_host/render_process_host.h"
#include "chrome/browser/renderer_host/resource_dispatcher_host.h"
#include "chrome/browser/safe_browsing/safe_browsing_service.h"
+#include "chrome/browser/status_icons/status_tray_manager.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
@@ -431,6 +432,11 @@ void BrowserProcessImpl::SetApplicationLocale(const std::string& locale) {
extension_l10n_util::SetProcessLocale(locale);
}
+void BrowserProcessImpl::CreateStatusTrayManager() {
+ DCHECK(status_tray_manager_.get() == NULL);
+ status_tray_manager_.reset(new StatusTrayManager());
+}
+
// The BrowserProcess object must outlive the file thread so we use traits
// which don't do any management.
template <>
diff --git a/chrome/browser/browser_process_impl.h b/chrome/browser/browser_process_impl.h
index 50ab5a2..118400e 100644
--- a/chrome/browser/browser_process_impl.h
+++ b/chrome/browser/browser_process_impl.h
@@ -140,6 +140,13 @@ class BrowserProcessImpl : public BrowserProcess, public NonThreadSafe {
return notification_ui_manager_.get();
}
+ virtual StatusTrayManager* status_tray_manager() {
+ DCHECK(CalledOnValidThread());
+ if (!status_tray_manager_.get())
+ CreateStatusTrayManager();
+ return status_tray_manager_.get();
+ }
+
virtual IconManager* icon_manager() {
DCHECK(CalledOnValidThread());
if (!created_icon_manager_)
@@ -235,6 +242,7 @@ class BrowserProcessImpl : public BrowserProcess, public NonThreadSafe {
void CreateGoogleURLTracker();
void CreateIntranetRedirectDetector();
void CreateNotificationUIManager();
+ void CreateStatusTrayManager();
#if defined(IPC_MESSAGE_LOG_ENABLED)
void SetIPCLoggingEnabledForChildProcesses(bool enabled);
@@ -288,6 +296,9 @@ class BrowserProcessImpl : public BrowserProcess, public NonThreadSafe {
bool created_notification_ui_manager_;
scoped_ptr<NotificationUIManager> notification_ui_manager_;
+ // Manager for status tray.
+ scoped_ptr<StatusTrayManager> status_tray_manager_;
+
scoped_ptr<AutomationProviderList> automation_provider_list_;
scoped_ptr<GoogleURLTracker> google_url_tracker_;
diff --git a/chrome/browser/cocoa/status_icons/status_icon_mac.h b/chrome/browser/cocoa/status_icons/status_icon_mac.h
new file mode 100644
index 0000000..d606429
--- /dev/null
+++ b/chrome/browser/cocoa/status_icons/status_icon_mac.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2010 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.
+
+#ifndef CHROME_BROWSER_COCOA_STATUS_ICONS_STATUS_ICON_MAC_H_
+#define CHROME_BROWSER_COCOA_STATUS_ICONS_STATUS_ICON_MAC_H_
+
+#include <vector>
+
+#import <Cocoa/Cocoa.h>
+
+#include "base/scoped_nsobject.h"
+#include "base/string16.h"
+#include "chrome/browser/status_icons/status_icon.h"
+
+class SkBitmap;
+@class NSStatusItem;
+@class StatusItemController;
+
+class StatusIconMac : public StatusIcon {
+ public:
+ StatusIconMac();
+ virtual ~StatusIconMac();
+
+ // Sets the image associated with this status icon.
+ virtual void SetImage(const SkBitmap& image);
+
+ // Sets the hover text for this status icon.
+ virtual void SetToolTip(const string16& tool_tip);
+
+ // Adds/removes a observer for status bar events.
+ virtual void AddObserver(StatusIcon::StatusIconObserver* observer);
+ virtual void RemoveObserver(StatusIcon::StatusIconObserver* observer);
+
+ // Called back if the user clicks on the status bar icon.
+ void HandleClick();
+
+ private:
+ // Getter for item_ that allows lazy initialization.
+ NSStatusItem* item();
+ scoped_nsobject<NSStatusItem> item_;
+
+ scoped_nsobject<StatusItemController> controller_;
+ std::vector<StatusIcon::StatusIconObserver*> observers_;
+};
+
+
+#endif // CHROME_BROWSER_COCOA_STATUS_ICONS_STATUS_ICON_MAC_H_
diff --git a/chrome/browser/cocoa/status_icons/status_icon_mac.mm b/chrome/browser/cocoa/status_icons/status_icon_mac.mm
new file mode 100644
index 0000000..201b6d4
--- /dev/null
+++ b/chrome/browser/cocoa/status_icons/status_icon_mac.mm
@@ -0,0 +1,95 @@
+// Copyright (c) 2010 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/cocoa/status_icons/status_icon_mac.h"
+
+#include "base/logging.h"
+#include "base/sys_string_conversions.h"
+#include "skia/ext/skia_utils_mac.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+@interface StatusItemController : NSObject {
+ StatusIconMac* statusIcon_; // weak
+}
+- initWithIcon:(StatusIconMac*)icon;
+- (void)handleClick:(id)sender;
+
+@end // @interface StatusItemController
+
+@implementation StatusItemController
+
+- (id)initWithIcon:(StatusIconMac*)icon {
+ statusIcon_ = icon;
+ return self;
+}
+
+- (void)handleClick:(id)sender {
+ // Pass along the click notification to our owner.
+ DCHECK(statusIcon_);
+ statusIcon_->HandleClick();
+}
+
+@end
+
+StatusIconMac::StatusIconMac()
+ : item_(NULL) {
+ controller_.reset([[StatusItemController alloc] initWithIcon:this]);
+}
+
+StatusIconMac::~StatusIconMac() {
+ // Remove the status item from the status bar.
+ if (item_)
+ [[NSStatusBar systemStatusBar] removeStatusItem:item_];
+}
+
+NSStatusItem* StatusIconMac::item() {
+ if (!item_.get()) {
+ // Create a new status item.
+ item_.reset([[[NSStatusBar systemStatusBar]
+ statusItemWithLength:NSSquareStatusItemLength] retain]);
+ [item_ setEnabled:YES];
+ [item_ setTarget:controller_];
+ [item_ setAction:@selector(handleClick:)];
+ }
+ return item_.get();
+}
+
+void StatusIconMac::SetImage(const SkBitmap& bitmap) {
+ if (!bitmap.isNull()) {
+ NSImage* image = gfx::SkBitmapToNSImage(bitmap);
+ if (image)
+ [item() setImage:image];
+ }
+}
+
+void StatusIconMac::SetToolTip(const string16& tool_tip) {
+ [item() setToolTip:base::SysUTF16ToNSString(tool_tip)];
+}
+
+void StatusIconMac::AddObserver(StatusIconObserver* observer) {
+ observers_.push_back(observer);
+}
+
+void StatusIconMac::RemoveObserver(StatusIconObserver* observer) {
+ std::vector<StatusIconObserver*>::iterator iter =
+ std::find(observers_.begin(), observers_.end(), observer);
+ if (iter != observers_.end())
+ observers_.erase(iter);
+}
+
+void StatusIconMac::HandleClick() {
+ // Walk observers, call callback for each one.
+ for (std::vector<StatusIconObserver*>::const_iterator iter =
+ observers_.begin();
+ iter != observers_.end();
+ ++iter) {
+ StatusIconObserver* observer = *iter;
+ observer->OnClicked();
+ }
+}
+
+// static factory method
+StatusIcon* StatusIcon::Create() {
+ return new StatusIconMac();
+}
diff --git a/chrome/browser/cocoa/status_icons/status_icon_mac_unittest.mm b/chrome/browser/cocoa/status_icons/status_icon_mac_unittest.mm
new file mode 100644
index 0000000..4a7b284
--- /dev/null
+++ b/chrome/browser/cocoa/status_icons/status_icon_mac_unittest.mm
@@ -0,0 +1,57 @@
+// Copyright (c) 2010 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 "app/resource_bundle.h"
+#include "base/string_util.h"
+#import "chrome/browser/cocoa/cocoa_test_helper.h"
+#include "chrome/browser/cocoa/status_icons/status_icon_mac.h"
+#include "grit/browser_resources.h"
+#include "grit/theme_resources.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class SkBitmap;
+
+class MockStatusIconObserver : public StatusIcon::StatusIconObserver {
+ public:
+ MOCK_METHOD0(OnClicked, void());
+};
+
+class StatusIconMacTest : public CocoaTest {
+};
+
+TEST_F(StatusIconMacTest, Create) {
+ // Create an icon, set the tool tip, then shut it down (checks for leaks).
+ scoped_ptr<StatusIcon> icon(new StatusIconMac());
+ SkBitmap* bitmap = ResourceBundle::GetSharedInstance().GetBitmapNamed(
+ IDR_STATUS_TRAY_ICON);
+ icon->SetImage(*bitmap);
+ icon->SetToolTip(ASCIIToUTF16("tool tip"));
+}
+
+TEST_F(StatusIconMacTest, ObserverAdd) {
+ // Make sure that observers are invoked when we click items.
+ StatusIconMac icon;
+ MockStatusIconObserver observer, observer2;
+ EXPECT_CALL(observer, OnClicked()).Times(2);
+ EXPECT_CALL(observer2, OnClicked());
+ icon.AddObserver(&observer);
+ icon.HandleClick();
+ icon.AddObserver(&observer2);
+ icon.HandleClick();
+ icon.RemoveObserver(&observer);
+ icon.RemoveObserver(&observer2);
+}
+
+TEST_F(StatusIconMacTest, ObserverRemove) {
+ // Make sure that observers are no longer invoked after they are removed.
+ StatusIconMac icon;
+ MockStatusIconObserver observer;
+ EXPECT_CALL(observer, OnClicked());
+ icon.AddObserver(&observer);
+ icon.HandleClick();
+ icon.RemoveObserver(&observer);
+ icon.HandleClick();
+}
+
diff --git a/chrome/browser/status_icons/status_icon.h b/chrome/browser/status_icons/status_icon.h
new file mode 100644
index 0000000..aeefdfe
--- /dev/null
+++ b/chrome/browser/status_icons/status_icon.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2010 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.
+
+#ifndef CHROME_BROWSER_STATUS_ICONS_STATUS_ICON_H_
+#define CHROME_BROWSER_STATUS_ICONS_STATUS_ICON_H_
+
+#include "base/string16.h"
+
+class SkBitmap;
+
+class StatusIcon {
+ public:
+ // Creates a new StatusIcon.
+ static StatusIcon* Create();
+
+ virtual ~StatusIcon() {}
+
+ // Sets the image associated with this status icon.
+ virtual void SetImage(const SkBitmap& image) = 0;
+
+ // Sets the hover text for this status icon.
+ virtual void SetToolTip(const string16& tool_tip) = 0;
+
+ class StatusIconObserver {
+ public:
+ virtual ~StatusIconObserver() {}
+
+ // Called when the user clicks on the system tray icon.
+ virtual void OnClicked() = 0;
+ };
+
+ // Adds/removes a observer for status bar events.
+ virtual void AddObserver(StatusIcon::StatusIconObserver* observer) = 0;
+ virtual void RemoveObserver(StatusIcon::StatusIconObserver* observer) = 0;
+};
+
+
+#endif // CHROME_BROWSER_STATUS_ICONS_STATUS_ICON_H_
diff --git a/chrome/browser/status_icons/status_tray.cc b/chrome/browser/status_icons/status_tray.cc
new file mode 100644
index 0000000..1415babe
--- /dev/null
+++ b/chrome/browser/status_icons/status_tray.cc
@@ -0,0 +1,40 @@
+// Copyright (c) 2010 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/status_icons/status_tray.h"
+
+#include "base/stl_util-inl.h"
+#include "chrome/browser/status_icons/status_icon.h"
+
+StatusTray::StatusTray(StatusIconFactory* factory)
+ : factory_(factory) {
+}
+
+StatusTray::~StatusTray() {
+ // Walk any active status icons and delete them.
+ STLDeleteContainerPairSecondPointers(status_icons_.begin(),
+ status_icons_.end());
+}
+
+StatusIcon* StatusTray::GetStatusIcon(const string16& identifier) {
+ StatusIconMap::const_iterator iter = status_icons_.find(identifier);
+ if (iter != status_icons_.end())
+ return iter->second;
+
+ // No existing StatusIcon, create a new one.
+ StatusIcon* icon = factory_->CreateIcon();
+ if (icon)
+ status_icons_[identifier] = icon;
+ return icon;
+}
+
+void StatusTray::RemoveStatusIcon(const string16& identifier) {
+ StatusIconMap::iterator iter = status_icons_.find(identifier);
+ if (iter != status_icons_.end()) {
+ // Free the StatusIcon from the map (can't put scoped_ptr in a map, so we
+ // have to do it manually).
+ delete iter->second;
+ status_icons_.erase(iter);
+ }
+}
diff --git a/chrome/browser/status_icons/status_tray.h b/chrome/browser/status_icons/status_tray.h
new file mode 100644
index 0000000..663bfa8
--- /dev/null
+++ b/chrome/browser/status_icons/status_tray.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2010 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.
+
+#ifndef CHROME_BROWSER_STATUS_ICONS_STATUS_TRAY_H_
+#define CHROME_BROWSER_STATUS_ICONS_STATUS_TRAY_H_
+
+#include "base/hash_tables.h"
+#include "base/scoped_ptr.h"
+
+class StatusIcon;
+
+// Factory interface for creating icons to improve testability.
+class StatusIconFactory {
+ public:
+ virtual StatusIcon* CreateIcon() = 0;
+ virtual ~StatusIconFactory() { }
+};
+
+// Provides a cross-platform interface to the system's status tray, and exposes
+// APIs to add/remove icons to the tray and attach context menus.
+class StatusTray {
+ public:
+ // Takes ownership of |factory|.
+ explicit StatusTray(StatusIconFactory* factory);
+ ~StatusTray();
+
+ // Gets the current status icon associated with this identifier, or creates
+ // a new one if none exists. The StatusTray retains ownership of the
+ // StatusIcon. Returns NULL if the status tray icon could not be created.
+ StatusIcon* GetStatusIcon(const string16& identifier);
+
+ // Removes the current status icon associated with this identifier, if any.
+ void RemoveStatusIcon(const string16& identifier);
+
+ private:
+ typedef base::hash_map<string16, StatusIcon*> StatusIconMap;
+ // Map containing all active StatusIcons.
+ // Key: String identifiers (passed in to GetStatusIcon)
+ // Value: The StatusIcon associated with that identifier (strong pointer -
+ // StatusIcons are freed when the StatusTray destructor is called).
+ StatusIconMap status_icons_;
+
+ scoped_ptr<StatusIconFactory> factory_;
+};
+
+#endif // CHROME_BROWSER_STATUS_ICONS_STATUS_TRAY_H_
diff --git a/chrome/browser/status_icons/status_tray_manager.cc b/chrome/browser/status_icons/status_tray_manager.cc
new file mode 100644
index 0000000..26d0840
--- /dev/null
+++ b/chrome/browser/status_icons/status_tray_manager.cc
@@ -0,0 +1,63 @@
+// Copyright (c) 2010 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/status_icons/status_tray_manager.h"
+
+#include "app/resource_bundle.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_list.h"
+#include "chrome/browser/browser_window.h"
+#include "chrome/browser/status_icons/status_tray.h"
+#include "grit/browser_resources.h"
+#include "grit/theme_resources.h"
+
+class StatusIconFactoryImpl : public StatusIconFactory {
+ public:
+ virtual StatusIcon* CreateIcon();
+};
+
+StatusIcon* StatusIconFactoryImpl::CreateIcon() {
+#ifdef OS_MACOSX
+ return StatusIcon::Create();
+#else
+ // TODO(atwilson): Add support for non-Mac platforms.
+ return 0;
+#endif
+}
+
+
+StatusTrayManager::StatusTrayManager() {
+}
+
+StatusTrayManager::~StatusTrayManager() {
+}
+
+void StatusTrayManager::Init(Profile* profile) {
+ DCHECK(profile);
+ profile_ = profile;
+ status_tray_.reset(new StatusTray(new StatusIconFactoryImpl()));
+ StatusIcon* icon = status_tray_->GetStatusIcon(ASCIIToUTF16("chrome_main"));
+ if (icon) {
+ // Create an icon and add ourselves as a click observer on it
+ SkBitmap* bitmap = ResourceBundle::GetSharedInstance().GetBitmapNamed(
+ IDR_STATUS_TRAY_ICON);
+ icon->SetImage(*bitmap);
+ icon->AddObserver(this);
+ }
+}
+
+void StatusTrayManager::OnClicked() {
+ // When the tray icon is clicked, bring up the extensions page for now.
+ Browser* browser = BrowserList::GetLastActiveWithProfile(profile_);
+ if (browser) {
+ // Bring up the existing browser window and show the extensions tab.
+ browser->window()->Activate();
+ browser->ShowExtensionsTab();
+ } else {
+ // No windows are currently open, so open a new one.
+ Browser::OpenExtensionsWindow(profile_);
+ }
+}
diff --git a/chrome/browser/status_icons/status_tray_manager.h b/chrome/browser/status_icons/status_tray_manager.h
new file mode 100644
index 0000000..9f9d2d7
--- /dev/null
+++ b/chrome/browser/status_icons/status_tray_manager.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2010 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.
+
+#ifndef CHROME_BROWSER_STATUS_ICONS_STATUS_TRAY_MANAGER_H_
+#define CHROME_BROWSER_STATUS_ICONS_STATUS_TRAY_MANAGER_H_
+
+#include "base/scoped_ptr.h"
+#include "chrome/browser/status_icons/status_icon.h"
+
+class Profile;
+class StatusTray;
+
+// Manages the set of status tray icons and associated UI.
+class StatusTrayManager : private StatusIcon::StatusIconObserver {
+ public:
+ StatusTrayManager();
+ virtual ~StatusTrayManager();
+
+ void Init(Profile* profile);
+
+ private:
+ // StatusIcon::StatusIconObserver callbacks
+ virtual void OnClicked();
+
+ scoped_ptr<StatusTray> status_tray_;
+ Profile* profile_;
+};
+
+#endif // CHROME_BROWSER_STATUS_ICONS_STATUS_TRAY_MANAGER_H_
diff --git a/chrome/browser/status_icons/status_tray_unittest.cc b/chrome/browser/status_icons/status_tray_unittest.cc
new file mode 100644
index 0000000..01f9c30
--- /dev/null
+++ b/chrome/browser/status_icons/status_tray_unittest.cc
@@ -0,0 +1,56 @@
+// Copyright (c) 2010 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 "base/string_util.h"
+#include "chrome/browser/status_icons/status_icon.h"
+#include "chrome/browser/status_icons/status_tray.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::Return;
+
+class MockStatusIcon : public StatusIcon {
+ virtual void SetImage(const SkBitmap& image) {}
+ virtual void SetToolTip(const string16& tool_tip) {}
+ virtual void AddObserver(StatusIcon::StatusIconObserver* observer) {}
+ virtual void RemoveObserver(StatusIcon::StatusIconObserver* observer) {}
+};
+
+class TestStatusIconFactory : public StatusIconFactory {
+ public:
+ MOCK_METHOD0(CreateIcon, StatusIcon*());
+};
+
+TEST(StatusTrayTest, Create) {
+ // Check for creation and leaks.
+ TestStatusIconFactory* factory = new TestStatusIconFactory();
+ StatusTray tray(factory);
+ EXPECT_CALL(*factory, CreateIcon()).WillOnce(Return(new MockStatusIcon()));
+ tray.GetStatusIcon(ASCIIToUTF16("test"));
+}
+
+TEST(StatusTrayTest, GetIconTwice) {
+ TestStatusIconFactory* factory = new TestStatusIconFactory();
+ StatusTray tray(factory);
+ string16 id = ASCIIToUTF16("test");
+ // We should not try to create a new icon if we get the same ID twice.
+ EXPECT_CALL(*factory, CreateIcon()).WillOnce(Return(new MockStatusIcon()));
+ StatusIcon* icon = tray.GetStatusIcon(id);
+ EXPECT_EQ(icon, tray.GetStatusIcon(id));
+}
+
+TEST(StatusTrayTest, GetIconAfterRemove) {
+ TestStatusIconFactory* factory = new TestStatusIconFactory();
+ StatusTray tray(factory);
+ string16 id = ASCIIToUTF16("test");
+ EXPECT_CALL(*factory, CreateIcon()).Times(2)
+ .WillOnce(Return(new MockStatusIcon()))
+ .WillOnce(Return(new MockStatusIcon()));
+ StatusIcon* icon = tray.GetStatusIcon(id);
+ EXPECT_EQ(icon, tray.GetStatusIcon(id));
+
+ // If we remove the icon, then we should create a new one the next time in.
+ tray.RemoveStatusIcon(id);
+ tray.GetStatusIcon(id);
+}
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 2252f69..7ffacbd 100755
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -690,6 +690,8 @@
'browser/cocoa/shell_dialogs_mac.mm',
'browser/cocoa/status_bubble_mac.h',
'browser/cocoa/status_bubble_mac.mm',
+ 'browser/cocoa/status_icons/status_icon_mac.h',
+ 'browser/cocoa/status_icons/status_icon_mac.mm',
'browser/cocoa/styled_text_field.h',
'browser/cocoa/styled_text_field.mm',
'browser/cocoa/styled_text_field_cell.h',
@@ -1798,6 +1800,11 @@
'browser/ssl/ssl_policy_backend.h',
'browser/ssl/ssl_request_info.h',
'browser/status_bubble.h',
+ 'browser/status_icons/status_tray.cc',
+ 'browser/status_icons/status_tray.h',
+ 'browser/status_icons/status_tray_manager.cc',
+ 'browser/status_icons/status_tray_manager.h',
+ 'browser/status_icons/status_icon.h',
'browser/transport_security_persister.cc',
'browser/transport_security_persister.h',
'browser/sync/engine/syncapi.h',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index ad8390d..dd3a75f 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -684,6 +684,7 @@
'browser/cocoa/search_engine_list_model_unittest.mm',
'browser/cocoa/section_separator_view_unittest.mm',
'browser/cocoa/status_bubble_mac_unittest.mm',
+ 'browser/cocoa/status_icons/status_icon_mac_unittest.mm',
'browser/cocoa/styled_text_field_test_helper.h',
'browser/cocoa/styled_text_field_test_helper.mm',
'browser/cocoa/styled_text_field_unittest.mm',
@@ -841,6 +842,7 @@
'browser/shell_integration_unittest.cc',
'browser/spellchecker_platform_engine_unittest.cc',
'browser/ssl/ssl_host_state_unittest.cc',
+ 'browser/status_icons/status_tray_unittest.cc',
'browser/sync/glue/change_processor_mock.h',
'browser/sync/glue/data_type_controller_mock.h',
'browser/sync/glue/data_type_manager_impl_unittest.cc',
diff --git a/chrome/test/testing_browser_process.h b/chrome/test/testing_browser_process.h
index 690f8af..47a75e1 100644
--- a/chrome/test/testing_browser_process.h
+++ b/chrome/test/testing_browser_process.h
@@ -110,6 +110,10 @@ class TestingBrowserProcess : public BrowserProcess {
return NULL;
}
+ virtual StatusTrayManager* status_tray_manager() {
+ return NULL;
+ }
+
virtual GoogleURLTracker* google_url_tracker() {
return NULL;
}