summaryrefslogtreecommitdiffstats
path: root/chrome/browser/sidebar
diff options
context:
space:
mode:
authorbrettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-19 18:05:56 +0000
committerbrettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-19 18:05:56 +0000
commit8b8e7c9bce4198a4ee2865d9dfce6e8baa173ad3 (patch)
tree938304b8e1be417c1b9c73d7463dacbc1da79843 /chrome/browser/sidebar
parentf2c4ee3627e6039fd42bd7c0c93e902b72653449 (diff)
downloadchromium_src-8b8e7c9bce4198a4ee2865d9dfce6e8baa173ad3.zip
chromium_src-8b8e7c9bce4198a4ee2865d9dfce6e8baa173ad3.tar.gz
chromium_src-8b8e7c9bce4198a4ee2865d9dfce6e8baa173ad3.tar.bz2
Initial version of chrome.experimental.sidebar extension API.
BUG=51084 TEST=Run interactive_ui_tests and browser_tests. New: - sidebar Extension API (design doc: https://docs.google.com/a/google.com/Doc?docid=0AV4Qg3xyZ8RQZGZtbWIydDJfNWc0eHJtbmRm&hl=en); - Sidebar panel in Chrome browser view; Original review=http://codereview.chromium.org/2836040/show Patch by alekseys@google.com git-svn-id: svn://svn.chromium.org/chrome/trunk/src@56716 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/sidebar')
-rw-r--r--chrome/browser/sidebar/sidebar_container.cc73
-rw-r--r--chrome/browser/sidebar/sidebar_container.h141
-rw-r--r--chrome/browser/sidebar/sidebar_manager.cc323
-rw-r--r--chrome/browser/sidebar/sidebar_manager.h156
-rw-r--r--chrome/browser/sidebar/sidebar_test.cc177
5 files changed, 870 insertions, 0 deletions
diff --git a/chrome/browser/sidebar/sidebar_container.cc b/chrome/browser/sidebar/sidebar_container.cc
new file mode 100644
index 0000000..4c73110
--- /dev/null
+++ b/chrome/browser/sidebar/sidebar_container.cc
@@ -0,0 +1,73 @@
+// 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/sidebar/sidebar_container.h"
+
+#include "chrome/browser/renderer_host/render_view_host.h"
+#include "chrome/browser/tab_contents/navigation_controller.h"
+#include "chrome/browser/tab_contents/navigation_entry.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/tab_contents/tab_contents_view.h"
+#include "chrome/common/bindings_policy.h"
+#include "googleurl/src/gurl.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+SidebarContainer::SidebarContainer(TabContents* tab,
+ const std::string& content_id,
+ Delegate* delegate)
+ : tab_(tab),
+ content_id_(content_id),
+ delegate_(delegate),
+ icon_(new SkBitmap) {
+ // Create TabContents for sidebar.
+ sidebar_contents_.reset(
+ new TabContents(tab->profile(), NULL, MSG_ROUTING_NONE, NULL));
+ sidebar_contents_->render_view_host()->set_is_extension_process(true);
+ sidebar_contents_->render_view_host()->AllowBindings(
+ BindingsPolicy::EXTENSION);
+ sidebar_contents_->set_delegate(this);
+}
+
+SidebarContainer::~SidebarContainer() {
+}
+
+void SidebarContainer::SidebarClosing() {
+ delegate_->UpdateSidebar(this);
+}
+
+int SidebarContainer::GetTabId() const {
+ return tab_->controller().session_id().id();
+}
+
+void SidebarContainer::Show() {
+ delegate_->UpdateSidebar(this);
+}
+
+void SidebarContainer::Expand() {
+ delegate_->UpdateSidebar(this);
+ sidebar_contents_->view()->SetInitialFocus();
+}
+
+void SidebarContainer::Collapse() {
+ delegate_->UpdateSidebar(this);
+}
+
+void SidebarContainer::Navigate(const GURL& url) {
+ DCHECK(sidebar_contents_.get());
+ // TODO(alekseys): add a progress UI.
+ sidebar_contents_->controller().LoadURL(
+ url, GURL(), PageTransition::START_PAGE);
+}
+
+void SidebarContainer::SetBadgeText(const string16& badge_text) {
+ badge_text_ = badge_text;
+}
+
+void SidebarContainer::SetIcon(const SkBitmap& bitmap) {
+ *icon_ = bitmap;
+}
+
+void SidebarContainer::SetTitle(const string16& title) {
+ title_ = title;
+}
diff --git a/chrome/browser/sidebar/sidebar_container.h b/chrome/browser/sidebar/sidebar_container.h
new file mode 100644
index 0000000..152331d
--- /dev/null
+++ b/chrome/browser/sidebar/sidebar_container.h
@@ -0,0 +1,141 @@
+// 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_SIDEBAR_SIDEBAR_CONTAINER_H_
+#define CHROME_BROWSER_SIDEBAR_SIDEBAR_CONTAINER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "base/string16.h"
+#include "chrome/browser/tab_contents/tab_contents_delegate.h"
+
+class BrowserWindow;
+class Profile;
+class RenderViewHost;
+class SkBitmap;
+class TabContents;
+
+///////////////////////////////////////////////////////////////////////////////
+// SidebarContainer
+//
+// Stores one particular sidebar state: sidebar's content, its content id,
+// tab it is linked to, mini tab icon, title etc.
+//
+class SidebarContainer
+ : public TabContentsDelegate {
+ public:
+ // Interface to implement to listen for sidebar update notification.
+ class Delegate {
+ public:
+ Delegate() {}
+ virtual ~Delegate() {}
+ virtual void UpdateSidebar(SidebarContainer* host) = 0;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Delegate);
+ };
+
+ SidebarContainer(TabContents* tab, const std::string& content_id,
+ Delegate* delegate);
+ virtual ~SidebarContainer();
+
+ // Called right before destroying this sidebar.
+ // Does all the necessary cleanup.
+ void SidebarClosing();
+
+ // Returns id of the tab this sidebar is linked to.
+ int GetTabId() const;
+
+ // Returns sidebar's content id.
+ const std::string& content_id() const { return content_id_; }
+
+ // Returns TabContents sidebar is linked to.
+ TabContents* tab_contents() const { return tab_; }
+
+ // Returns sidebar's TabContents.
+ TabContents* sidebar_contents() const { return sidebar_contents_.get(); }
+
+ // Accessor for the badge text.
+ const string16& badge_text() const { return badge_text_; }
+
+ // Accessor for the icon.
+ const SkBitmap& icon() const { return *icon_; }
+
+ // Accessor for the title.
+ const string16& title() const { return title_; }
+
+ // Functions supporting chrome.experimental.sidebar API.
+
+ // Notifies hosting window that this sidebar was expanded.
+ void Show();
+
+ // Notifies hosting window that this sidebar was expanded.
+ void Expand();
+
+ // Notifies hosting window that this sidebar was collapsed.
+ void Collapse();
+
+ // Navigates sidebar contents to the |url|.
+ void Navigate(const GURL& url);
+
+ // Changes sidebar's badge text.
+ void SetBadgeText(const string16& badge_text);
+
+ // Changes sidebar's icon.
+ void SetIcon(const SkBitmap& bitmap);
+
+ // Changes sidebar's title.
+ void SetTitle(const string16& title);
+
+ private:
+ // Overridden from TabContentsDelegate.
+ virtual void OpenURLFromTab(TabContents* source,
+ const GURL& url,
+ const GURL& referrer,
+ WindowOpenDisposition disposition,
+ PageTransition::Type transition) {}
+ virtual void NavigationStateChanged(const TabContents* source,
+ unsigned changed_flags) {}
+ virtual void AddNewContents(TabContents* source,
+ TabContents* new_contents,
+ WindowOpenDisposition disposition,
+ const gfx::Rect& initial_pos,
+ bool user_gesture) {}
+ virtual void ActivateContents(TabContents* contents) {}
+ virtual void LoadingStateChanged(TabContents* source) {}
+ virtual void CloseContents(TabContents* source) {}
+ virtual void MoveContents(TabContents* source, const gfx::Rect& pos) {}
+ virtual bool IsPopup(TabContents* source) { return false; }
+ virtual void URLStarredChanged(TabContents* source, bool starred) {}
+ virtual void UpdateTargetURL(TabContents* source, const GURL& url) {}
+ virtual void ToolbarSizeChanged(TabContents* source, bool is_animating) {}
+
+ // Contents of the tab this sidebar is linked to.
+ TabContents* tab_;
+
+ // Sidebar's content id. There might be more than one sidebar liked to each
+ // particular tab and they are identified by their unique content id.
+ const std::string content_id_;
+
+ // Sidebar update notification listener.
+ Delegate* delegate_;
+
+ // Sidebar contents.
+ scoped_ptr<TabContents> sidebar_contents_;
+
+ // Badge text displayed on the sidebar's mini tab.
+ string16 badge_text_;
+
+ // Icon displayed on the sidebar's mini tab.
+ scoped_ptr<SkBitmap> icon_;
+
+ // Sidebar's title, displayed as a tooltip for sidebar's mini tab.
+ string16 title_;
+
+ DISALLOW_COPY_AND_ASSIGN(SidebarContainer);
+};
+
+#endif // CHROME_BROWSER_SIDEBAR_SIDEBAR_CONTAINER_H_
+
diff --git a/chrome/browser/sidebar/sidebar_manager.cc b/chrome/browser/sidebar/sidebar_manager.cc
new file mode 100644
index 0000000..5ec9e12
--- /dev/null
+++ b/chrome/browser/sidebar/sidebar_manager.cc
@@ -0,0 +1,323 @@
+// 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/sidebar/sidebar_manager.h"
+
+#include <vector>
+
+#include "base/command_line.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/extensions/extension_sidebar_api.h"
+#include "chrome/browser/pref_service.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/sidebar/sidebar_container.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/pref_names.h"
+#include "googleurl/src/gurl.h"
+
+// static
+SidebarManager* SidebarManager::GetInstance() {
+ return g_browser_process->sidebar_manager();
+}
+
+// static
+bool SidebarManager::IsSidebarAllowed() {
+ return CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableExperimentalExtensionApis);
+}
+
+SidebarManager::SidebarManager() {
+}
+
+SidebarContainer* SidebarManager::GetActiveSidebarContainerFor(
+ TabContents* tab) {
+ TabToSidebarHostMap::iterator it = tab_to_sidebar_host_.find(tab);
+ if (it == tab_to_sidebar_host_.end())
+ return NULL;
+ if (it->second.active_content_id.empty())
+ return NULL;
+ ContentIdToSidebarHostMap::iterator host_it =
+ it->second.content_id_to_sidebar_host.find(it->second.active_content_id);
+ DCHECK(host_it != it->second.content_id_to_sidebar_host.end());
+ return host_it->second;
+}
+
+SidebarContainer* SidebarManager::GetSidebarContainerFor(
+ TabContents* tab, const std::string& content_id) {
+ DCHECK(!content_id.empty());
+ TabToSidebarHostMap::iterator it = tab_to_sidebar_host_.find(tab);
+ if (it == tab_to_sidebar_host_.end())
+ return NULL;
+ ContentIdToSidebarHostMap::iterator host_it =
+ it->second.content_id_to_sidebar_host.find(content_id);
+ if (host_it == it->second.content_id_to_sidebar_host.end())
+ return NULL;
+ return host_it->second;
+}
+
+TabContents* SidebarManager::GetSidebarTabContents(
+ TabContents* tab, const std::string& content_id) {
+ DCHECK(!content_id.empty());
+ SidebarContainer* sidebar_host = GetSidebarContainerFor(tab, content_id);
+ if (!sidebar_host)
+ return NULL;
+ return sidebar_host->sidebar_contents();
+}
+
+void SidebarManager::NotifyStateChanges(
+ TabContents* was_active_sidebar_contents,
+ TabContents* active_sidebar_contents) {
+ if (was_active_sidebar_contents == active_sidebar_contents)
+ return;
+
+ SidebarContainer* was_active_host =
+ was_active_sidebar_contents == NULL ? NULL :
+ FindSidebarContainerFor(was_active_sidebar_contents);
+ SidebarContainer* active_host =
+ active_sidebar_contents == NULL ? NULL :
+ FindSidebarContainerFor(active_sidebar_contents);
+
+ if (was_active_host != NULL) {
+ ExtensionSidebarEventRouter::OnStateChanged(
+ was_active_sidebar_contents->profile(),
+ was_active_host->GetTabId(), was_active_host->content_id(),
+ extension_sidebar_constants::kShownState);
+ }
+
+ if (active_host != NULL) {
+ ExtensionSidebarEventRouter::OnStateChanged(
+ active_sidebar_contents->profile(),
+ active_host->GetTabId(), active_host->content_id(),
+ extension_sidebar_constants::kActiveState);
+ }
+}
+
+void SidebarManager::ShowSidebar(TabContents* tab,
+ const std::string& content_id) {
+ DCHECK(!content_id.empty());
+ SidebarContainer* host = GetSidebarContainerFor(tab, content_id);
+ if (!host) {
+ host = new SidebarContainer(tab, content_id, this);
+ RegisterSidebarContainerFor(tab, host);
+ }
+
+ host->Show();
+
+ ExtensionSidebarEventRouter::OnStateChanged(
+ tab->profile(), host->GetTabId(), content_id,
+ extension_sidebar_constants::kShownState);
+}
+
+void SidebarManager::ExpandSidebar(TabContents* tab,
+ const std::string& content_id) {
+ DCHECK(!content_id.empty());
+ TabToSidebarHostMap::iterator it = tab_to_sidebar_host_.find(tab);
+ if (it == tab_to_sidebar_host_.end())
+ return;
+ // If it's already active, bail out.
+ if (it->second.active_content_id == content_id)
+ return;
+
+ SidebarContainer* host = GetSidebarContainerFor(tab, content_id);
+ DCHECK(host);
+ if (!host)
+ return;
+ it->second.active_content_id = content_id;
+
+ host->Expand();
+}
+
+void SidebarManager::CollapseSidebar(TabContents* tab,
+ const std::string& content_id) {
+ DCHECK(!content_id.empty());
+ TabToSidebarHostMap::iterator it = tab_to_sidebar_host_.find(tab);
+ if (it == tab_to_sidebar_host_.end())
+ return;
+ // If it's not the one active now, bail out.
+ if (it->second.active_content_id != content_id)
+ return;
+
+ SidebarContainer* host = GetSidebarContainerFor(tab, content_id);
+ DCHECK(host);
+ if (!host)
+ return;
+ it->second.active_content_id.clear();
+
+ host->Collapse();
+}
+
+void SidebarManager::HideSidebar(TabContents* tab,
+ const std::string& content_id) {
+ DCHECK(!content_id.empty());
+ TabToSidebarHostMap::iterator it = tab_to_sidebar_host_.find(tab);
+ if (it == tab_to_sidebar_host_.end())
+ return;
+ if (it->second.active_content_id == content_id)
+ it->second.active_content_id.clear();
+
+ SidebarContainer* host = GetSidebarContainerFor(tab, content_id);
+ DCHECK(host);
+ int tab_id = host->GetTabId();
+
+ UnregisterSidebarContainerFor(tab, content_id);
+
+ ExtensionSidebarEventRouter::OnStateChanged(
+ tab->profile(), tab_id, content_id,
+ extension_sidebar_constants::kHiddenState);
+}
+
+void SidebarManager::NavigateSidebar(TabContents* tab,
+ const std::string& content_id,
+ const GURL& url) {
+ DCHECK(!content_id.empty());
+ SidebarContainer* host = GetSidebarContainerFor(tab, content_id);
+ if (!host)
+ return;
+
+ host->Navigate(url);
+}
+
+void SidebarManager::SetSidebarBadgeText(
+ TabContents* tab, const std::string& content_id,
+ const string16& badge_text) {
+ SidebarContainer* host = GetSidebarContainerFor(tab, content_id);
+ if (!host)
+ return;
+ host->SetBadgeText(badge_text);
+}
+
+void SidebarManager::SetSidebarIcon(
+ TabContents* tab, const std::string& content_id,
+ const SkBitmap& bitmap) {
+ SidebarContainer* host = GetSidebarContainerFor(tab, content_id);
+ if (!host)
+ return;
+ host->SetIcon(bitmap);
+}
+
+void SidebarManager::SetSidebarTitle(
+ TabContents* tab, const std::string& content_id,
+ const string16& title) {
+ SidebarContainer* host = GetSidebarContainerFor(tab, content_id);
+ if (!host)
+ return;
+ host->SetTitle(title);
+}
+
+SidebarManager::~SidebarManager() {
+ DCHECK(tab_to_sidebar_host_.empty());
+ DCHECK(sidebar_host_to_tab_.empty());
+}
+
+void SidebarManager::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (type == NotificationType::TAB_CONTENTS_DESTROYED) {
+ HideAllSidebars(Source<TabContents>(source).ptr());
+ } else {
+ NOTREACHED() << "Got a notification we didn't register for!";
+ }
+}
+
+void SidebarManager::UpdateSidebar(SidebarContainer* host) {
+ NotificationService::current()->Notify(
+ NotificationType::SIDEBAR_CHANGED,
+ Source<SidebarManager>(this),
+ Details<SidebarContainer>(host));
+}
+
+void SidebarManager::HideAllSidebars(TabContents* tab) {
+ TabToSidebarHostMap::iterator tab_it = tab_to_sidebar_host_.find(tab);
+ if (tab_it == tab_to_sidebar_host_.end())
+ return;
+ const ContentIdToSidebarHostMap& hosts =
+ tab_it->second.content_id_to_sidebar_host;
+
+ std::vector<std::string> content_ids;
+ for (ContentIdToSidebarHostMap::const_iterator it = hosts.begin();
+ it != hosts.end(); ++it) {
+ content_ids.push_back(it->first);
+ }
+
+ for (std::vector<std::string>::iterator it = content_ids.begin();
+ it != content_ids.end(); ++it) {
+ HideSidebar(tab, *it);
+ }
+}
+
+SidebarContainer* SidebarManager::FindSidebarContainerFor(
+ TabContents* sidebar_contents) {
+ for (SidebarHostToTabMap::iterator it = sidebar_host_to_tab_.begin();
+ it != sidebar_host_to_tab_.end();
+ ++it) {
+ if (sidebar_contents == it->first->sidebar_contents())
+ return it->first;
+ }
+ return NULL;
+}
+
+void SidebarManager::RegisterSidebarContainerFor(
+ TabContents* tab, SidebarContainer* sidebar_host) {
+ DCHECK(!GetSidebarContainerFor(tab, sidebar_host->content_id()));
+
+ // If it's a first sidebar for this tab, register destroy notification.
+ if (tab_to_sidebar_host_.find(tab) == tab_to_sidebar_host_.end()) {
+ registrar_.Add(this,
+ NotificationType::TAB_CONTENTS_DESTROYED,
+ Source<TabContents>(tab));
+ }
+
+ BindSidebarHost(tab, sidebar_host);
+}
+
+void SidebarManager::UnregisterSidebarContainerFor(
+ TabContents* tab, const std::string& content_id) {
+ SidebarContainer* host = GetSidebarContainerFor(tab, content_id);
+ DCHECK(host);
+ if (!host)
+ return;
+
+ UnbindSidebarHost(tab, host);
+
+ // If there's no more sidebars linked to this tab, unsubscribe.
+ if (tab_to_sidebar_host_.find(tab) == tab_to_sidebar_host_.end()) {
+ registrar_.Remove(this,
+ NotificationType::TAB_CONTENTS_DESTROYED,
+ Source<TabContents>(tab));
+ }
+
+ // Issue tab closing event post unbound.
+ host->SidebarClosing();
+ // Destroy sidebar container.
+ delete host;
+}
+
+void SidebarManager::BindSidebarHost(TabContents* tab,
+ SidebarContainer* sidebar_host) {
+ const std::string& content_id = sidebar_host->content_id();
+
+ DCHECK(GetSidebarContainerFor(tab, content_id) == NULL);
+ DCHECK(sidebar_host_to_tab_.find(sidebar_host) ==
+ sidebar_host_to_tab_.end());
+
+ tab_to_sidebar_host_[tab].content_id_to_sidebar_host[content_id] =
+ sidebar_host;
+ sidebar_host_to_tab_[sidebar_host] = tab;
+}
+
+void SidebarManager::UnbindSidebarHost(TabContents* tab,
+ SidebarContainer* sidebar_host) {
+ const std::string& content_id = sidebar_host->content_id();
+
+ DCHECK(GetSidebarContainerFor(tab, content_id) == sidebar_host);
+ DCHECK(sidebar_host_to_tab_.find(sidebar_host)->second == tab);
+ DCHECK(tab_to_sidebar_host_[tab].active_content_id != content_id);
+
+ tab_to_sidebar_host_[tab].content_id_to_sidebar_host.erase(content_id);
+ if (tab_to_sidebar_host_[tab].content_id_to_sidebar_host.empty())
+ tab_to_sidebar_host_.erase(tab);
+ sidebar_host_to_tab_.erase(sidebar_host);
+}
diff --git a/chrome/browser/sidebar/sidebar_manager.h b/chrome/browser/sidebar/sidebar_manager.h
new file mode 100644
index 0000000..4f3231e
--- /dev/null
+++ b/chrome/browser/sidebar/sidebar_manager.h
@@ -0,0 +1,156 @@
+// 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_SIDEBAR_SIDEBAR_MANAGER_H_
+#define CHROME_BROWSER_SIDEBAR_SIDEBAR_MANAGER_H_
+
+#include <map>
+#include <set>
+#include <string>
+
+#include "base/ref_counted.h"
+#include "base/string16.h"
+#include "chrome/browser/sidebar/sidebar_container.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_registrar.h"
+
+class GURL;
+class PrefService;
+class Profile;
+class SidebarContainer;
+class SkBitmap;
+class TabContents;
+
+///////////////////////////////////////////////////////////////////////////////
+// SidebarManager
+//
+// This class is a singleton that manages SidebarContainer instances and
+// maintains a connection between tabs and sidebars.
+//
+class SidebarManager : public NotificationObserver,
+ public base::RefCounted<SidebarManager>,
+ private SidebarContainer::Delegate {
+ public:
+ // Returns s singleton instance.
+ static SidebarManager* GetInstance();
+
+ // Returns true if sidebar is allowed to be displayed in the browser.
+ static bool IsSidebarAllowed();
+
+ SidebarManager();
+
+ // Returns SidebarContainer registered for |tab| and active or NULL if
+ // there is no alive and active SidebarContainer registered for |tab|.
+ SidebarContainer* GetActiveSidebarContainerFor(TabContents* tab);
+
+ // Returns SidebarContainer registered for |tab| and |content_id| or NULL if
+ // there is no such SidebarContainer registered.
+ SidebarContainer* GetSidebarContainerFor(TabContents* tab,
+ const std::string& content_id);
+
+ // Returns sidebar's TabContents registered for |tab| and |content_id|.
+ TabContents* GetSidebarTabContents(TabContents* tab,
+ const std::string& content_id);
+
+ // Sends sidebar state change notification to extensions.
+ void NotifyStateChanges(TabContents* was_active_sidebar_contents,
+ TabContents* active_sidebar_contents);
+
+ // Functions supporting chrome.experimental.sidebar API.
+
+ // Shows sidebar identified by |tab| and |content_id| (only sidebar's
+ // mini tab is visible).
+ void ShowSidebar(TabContents* tab, const std::string& content_id);
+
+ // Expands sidebar identified by |tab| and |content_id|.
+ void ExpandSidebar(TabContents* tab, const std::string& content_id);
+
+ // Collapses sidebar identified by |tab| and |content_id| (has no effect
+ // if sidebar is not expanded).
+ void CollapseSidebar(TabContents* tab, const std::string& content_id);
+
+ // Hides sidebar identified by |tab| and |content_id| (removes sidebar's
+ // mini tab).
+ void HideSidebar(TabContents* tab, const std::string& content_id);
+
+ // Navigates sidebar identified by |tab| and |content_id| to |url|.
+ void NavigateSidebar(TabContents* tab, const std::string& content_id,
+ const GURL& url);
+
+ // Changes sidebar's badge text (displayed on the mini tab).
+ void SetSidebarBadgeText(TabContents* tab, const std::string& content_id,
+ const string16& badge_text);
+
+ // Changes sidebar's icon (displayed on the mini tab).
+ void SetSidebarIcon(TabContents* tab, const std::string& content_id,
+ const SkBitmap& bitmap);
+
+ // Changes sidebar's title (mini tab's tooltip).
+ void SetSidebarTitle(TabContents* tab, const std::string& content_id,
+ const string16& title);
+
+ private:
+ friend class base::RefCounted<SidebarManager>;
+
+ virtual ~SidebarManager();
+
+ // Overridden from NotificationObserver.
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ // Overridden from SidebarContainer::Delegate.
+ virtual void UpdateSidebar(SidebarContainer* host);
+
+ // Hides all sidebars registered for |tab|.
+ void HideAllSidebars(TabContents* tab);
+
+ // Returns SidebarContainer corresponding to |sidebar_contents|.
+ SidebarContainer* FindSidebarContainerFor(TabContents* sidebar_contents);
+
+ // Registers new SidebarContainer for |tab|. There must be no
+ // other SidebarContainers registered for the RenderViewHost at the moment.
+ void RegisterSidebarContainerFor(TabContents* tab,
+ SidebarContainer* container);
+
+ // Unregisters SidebarContainer identified by |tab| and |content_id|.
+ void UnregisterSidebarContainerFor(TabContents* tab,
+ const std::string& content_id);
+
+ // Records the link between |tab| and |sidebar_host|.
+ void BindSidebarHost(TabContents* tab, SidebarContainer* sidebar_host);
+
+ // Forgets the link between |tab| and |sidebar_host|.
+ void UnbindSidebarHost(TabContents* tab, SidebarContainer* sidebar_host);
+
+ NotificationRegistrar registrar_;
+
+ // This map stores sidebars linked to a particular tab. Sidebars are
+ // identified by their unique content id (string).
+ typedef std::map<std::string, SidebarContainer*>
+ ContentIdToSidebarHostMap;
+ // These two maps are for tracking dependencies between tabs and
+ // their SidebarContainers.
+ //
+ // SidebarManager start listening to SidebarContainers when they are put
+ // into these maps and removes them when they are closing.
+ typedef struct {
+ // Sidebars linked to this tab.
+ ContentIdToSidebarHostMap content_id_to_sidebar_host;
+ // Content id of the currently active (expanded and visible) sidebar.
+ std::string active_content_id;
+ } SidebarStateForTab;
+ typedef std::map<TabContents*, SidebarStateForTab>
+ TabToSidebarHostMap;
+ TabToSidebarHostMap tab_to_sidebar_host_;
+
+ typedef std::map<SidebarContainer*, TabContents*>
+ SidebarHostToTabMap;
+ SidebarHostToTabMap sidebar_host_to_tab_;
+
+ DISALLOW_COPY_AND_ASSIGN(SidebarManager);
+};
+
+#endif // CHROME_BROWSER_SIDEBAR_SIDEBAR_MANAGER_H_
+
diff --git a/chrome/browser/sidebar/sidebar_test.cc b/chrome/browser/sidebar/sidebar_test.cc
new file mode 100644
index 0000000..a518587
--- /dev/null
+++ b/chrome/browser/sidebar/sidebar_test.cc
@@ -0,0 +1,177 @@
+// 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/command_line.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_window.h"
+#include "chrome/browser/sidebar/sidebar_manager.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/views/frame/browser_view.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/test/in_process_browser_test.h"
+#include "chrome/test/ui_test_utils.h"
+#include "net/test/test_server.h"
+
+namespace {
+
+const char kSampleContentId[] = "sample_content_id";
+const char kSimplePage[] = "files/sidebar/simple_page.html";
+
+class SidebarTest : public InProcessBrowserTest {
+ public:
+ SidebarTest() {
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kEnableExperimentalExtensionApis);
+ set_show_window(true);
+ }
+
+ protected:
+ void ShowSidebarForCurrentTab() {
+ ShowSidebar(browser()->GetSelectedTabContents());
+ }
+
+ void ExpandSidebarForCurrentTab() {
+ ExpandSidebar(browser()->GetSelectedTabContents());
+ }
+
+ void CollapseSidebarForCurrentTab() {
+ CollapseSidebar(browser()->GetSelectedTabContents());
+ }
+
+ void HideSidebarForCurrentTab() {
+ HideSidebar(browser()->GetSelectedTabContents());
+ }
+
+ void NavigateSidebarForCurrentTabTo(const std::string& test_page) {
+ net::TestServer* server = test_server();
+ ASSERT_TRUE(server);
+ GURL url = server->GetURL(test_page);
+
+ TabContents* tab = browser()->GetSelectedTabContents();
+
+ SidebarManager* sidebar_manager = SidebarManager::GetInstance();
+
+ sidebar_manager->NavigateSidebar(tab, kSampleContentId, url);
+
+ SidebarContainer* sidebar_container =
+ sidebar_manager->GetSidebarContainerFor(tab, kSampleContentId);
+
+ TabContents* client_contents = sidebar_container->sidebar_contents();
+ ui_test_utils::WaitForNavigation(&client_contents->controller());
+ }
+
+ void ShowSidebar(TabContents* tab) {
+ SidebarManager* sidebar_manager = SidebarManager::GetInstance();
+ sidebar_manager->ShowSidebar(tab, kSampleContentId);
+ }
+
+ void ExpandSidebar(TabContents* tab) {
+ SidebarManager* sidebar_manager = SidebarManager::GetInstance();
+ sidebar_manager->ExpandSidebar(tab, kSampleContentId);
+ if (browser()->GetSelectedTabContents() == tab)
+ EXPECT_GT(browser_view()->GetSidebarWidth(), 0);
+ }
+
+ void CollapseSidebar(TabContents* tab) {
+ SidebarManager* sidebar_manager = SidebarManager::GetInstance();
+ sidebar_manager->CollapseSidebar(tab, kSampleContentId);
+ if (browser()->GetSelectedTabContents() == tab)
+ EXPECT_EQ(0, browser_view()->GetSidebarWidth());
+ }
+
+ void HideSidebar(TabContents* tab) {
+ SidebarManager* sidebar_manager = SidebarManager::GetInstance();
+ sidebar_manager->HideSidebar(tab, kSampleContentId);
+ if (browser()->GetSelectedTabContents() == tab)
+ EXPECT_EQ(0, browser_view()->GetSidebarWidth());
+ }
+
+ TabContents* tab_contents(int i) {
+ return browser()->GetTabContentsAt(i);
+ }
+
+ BrowserView* browser_view() const {
+ return static_cast<BrowserView*>(browser()->window());
+ }
+};
+
+IN_PROC_BROWSER_TEST_F(SidebarTest, OpenClose) {
+ ShowSidebarForCurrentTab();
+
+ ExpandSidebarForCurrentTab();
+ CollapseSidebarForCurrentTab();
+
+ ExpandSidebarForCurrentTab();
+ CollapseSidebarForCurrentTab();
+
+ ExpandSidebarForCurrentTab();
+ CollapseSidebarForCurrentTab();
+
+ HideSidebarForCurrentTab();
+
+ ShowSidebarForCurrentTab();
+
+ ExpandSidebarForCurrentTab();
+ CollapseSidebarForCurrentTab();
+
+ HideSidebarForCurrentTab();
+}
+
+IN_PROC_BROWSER_TEST_F(SidebarTest, SwitchingTabs) {
+ ShowSidebarForCurrentTab();
+ ExpandSidebarForCurrentTab();
+
+ browser()->NewTab();
+
+ // Make sure sidebar is not visbile for the newly opened tab.
+ EXPECT_EQ(0, browser_view()->GetSidebarWidth());
+
+ // Switch back to the first tab.
+ browser()->SelectNumberedTab(0);
+
+ // Make sure it is visible now.
+ EXPECT_GT(browser_view()->GetSidebarWidth(), 0);
+
+ HideSidebarForCurrentTab();
+}
+
+IN_PROC_BROWSER_TEST_F(SidebarTest, SidebarOnInactiveTab) {
+ ShowSidebarForCurrentTab();
+ ExpandSidebarForCurrentTab();
+
+ browser()->NewTab();
+
+ // Hide sidebar on inactive (first) tab.
+ HideSidebar(tab_contents(0));
+
+ // Switch back to the first tab.
+ browser()->SelectNumberedTab(0);
+
+ // Make sure sidebar is not visbile anymore.
+ EXPECT_EQ(0, browser_view()->GetSidebarWidth());
+
+ // Show sidebar on inactive (second) tab.
+ ShowSidebar(tab_contents(1));
+ ExpandSidebar(tab_contents(1));
+ // Make sure sidebar is not visible yet.
+ EXPECT_EQ(0, browser_view()->GetSidebarWidth());
+
+ // Switch back to the second tab.
+ browser()->SelectNumberedTab(1);
+ // Make sure sidebar is visible now.
+ EXPECT_GT(browser_view()->GetSidebarWidth(), 0);
+
+ HideSidebarForCurrentTab();
+}
+
+IN_PROC_BROWSER_TEST_F(SidebarTest, SidebarNavigate) {
+ ShowSidebarForCurrentTab();
+
+ NavigateSidebarForCurrentTabTo(kSimplePage);
+
+ HideSidebarForCurrentTab();
+}
+
+} // namespace
+