sidebar_manager.cc revision dc0f95d653279beabeb9817299e2902918ba123e
1// Copyright (c) 2010 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 "chrome/common/notification_service.h"
16#include "content/browser/tab_contents/tab_contents.h"
17#include "googleurl/src/gurl.h"
18
19struct 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
27SidebarManager* SidebarManager::GetInstance() {
28  return g_browser_process->sidebar_manager();
29}
30
31// static
32bool SidebarManager::IsSidebarAllowed() {
33  return CommandLine::ForCurrentProcess()->HasSwitch(
34      switches::kEnableExperimentalExtensionApis);
35}
36
37SidebarManager::SidebarManager() {
38}
39
40SidebarContainer* 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
53SidebarContainer* 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
66TabContents* 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
75void 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
103void 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
122void 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
141void 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
160void 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
179void 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
190void 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
199void 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
208void 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
217SidebarManager::~SidebarManager() {
218  DCHECK(tab_to_sidebar_host_.empty());
219  DCHECK(sidebar_host_to_tab_.empty());
220}
221
222void 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
232void SidebarManager::UpdateSidebar(SidebarContainer* host) {
233  NotificationService::current()->Notify(
234      NotificationType::SIDEBAR_CHANGED,
235      Source<SidebarManager>(this),
236      Details<SidebarContainer>(host));
237}
238
239void 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
258SidebarContainer* 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
269void 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
283void 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
305void 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
318void 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