1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/sidebar/sidebar_manager.h" 6 7 #include <vector> 8 9 #include "base/command_line.h" 10 #include "chrome/browser/browser_process.h" 11 #include "chrome/browser/extensions/extension_sidebar_api.h" 12 #include "chrome/browser/profiles/profile.h" 13 #include "chrome/browser/sidebar/sidebar_container.h" 14 #include "chrome/common/chrome_switches.h" 15 #include "content/browser/tab_contents/tab_contents.h" 16 #include "content/common/notification_service.h" 17 #include "googleurl/src/gurl.h" 18 19 struct SidebarManager::SidebarStateForTab { 20 // Sidebars linked to this tab. 21 ContentIdToSidebarHostMap content_id_to_sidebar_host; 22 // Content id of the currently active (expanded and visible) sidebar. 23 std::string active_content_id; 24 }; 25 26 // static 27 SidebarManager* SidebarManager::GetInstance() { 28 return g_browser_process->sidebar_manager(); 29 } 30 31 // static 32 bool SidebarManager::IsSidebarAllowed() { 33 return CommandLine::ForCurrentProcess()->HasSwitch( 34 switches::kEnableExperimentalExtensionApis); 35 } 36 37 SidebarManager::SidebarManager() { 38 } 39 40 SidebarContainer* SidebarManager::GetActiveSidebarContainerFor( 41 TabContents* tab) { 42 TabToSidebarHostMap::iterator it = tab_to_sidebar_host_.find(tab); 43 if (it == tab_to_sidebar_host_.end()) 44 return NULL; 45 if (it->second.active_content_id.empty()) 46 return NULL; 47 ContentIdToSidebarHostMap::iterator host_it = 48 it->second.content_id_to_sidebar_host.find(it->second.active_content_id); 49 DCHECK(host_it != it->second.content_id_to_sidebar_host.end()); 50 return host_it->second; 51 } 52 53 SidebarContainer* SidebarManager::GetSidebarContainerFor( 54 TabContents* tab, const std::string& content_id) { 55 DCHECK(!content_id.empty()); 56 TabToSidebarHostMap::iterator it = tab_to_sidebar_host_.find(tab); 57 if (it == tab_to_sidebar_host_.end()) 58 return NULL; 59 ContentIdToSidebarHostMap::iterator host_it = 60 it->second.content_id_to_sidebar_host.find(content_id); 61 if (host_it == it->second.content_id_to_sidebar_host.end()) 62 return NULL; 63 return host_it->second; 64 } 65 66 TabContents* SidebarManager::GetSidebarTabContents( 67 TabContents* tab, const std::string& content_id) { 68 DCHECK(!content_id.empty()); 69 SidebarContainer* sidebar_host = GetSidebarContainerFor(tab, content_id); 70 if (!sidebar_host) 71 return NULL; 72 return sidebar_host->sidebar_contents(); 73 } 74 75 void SidebarManager::NotifyStateChanges( 76 TabContents* was_active_sidebar_contents, 77 TabContents* active_sidebar_contents) { 78 if (was_active_sidebar_contents == active_sidebar_contents) 79 return; 80 81 SidebarContainer* was_active_host = 82 was_active_sidebar_contents == NULL ? NULL : 83 FindSidebarContainerFor(was_active_sidebar_contents); 84 SidebarContainer* active_host = 85 active_sidebar_contents == NULL ? NULL : 86 FindSidebarContainerFor(active_sidebar_contents); 87 88 if (was_active_host != NULL) { 89 ExtensionSidebarEventRouter::OnStateChanged( 90 was_active_sidebar_contents->profile(), 91 was_active_host->tab_contents(), was_active_host->content_id(), 92 extension_sidebar_constants::kShownState); 93 } 94 95 if (active_host != NULL) { 96 ExtensionSidebarEventRouter::OnStateChanged( 97 active_sidebar_contents->profile(), 98 active_host->tab_contents(), active_host->content_id(), 99 extension_sidebar_constants::kActiveState); 100 } 101 } 102 103 void SidebarManager::ShowSidebar(TabContents* tab, 104 const std::string& content_id) { 105 DCHECK(!content_id.empty()); 106 SidebarContainer* host = GetSidebarContainerFor(tab, content_id); 107 if (!host) { 108 host = new SidebarContainer(tab, content_id, this); 109 RegisterSidebarContainerFor(tab, host); 110 // It might trigger UpdateSidebar notification, so load them after 111 // the registration. 112 host->LoadDefaults(); 113 } 114 115 host->Show(); 116 117 ExtensionSidebarEventRouter::OnStateChanged( 118 tab->profile(), tab, content_id, 119 extension_sidebar_constants::kShownState); 120 } 121 122 void SidebarManager::ExpandSidebar(TabContents* tab, 123 const std::string& content_id) { 124 DCHECK(!content_id.empty()); 125 TabToSidebarHostMap::iterator it = tab_to_sidebar_host_.find(tab); 126 if (it == tab_to_sidebar_host_.end()) 127 return; 128 // If it's already active, bail out. 129 if (it->second.active_content_id == content_id) 130 return; 131 132 SidebarContainer* host = GetSidebarContainerFor(tab, content_id); 133 DCHECK(host); 134 if (!host) 135 return; 136 it->second.active_content_id = content_id; 137 138 host->Expand(); 139 } 140 141 void SidebarManager::CollapseSidebar(TabContents* tab, 142 const std::string& content_id) { 143 DCHECK(!content_id.empty()); 144 TabToSidebarHostMap::iterator it = tab_to_sidebar_host_.find(tab); 145 if (it == tab_to_sidebar_host_.end()) 146 return; 147 // If it's not the one active now, bail out. 148 if (it->second.active_content_id != content_id) 149 return; 150 151 SidebarContainer* host = GetSidebarContainerFor(tab, content_id); 152 DCHECK(host); 153 if (!host) 154 return; 155 it->second.active_content_id.clear(); 156 157 host->Collapse(); 158 } 159 160 void SidebarManager::HideSidebar(TabContents* tab, 161 const std::string& content_id) { 162 DCHECK(!content_id.empty()); 163 TabToSidebarHostMap::iterator it = tab_to_sidebar_host_.find(tab); 164 if (it == tab_to_sidebar_host_.end()) 165 return; 166 if (it->second.active_content_id == content_id) 167 it->second.active_content_id.clear(); 168 169 SidebarContainer* host = GetSidebarContainerFor(tab, content_id); 170 DCHECK(host); 171 172 UnregisterSidebarContainerFor(tab, content_id); 173 174 ExtensionSidebarEventRouter::OnStateChanged( 175 tab->profile(), tab, content_id, 176 extension_sidebar_constants::kHiddenState); 177 } 178 179 void SidebarManager::NavigateSidebar(TabContents* tab, 180 const std::string& content_id, 181 const GURL& url) { 182 DCHECK(!content_id.empty()); 183 SidebarContainer* host = GetSidebarContainerFor(tab, content_id); 184 if (!host) 185 return; 186 187 host->Navigate(url); 188 } 189 190 void SidebarManager::SetSidebarBadgeText( 191 TabContents* tab, const std::string& content_id, 192 const string16& badge_text) { 193 SidebarContainer* host = GetSidebarContainerFor(tab, content_id); 194 if (!host) 195 return; 196 host->SetBadgeText(badge_text); 197 } 198 199 void SidebarManager::SetSidebarIcon( 200 TabContents* tab, const std::string& content_id, 201 const SkBitmap& bitmap) { 202 SidebarContainer* host = GetSidebarContainerFor(tab, content_id); 203 if (!host) 204 return; 205 host->SetIcon(bitmap); 206 } 207 208 void SidebarManager::SetSidebarTitle( 209 TabContents* tab, const std::string& content_id, 210 const string16& title) { 211 SidebarContainer* host = GetSidebarContainerFor(tab, content_id); 212 if (!host) 213 return; 214 host->SetTitle(title); 215 } 216 217 SidebarManager::~SidebarManager() { 218 DCHECK(tab_to_sidebar_host_.empty()); 219 DCHECK(sidebar_host_to_tab_.empty()); 220 } 221 222 void SidebarManager::Observe(NotificationType type, 223 const NotificationSource& source, 224 const NotificationDetails& details) { 225 if (type == NotificationType::TAB_CONTENTS_DESTROYED) { 226 HideAllSidebars(Source<TabContents>(source).ptr()); 227 } else { 228 NOTREACHED() << "Got a notification we didn't register for!"; 229 } 230 } 231 232 void SidebarManager::UpdateSidebar(SidebarContainer* host) { 233 NotificationService::current()->Notify( 234 NotificationType::SIDEBAR_CHANGED, 235 Source<SidebarManager>(this), 236 Details<SidebarContainer>(host)); 237 } 238 239 void SidebarManager::HideAllSidebars(TabContents* tab) { 240 TabToSidebarHostMap::iterator tab_it = tab_to_sidebar_host_.find(tab); 241 if (tab_it == tab_to_sidebar_host_.end()) 242 return; 243 const ContentIdToSidebarHostMap& hosts = 244 tab_it->second.content_id_to_sidebar_host; 245 246 std::vector<std::string> content_ids; 247 for (ContentIdToSidebarHostMap::const_iterator it = hosts.begin(); 248 it != hosts.end(); ++it) { 249 content_ids.push_back(it->first); 250 } 251 252 for (std::vector<std::string>::iterator it = content_ids.begin(); 253 it != content_ids.end(); ++it) { 254 HideSidebar(tab, *it); 255 } 256 } 257 258 SidebarContainer* SidebarManager::FindSidebarContainerFor( 259 TabContents* sidebar_contents) { 260 for (SidebarHostToTabMap::iterator it = sidebar_host_to_tab_.begin(); 261 it != sidebar_host_to_tab_.end(); 262 ++it) { 263 if (sidebar_contents == it->first->sidebar_contents()) 264 return it->first; 265 } 266 return NULL; 267 } 268 269 void SidebarManager::RegisterSidebarContainerFor( 270 TabContents* tab, SidebarContainer* sidebar_host) { 271 DCHECK(!GetSidebarContainerFor(tab, sidebar_host->content_id())); 272 273 // If it's a first sidebar for this tab, register destroy notification. 274 if (tab_to_sidebar_host_.find(tab) == tab_to_sidebar_host_.end()) { 275 registrar_.Add(this, 276 NotificationType::TAB_CONTENTS_DESTROYED, 277 Source<TabContents>(tab)); 278 } 279 280 BindSidebarHost(tab, sidebar_host); 281 } 282 283 void SidebarManager::UnregisterSidebarContainerFor( 284 TabContents* tab, const std::string& content_id) { 285 SidebarContainer* host = GetSidebarContainerFor(tab, content_id); 286 DCHECK(host); 287 if (!host) 288 return; 289 290 UnbindSidebarHost(tab, host); 291 292 // If there's no more sidebars linked to this tab, unsubscribe. 293 if (tab_to_sidebar_host_.find(tab) == tab_to_sidebar_host_.end()) { 294 registrar_.Remove(this, 295 NotificationType::TAB_CONTENTS_DESTROYED, 296 Source<TabContents>(tab)); 297 } 298 299 // Issue tab closing event post unbound. 300 host->SidebarClosing(); 301 // Destroy sidebar container. 302 delete host; 303 } 304 305 void SidebarManager::BindSidebarHost(TabContents* tab, 306 SidebarContainer* sidebar_host) { 307 const std::string& content_id = sidebar_host->content_id(); 308 309 DCHECK(GetSidebarContainerFor(tab, content_id) == NULL); 310 DCHECK(sidebar_host_to_tab_.find(sidebar_host) == 311 sidebar_host_to_tab_.end()); 312 313 tab_to_sidebar_host_[tab].content_id_to_sidebar_host[content_id] = 314 sidebar_host; 315 sidebar_host_to_tab_[sidebar_host] = tab; 316 } 317 318 void SidebarManager::UnbindSidebarHost(TabContents* tab, 319 SidebarContainer* sidebar_host) { 320 const std::string& content_id = sidebar_host->content_id(); 321 322 DCHECK(GetSidebarContainerFor(tab, content_id) == sidebar_host); 323 DCHECK(sidebar_host_to_tab_.find(sidebar_host)->second == tab); 324 DCHECK(tab_to_sidebar_host_[tab].active_content_id != content_id); 325 326 tab_to_sidebar_host_[tab].content_id_to_sidebar_host.erase(content_id); 327 if (tab_to_sidebar_host_[tab].content_id_to_sidebar_host.empty()) 328 tab_to_sidebar_host_.erase(tab); 329 sidebar_host_to_tab_.erase(sidebar_host); 330 } 331