// Copyright (c) 2011 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/profiles/profile.h"
#include "chrome/browser/sidebar/sidebar_container.h"
#include "chrome/common/chrome_switches.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "content/common/notification_service.h"
#include "googleurl/src/gurl.h"
struct SidebarManager::SidebarStateForTab {
// 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;
};
// 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->tab_contents(), was_active_host->content_id(),
extension_sidebar_constants::kShownState);
}
if (active_host != NULL) {
ExtensionSidebarEventRouter::OnStateChanged(
active_sidebar_contents->profile(),
active_host->tab_contents(), 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);
// It might trigger UpdateSidebar notification, so load them after
// the registration.
host->LoadDefaults();
}
host->Show();
ExtensionSidebarEventRouter::OnStateChanged(
tab->profile(), tab, 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);
UnregisterSidebarContainerFor(tab, content_id);
ExtensionSidebarEventRouter::OnStateChanged(
tab->profile(), tab, 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);
}