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
GetInstance()27 SidebarManager* SidebarManager::GetInstance() {
28 return g_browser_process->sidebar_manager();
29 }
30
31 // static
IsSidebarAllowed()32 bool SidebarManager::IsSidebarAllowed() {
33 return CommandLine::ForCurrentProcess()->HasSwitch(
34 switches::kEnableExperimentalExtensionApis);
35 }
36
SidebarManager()37 SidebarManager::SidebarManager() {
38 }
39
GetActiveSidebarContainerFor(TabContents * tab)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
GetSidebarContainerFor(TabContents * tab,const std::string & content_id)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
GetSidebarTabContents(TabContents * tab,const std::string & content_id)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
NotifyStateChanges(TabContents * was_active_sidebar_contents,TabContents * active_sidebar_contents)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
ShowSidebar(TabContents * tab,const std::string & content_id)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
ExpandSidebar(TabContents * tab,const std::string & content_id)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
CollapseSidebar(TabContents * tab,const std::string & content_id)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
HideSidebar(TabContents * tab,const std::string & content_id)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
NavigateSidebar(TabContents * tab,const std::string & content_id,const GURL & url)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
SetSidebarBadgeText(TabContents * tab,const std::string & content_id,const string16 & badge_text)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
SetSidebarIcon(TabContents * tab,const std::string & content_id,const SkBitmap & bitmap)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
SetSidebarTitle(TabContents * tab,const std::string & content_id,const string16 & title)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
~SidebarManager()217 SidebarManager::~SidebarManager() {
218 DCHECK(tab_to_sidebar_host_.empty());
219 DCHECK(sidebar_host_to_tab_.empty());
220 }
221
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)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
UpdateSidebar(SidebarContainer * host)232 void SidebarManager::UpdateSidebar(SidebarContainer* host) {
233 NotificationService::current()->Notify(
234 NotificationType::SIDEBAR_CHANGED,
235 Source<SidebarManager>(this),
236 Details<SidebarContainer>(host));
237 }
238
HideAllSidebars(TabContents * tab)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
FindSidebarContainerFor(TabContents * sidebar_contents)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
RegisterSidebarContainerFor(TabContents * tab,SidebarContainer * sidebar_host)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
UnregisterSidebarContainerFor(TabContents * tab,const std::string & content_id)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
BindSidebarHost(TabContents * tab,SidebarContainer * sidebar_host)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
UnbindSidebarHost(TabContents * tab,SidebarContainer * sidebar_host)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