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/extensions/extension_tab_id_map.h"
6
7#include "content/browser/browser_thread.h"
8#include "content/browser/tab_contents/tab_contents.h"
9#include "content/browser/renderer_host/render_view_host.h"
10#include "content/browser/renderer_host/render_process_host.h"
11#include "content/common/notification_registrar.h"
12#include "content/common/notification_observer.h"
13#include "content/common/notification_service.h"
14
15// ExtensionTabIdMap is a Singleton, so it doesn't need refcounting.
16DISABLE_RUNNABLE_METHOD_REFCOUNT(ExtensionTabIdMap);
17
18//
19// ExtensionTabIdMap::TabObserver
20//
21
22// This class listens for notifications about new and closed tabs on the UI
23// thread, and notifies the ExtensionTabIdMap on the IO thread. It should only
24// ever be accessed on the UI thread.
25class ExtensionTabIdMap::TabObserver : public NotificationObserver {
26 public:
27  TabObserver();
28  ~TabObserver();
29
30 private:
31  // NotificationObserver interface.
32  virtual void Observe(NotificationType type,
33                       const NotificationSource& source,
34                       const NotificationDetails& details);
35
36  NotificationRegistrar registrar_;
37};
38
39ExtensionTabIdMap::TabObserver::TabObserver() {
40  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
41  registrar_.Add(this, NotificationType::RENDER_VIEW_HOST_CREATED_FOR_TAB,
42                 NotificationService::AllSources());
43  registrar_.Add(this, NotificationType::RENDER_VIEW_HOST_DELETED,
44                 NotificationService::AllSources());
45  registrar_.Add(this, NotificationType::TAB_PARENTED,
46                 NotificationService::AllSources());
47}
48
49ExtensionTabIdMap::TabObserver::~TabObserver() {
50  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
51}
52
53void ExtensionTabIdMap::TabObserver::Observe(
54    NotificationType type, const NotificationSource& source,
55    const NotificationDetails& details) {
56  switch (type.value) {
57    case NotificationType::RENDER_VIEW_HOST_CREATED_FOR_TAB: {
58      TabContents* contents = Source<TabContents>(source).ptr();
59      RenderViewHost* host = Details<RenderViewHost>(details).ptr();
60      // TODO(mpcmoplete): How can we tell if window_id is bogus? It may not
61      // have been set yet.
62      BrowserThread::PostTask(
63          BrowserThread::IO, FROM_HERE,
64          NewRunnableMethod(
65              ExtensionTabIdMap::GetInstance(),
66              &ExtensionTabIdMap::SetTabAndWindowId,
67              host->process()->id(), host->routing_id(),
68              contents->controller().session_id().id(),
69              contents->controller().window_id().id()));
70      break;
71    }
72    case NotificationType::TAB_PARENTED: {
73      NavigationController* controller =
74          Source<NavigationController>(source).ptr();
75      RenderViewHost* host = controller->tab_contents()->render_view_host();
76      BrowserThread::PostTask(
77          BrowserThread::IO, FROM_HERE,
78          NewRunnableMethod(
79              ExtensionTabIdMap::GetInstance(),
80              &ExtensionTabIdMap::SetTabAndWindowId,
81              host->process()->id(), host->routing_id(),
82              controller->session_id().id(),
83              controller->window_id().id()));
84      break;
85    }
86    case NotificationType::RENDER_VIEW_HOST_DELETED: {
87      RenderViewHost* host = Source<RenderViewHost>(source).ptr();
88      BrowserThread::PostTask(
89          BrowserThread::IO, FROM_HERE,
90          NewRunnableMethod(
91              ExtensionTabIdMap::GetInstance(),
92              &ExtensionTabIdMap::ClearTabAndWindowId,
93              host->process()->id(), host->routing_id()));
94      break;
95    }
96    default:
97      NOTREACHED();
98      return;
99  }
100}
101
102//
103// ExtensionTabIdMap
104//
105
106ExtensionTabIdMap::ExtensionTabIdMap() : observer_(NULL) {
107}
108
109ExtensionTabIdMap::~ExtensionTabIdMap() {
110}
111
112// static
113ExtensionTabIdMap* ExtensionTabIdMap::GetInstance() {
114  return Singleton<ExtensionTabIdMap>::get();
115}
116
117void ExtensionTabIdMap::Init() {
118  observer_ = new TabObserver;
119}
120
121void ExtensionTabIdMap::Shutdown() {
122  delete observer_;
123}
124
125void ExtensionTabIdMap::SetTabAndWindowId(
126    int render_process_host_id, int routing_id, int tab_id, int window_id) {
127  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
128  RenderId render_id(render_process_host_id, routing_id);
129  map_[render_id] = TabAndWindowId(tab_id, window_id);
130}
131
132void ExtensionTabIdMap::ClearTabAndWindowId(
133    int render_process_host_id, int routing_id) {
134  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
135  RenderId render_id(render_process_host_id, routing_id);
136  map_.erase(render_id);
137}
138
139bool ExtensionTabIdMap::GetTabAndWindowId(
140    int render_process_host_id, int routing_id, int* tab_id, int* window_id) {
141  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
142  RenderId render_id(render_process_host_id, routing_id);
143  TabAndWindowIdMap::iterator iter = map_.find(render_id);
144  if (iter != map_.end()) {
145    *tab_id = iter->second.first;
146    *window_id = iter->second.second;
147    return true;
148  }
149  return false;
150}
151