summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/app/generated_resources.grd6
-rw-r--r--chrome/browser/about_flags.cc7
-rw-r--r--chrome/browser/tabs/tab_finder.cc242
-rw-r--r--chrome/browser/tabs/tab_finder.h111
-rw-r--r--chrome/browser/ui/browser.cc16
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/common/chrome_switches.cc3
-rw-r--r--chrome/common/chrome_switches.h1
-rw-r--r--content/browser/tab_contents/tab_contents.cc2
-rw-r--r--content/browser/tab_contents/tab_contents_observer.cc31
-rw-r--r--content/browser/tab_contents/tab_contents_observer.h20
11 files changed, 434 insertions, 7 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 144042c..ff8a6ff 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -4034,6 +4034,12 @@ Keep your key file in a safe place. You will need it to create new versions of y
<message name="IDS_FLAGS_REMOTING_DESCRIPTION" desc="Description of the 'Remoting' client lab.">
Enable the UI plus backing code for the Remoting serivce process, and client plugin. Warning: This is currently for developer testing only. Unless you are on the dev team and whitelisted, nothing in the enabled UI will work.
</message>
+ <message name="IDS_FLAGS_FOCUS_EXISTING_TAB_ON_OPEN_NAME" desc="Name for 'Focus existing tab on open' lab">
+ Focus existing tab on open
+ </message>
+ <message name="IDS_FLAGS_FOCUS_EXISTING_TAB_ON_OPEN_DESCRIPTION" desc="Description of the 'Focus existing tab on open' lab.">
+ Typing a URL of an existing tab into the omnibox results in refocusing the tab instead of loading in the current tab.
+ </message>
<message name="IDS_FLAGS_ENABLE_NACL_NAME" desc="Description of the 'Enable Native Client' lab.">
Native Client
</message>
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 3fe6e719..4d59c635 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -262,6 +262,13 @@ const Experiment kExperiments[] = {
kOsAll,
SINGLE_VALUE_TYPE(switches::kEnableP2PApi)
},
+ {
+ "focus-existing-tab-on-open", // FLAGS:RECORD_UMA
+ IDS_FLAGS_FOCUS_EXISTING_TAB_ON_OPEN_NAME,
+ IDS_FLAGS_FOCUS_EXISTING_TAB_ON_OPEN_DESCRIPTION,
+ kOsAll,
+ SINGLE_VALUE_TYPE(switches::kFocusExistingTabOnOpen)
+ },
};
const Experiment* experiments = kExperiments;
diff --git a/chrome/browser/tabs/tab_finder.cc b/chrome/browser/tabs/tab_finder.cc
new file mode 100644
index 0000000..6758714
--- /dev/null
+++ b/chrome/browser/tabs/tab_finder.cc
@@ -0,0 +1,242 @@
+// Copyright (c) 2010 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/tabs/tab_finder.h"
+
+#include "base/command_line.h"
+#include "base/stl_util-inl.h"
+#include "chrome/browser/history/history.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/notification_source.h"
+#include "chrome/common/notification_type.h"
+#include "chrome/common/page_transition_types.h"
+#include "chrome/common/render_messages_params.h"
+#include "content/browser/tab_contents/navigation_entry.h"
+#include "content/browser/tab_contents/tab_contents.h"
+#include "content/browser/tab_contents/tab_contents_observer.h"
+
+class TabFinder::TabContentsObserverImpl : public TabContentsObserver {
+ public:
+ TabContentsObserverImpl(TabContents* tab, TabFinder* finder);
+ virtual ~TabContentsObserverImpl();
+
+ TabContents* tab_contents() { return TabContentsObserver::tab_contents(); }
+
+ // TabContentsObserver overrides:
+ virtual void DidNavigateAnyFramePostCommit(
+ const NavigationController::LoadCommittedDetails& details,
+ const ViewHostMsg_FrameNavigate_Params& params) OVERRIDE;
+ virtual void OnTabContentsDestroyed(TabContents* tab) OVERRIDE;
+
+ private:
+ TabFinder* finder_;
+
+ DISALLOW_COPY_AND_ASSIGN(TabContentsObserverImpl);
+};
+
+TabFinder::TabContentsObserverImpl::TabContentsObserverImpl(
+ TabContents* tab,
+ TabFinder* finder)
+ : TabContentsObserver(tab),
+ finder_(finder) {
+}
+
+TabFinder::TabContentsObserverImpl::~TabContentsObserverImpl() {
+}
+
+void TabFinder::TabContentsObserverImpl::DidNavigateAnyFramePostCommit(
+ const NavigationController::LoadCommittedDetails& details,
+ const ViewHostMsg_FrameNavigate_Params& params) {
+ finder_->DidNavigateAnyFramePostCommit(tab_contents(), details, params);
+}
+
+void TabFinder::TabContentsObserverImpl::OnTabContentsDestroyed(
+ TabContents* tab) {
+ finder_->TabDestroyed(this);
+ delete this;
+}
+
+// static
+TabFinder* TabFinder::GetInstance() {
+ return IsEnabled() ? Singleton<TabFinder>::get() : NULL;
+}
+
+// static
+bool TabFinder::IsEnabled() {
+ return CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kFocusExistingTabOnOpen);
+}
+
+TabContents* TabFinder::FindTab(Browser* browser,
+ const GURL& url,
+ Browser** existing_browser) {
+ if (browser->profile()->IsOffTheRecord())
+ return NULL;
+
+ // If the current tab matches the url, ignore it and let the user reload the
+ // existing tab.
+ TabContents* selected_tab = browser->GetSelectedTabContents();
+ if (TabMatchesURL(selected_tab, url))
+ return NULL;
+
+ // See if the current browser has a tab matching the specified url.
+ TabContents* tab_in_browser = FindTabInBrowser(browser, url);
+ if (tab_in_browser) {
+ *existing_browser = browser;
+ return tab_in_browser;
+ }
+
+ // Then check other browsers.
+ for (BrowserList::const_iterator i = BrowserList::begin();
+ i != BrowserList::end(); ++i) {
+ if (!(*i)->profile()->IsOffTheRecord()) {
+ tab_in_browser = FindTabInBrowser(*i, url);
+ if (tab_in_browser) {
+ *existing_browser = *i;
+ return tab_in_browser;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+void TabFinder::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK_EQ(type.value, NotificationType::TAB_PARENTED);
+
+ // The tab was added to a browser. Query for its state now.
+ NavigationController* controller =
+ Source<NavigationController>(source).ptr();
+ TrackTab(controller->tab_contents());
+}
+
+TabFinder::TabFinder() {
+ registrar_.Add(this, NotificationType::TAB_PARENTED,
+ NotificationService::AllSources());
+}
+
+TabFinder::~TabFinder() {
+ STLDeleteElements(&tab_contents_observers_);
+}
+
+void TabFinder::Init() {
+ for (BrowserList::const_iterator i = BrowserList::begin();
+ i != BrowserList::end(); ++i) {
+ if (!(*i)->profile()->IsOffTheRecord())
+ TrackBrowser(*i);
+ }
+}
+
+void TabFinder::DidNavigateAnyFramePostCommit(
+ TabContents* source,
+ const NavigationController::LoadCommittedDetails& details,
+ const ViewHostMsg_FrameNavigate_Params& params) {
+ CancelRequestsFor(source);
+
+ if (PageTransition::IsRedirect(params.transition)) {
+ // If this is a redirect, we need to go to the db to get the start.
+ FetchRedirectStart(source);
+ } else if (params.redirects.size() > 1 ||
+ params.redirects[0] != details.entry->url()) {
+ tab_contents_to_url_[source] = params.redirects[0];
+ }
+}
+
+bool TabFinder::TabMatchesURL(TabContents* tab_contents, const GURL& url) {
+ if (tab_contents->GetURL() == url)
+ return true;
+
+ TabContentsToURLMap::const_iterator i =
+ tab_contents_to_url_.find(tab_contents);
+ return i != tab_contents_to_url_.end() && i->second == url;
+}
+
+TabContents* TabFinder::FindTabInBrowser(Browser* browser, const GURL& url) {
+ if (browser->type() != Browser::TYPE_NORMAL)
+ return NULL;
+
+ for (int i = 0; i < browser->tab_count(); ++i) {
+ if (TabMatchesURL(browser->GetTabContentsAt(i), url))
+ return browser->GetTabContentsAt(i);
+ }
+ return NULL;
+}
+
+void TabFinder::TrackTab(TabContents* tab) {
+ for (TabContentsObservers::const_iterator i = tab_contents_observers_.begin();
+ i != tab_contents_observers_.end(); ++i) {
+ if ((*i)->tab_contents() == tab) {
+ // Already tracking the tab.
+ return;
+ }
+ }
+ TabContentsObserverImpl* observer = new TabContentsObserverImpl(tab, this);
+ tab_contents_observers_.insert(observer);
+ FetchRedirectStart(tab);
+}
+
+void TabFinder::TrackBrowser(Browser* browser) {
+ for (int i = 0; i < browser->tab_count(); ++i)
+ FetchRedirectStart(browser->GetTabContentsAt(i));
+}
+
+void TabFinder::TabDestroyed(TabContentsObserverImpl* observer) {
+ DCHECK_GT(tab_contents_observers_.count(observer), 0u);
+ tab_contents_observers_.erase(observer);
+}
+
+void TabFinder::CancelRequestsFor(TabContents* tab_contents) {
+ if (tab_contents->profile()->IsOffTheRecord())
+ return;
+
+ tab_contents_to_url_.erase(tab_contents);
+
+ HistoryService* history = tab_contents->profile()->GetHistoryService(
+ Profile::EXPLICIT_ACCESS);
+ if (history) {
+ CancelableRequestProvider::Handle request_handle;
+ if (callback_consumer_.GetFirstHandleForClientData(tab_contents,
+ &request_handle)) {
+ history->CancelRequest(request_handle);
+ }
+ }
+}
+
+void TabFinder::FetchRedirectStart(TabContents* tab) {
+ if (tab->profile()->IsOffTheRecord())
+ return;
+
+ NavigationEntry* committed_entry = tab->controller().GetLastCommittedEntry();
+ if (!committed_entry || committed_entry->url().is_empty())
+ return;
+
+ HistoryService* history =tab->profile()->GetHistoryService(
+ Profile::EXPLICIT_ACCESS);
+ if (history) {
+ CancelableRequestProvider::Handle request_handle =
+ history->QueryRedirectsTo(
+ committed_entry->url(),
+ &callback_consumer_,
+ NewCallback(this, &TabFinder::QueryRedirectsToComplete));
+ callback_consumer_.SetClientData(history, request_handle, tab);
+ }
+}
+
+void TabFinder::QueryRedirectsToComplete(HistoryService::Handle handle,
+ GURL url,
+ bool success,
+ history::RedirectList* redirects) {
+ if (success && !redirects->empty()) {
+ TabContents* tab_contents =
+ callback_consumer_.GetClientDataForCurrentRequest();
+ DCHECK(tab_contents);
+ tab_contents_to_url_[tab_contents] = redirects->back();
+ }
+}
diff --git a/chrome/browser/tabs/tab_finder.h b/chrome/browser/tabs/tab_finder.h
new file mode 100644
index 0000000..908b238
--- /dev/null
+++ b/chrome/browser/tabs/tab_finder.h
@@ -0,0 +1,111 @@
+// Copyright (c) 2010 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.
+
+#ifndef CHROME_BROWSER_TABS_TAB_FINDER_H_
+#define CHROME_BROWSER_TABS_TAB_FINDER_H_
+#pragma once
+
+#include <map>
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/singleton.h"
+#include "chrome/browser/history/history_types.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_registrar.h"
+#include "content/browser/cancelable_request.h"
+#include "content/browser/tab_contents/navigation_controller.h"
+
+class Browser;
+class GURL;
+class TabContents;
+
+// TabFinder is used to locate a tab by URL. TabFinder matches tabs based
+// on the tabs current url, or the start of the redirect chain.
+//
+// TODO: if we end up keeping this (moving it out of about:flags) then we
+// should persist the start of the redirect chain in the navigation entry.
+class TabFinder : public NotificationObserver {
+ public:
+ // Returns the TabFinder, or NULL if TabFinder is not enabled.
+ static TabFinder* GetInstance();
+
+ // Returns true if TabFinder is enabled.
+ static bool IsEnabled();
+
+ // Returns the tab that matches the specified url. If a tab is found the
+ // browser containing the tab is set in |existing_browser|. This searches
+ // in |browser| first before checking any other browsers.
+ TabContents* FindTab(Browser* browser,
+ const GURL& url,
+ Browser** existing_browser);
+
+ // NotificationObserver overrides:
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) OVERRIDE;
+
+ private:
+ friend struct DefaultSingletonTraits<TabFinder>;
+
+ class TabContentsObserverImpl;
+
+ typedef std::map<TabContents*, GURL> TabContentsToURLMap;
+ typedef std::set<TabContentsObserverImpl*> TabContentsObservers;
+
+ TabFinder();
+ ~TabFinder();
+
+ void Init();
+
+ // Forwarded from TabContentsObserverImpl.
+ void DidNavigateAnyFramePostCommit(
+ TabContents* source,
+ const NavigationController::LoadCommittedDetails& details,
+ const ViewHostMsg_FrameNavigate_Params& params);
+
+ // Returns true if the tab's current url is |url|, or the start of the
+ // redirect chain for the tab is |url|.
+ bool TabMatchesURL(TabContents* tab_contents, const GURL& url);
+
+ // Returns the first tab in the specified browser that matches the specified
+ // url. Returns NULL if there are no tabs matching the specified url.
+ TabContents* FindTabInBrowser(Browser* browser, const GURL& url);
+
+ // If we're not currently tracking |tab| this creates a
+ // TabContentsObserverImpl to listen for navigations.
+ void TrackTab(TabContents* tab);
+
+ // Queries all the tabs in |browser| for the start of the redirect chain.
+ void TrackBrowser(Browser* browser);
+
+ // Invoked when a TabContents is being destroyed.
+ void TabDestroyed(TabContentsObserverImpl* observer);
+
+ // Cancels any pending requests for the specified tabs redirect chain.
+ void CancelRequestsFor(TabContents* tab_contents);
+
+ // Starts the fetch for the redirect chain of the specified TabContents.
+ // QueryRedirectsToComplete is invoked when the redirect chain is retrieved.
+ void FetchRedirectStart(TabContents* tab);
+
+ // Callback when we get the redirect list for a tab.
+ void QueryRedirectsToComplete(CancelableRequestProvider::Handle handle,
+ GURL url,
+ bool success,
+ history::RedirectList* redirects);
+
+ // Maps from TabContents to the start of the redirect chain.
+ TabContentsToURLMap tab_contents_to_url_;
+
+ CancelableRequestConsumerTSimple<TabContents*> callback_consumer_;
+
+ NotificationRegistrar registrar_;
+
+ TabContentsObservers tab_contents_observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(TabFinder);
+};
+
+#endif // CHROME_BROWSER_TABS_TAB_FINDER_H_
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index ae9eba0..eadf787 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -66,6 +66,7 @@
#include "chrome/browser/sync/sync_ui_util.h"
#include "chrome/browser/tab_closeable_state_watcher.h"
#include "chrome/browser/tab_contents/simple_alert_infobar_delegate.h"
+#include "chrome/browser/tabs/tab_finder.h"
#include "chrome/browser/tabs/tab_strip_model.h"
#include "chrome/browser/ui/find_bar/find_bar.h"
#include "chrome/browser/ui/find_bar/find_bar_controller.h"
@@ -260,6 +261,10 @@ Browser::Browser(Type type, Profile* profile)
profile_->GetProfileSyncService()->AddObserver(this);
CreateInstantIfNecessary();
+
+ // Make sure TabFinder has been created. This does nothing if TabFinder is
+ // not enabled.
+ TabFinder::GetInstance();
}
Browser::~Browser() {
@@ -1302,6 +1307,17 @@ void Browser::OpenCurrentURL() {
return;
GURL url(WideToUTF8(location_bar->GetInputString()));
+
+ if (open_disposition == CURRENT_TAB && TabFinder::IsEnabled()) {
+ Browser* existing_browser = NULL;
+ TabContents* existing_tab = TabFinder::GetInstance()->FindTab(
+ this, url, &existing_browser);
+ if (existing_tab) {
+ existing_browser->ActivateContents(existing_tab);
+ return;
+ }
+ }
+
browser::NavigateParams params(this, url, location_bar->GetPageTransition());
params.disposition = open_disposition;
// Use ADD_INHERIT_OPENER so that all pages opened by the omnibox at least
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index e535fd3..293fb27 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -2012,6 +2012,8 @@
'browser/tabs/pinned_tab_service.cc',
'browser/tabs/pinned_tab_service.h',
'browser/tabs/tab_handler.h',
+ 'browser/tabs/tab_finder.cc',
+ 'browser/tabs/tab_finder.h',
'browser/tabs/tab_strip_model.cc',
'browser/tabs/tab_strip_model.h',
'browser/tabs/tab_strip_model_delegate.h',
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index 2c427e0..cd560e3 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -617,6 +617,9 @@ const char kFeedbackServer[] = "feedback-server";
// gracefully.
const char kFileDescriptorLimit[] = "file-descriptor-limit";
+// If true opening a url from the omnibox attepts to focus an existing tab.
+const char kFocusExistingTabOnOpen[] = "focus-existing-tab-on-open";
+
// Display the First Run experience when the browser is started, regardless of
// whether or not it's actually the first run.
const char kFirstRun[] = "first-run";
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index 1cf6dda..9880f7f 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -182,6 +182,7 @@ extern const char kFlagSwitchesBegin[];
extern const char kFlagSwitchesEnd[];
extern const char kFeedbackServer[];
extern const char kFileDescriptorLimit[];
+extern const char kFocusExistingTabOnOpen[];
extern const char kFirstRun[];
extern const char kForceAppsPromoVisible[];
extern const char kForceFieldTestNameAndValue[];
diff --git a/content/browser/tab_contents/tab_contents.cc b/content/browser/tab_contents/tab_contents.cc
index f837d98..6e7e91b 100644
--- a/content/browser/tab_contents/tab_contents.cc
+++ b/content/browser/tab_contents/tab_contents.cc
@@ -391,7 +391,7 @@ TabContents::~TabContents() {
base::TimeTicks::Now() - tab_close_start_time_);
}
- FOR_EACH_OBSERVER(TabContentsObserver, observers_, set_tab_contents(NULL));
+ FOR_EACH_OBSERVER(TabContentsObserver, observers_, TabContentsDestroyed());
net::NetworkChangeNotifier::RemoveOnlineStateObserver(this);
}
diff --git a/content/browser/tab_contents/tab_contents_observer.cc b/content/browser/tab_contents/tab_contents_observer.cc
index a94acbd..c3d9947 100644
--- a/content/browser/tab_contents/tab_contents_observer.cc
+++ b/content/browser/tab_contents/tab_contents_observer.cc
@@ -7,6 +7,25 @@
#include "content/browser/renderer_host/render_view_host.h"
#include "content/browser/tab_contents/tab_contents.h"
+void TabContentsObserver::NavigateToPendingEntry() {
+}
+
+void TabContentsObserver::DidNavigateMainFramePostCommit(
+ const NavigationController::LoadCommittedDetails& details,
+ const ViewHostMsg_FrameNavigate_Params& params) {
+}
+
+void TabContentsObserver::DidNavigateAnyFramePostCommit(
+ const NavigationController::LoadCommittedDetails& details,
+ const ViewHostMsg_FrameNavigate_Params& params) {
+}
+
+void TabContentsObserver::DidStartLoading() {
+}
+
+void TabContentsObserver::DidStopLoading() {
+}
+
TabContentsObserver::TabContentsObserver(TabContents* tab_contents)
: tab_contents_(tab_contents),
routing_id_(tab_contents->render_view_host()->routing_id()) {
@@ -18,6 +37,9 @@ TabContentsObserver::~TabContentsObserver() {
tab_contents_->RemoveObserver(this);
}
+void TabContentsObserver::OnTabContentsDestroyed(TabContents* tab) {
+}
+
bool TabContentsObserver::OnMessageReceived(const IPC::Message& message) {
return false;
}
@@ -30,3 +52,12 @@ bool TabContentsObserver::Send(IPC::Message* message) {
return tab_contents_->render_view_host()->Send(message);
}
+
+void TabContentsObserver::TabContentsDestroyed() {
+ // Do cleanup so that 'this' can safely be deleted from
+ // OnTabContentsDestroyed.
+ tab_contents_->RemoveObserver(this);
+ TabContents* tab = tab_contents_;
+ tab_contents_ = NULL;
+ OnTabContentsDestroyed(tab);
+}
diff --git a/content/browser/tab_contents/tab_contents_observer.h b/content/browser/tab_contents/tab_contents_observer.h
index b8bb343..82fc220 100644
--- a/content/browser/tab_contents/tab_contents_observer.h
+++ b/content/browser/tab_contents/tab_contents_observer.h
@@ -14,17 +14,17 @@ struct ViewHostMsg_FrameNavigate_Params;
// load events from TabContents. They also get a chance to filter IPC messages.
class TabContentsObserver : public IPC::Channel::Listener {
public:
- virtual void NavigateToPendingEntry() { }
+ virtual void NavigateToPendingEntry();
virtual void DidNavigateMainFramePostCommit(
const NavigationController::LoadCommittedDetails& details,
- const ViewHostMsg_FrameNavigate_Params& params) { }
+ const ViewHostMsg_FrameNavigate_Params& params);
virtual void DidNavigateAnyFramePostCommit(
const NavigationController::LoadCommittedDetails& details,
- const ViewHostMsg_FrameNavigate_Params& params) { }
+ const ViewHostMsg_FrameNavigate_Params& params);
- virtual void DidStartLoading() { }
- virtual void DidStopLoading() { }
+ virtual void DidStartLoading();
+ virtual void DidStopLoading();
#if 0
// For unifying with delegate...
@@ -45,6 +45,11 @@ class TabContentsObserver : public IPC::Channel::Listener {
TabContentsObserver(TabContents* tab_contents);
virtual ~TabContentsObserver();
+ // Invoked when the TabContents is being destroyed. Gives subclasses a chance
+ // to cleanup. At the time this is invoked |tab_contents()| returns NULL.
+ // It is safe to delete 'this' from here.
+ virtual void OnTabContentsDestroyed(TabContents* tab);
+
// IPC::Channel::Listener implementation.
virtual bool OnMessageReceived(const IPC::Message& message);
@@ -57,9 +62,12 @@ class TabContentsObserver : public IPC::Channel::Listener {
private:
friend class TabContents;
- void set_tab_contents(TabContents* tc) { tab_contents_ = tc; }
+ // Invoked from TabContents. Invokes OnTabContentsDestroyed and NULL out
+ // |tab_contents_|.
+ void TabContentsDestroyed();
TabContents* tab_contents_;
+
// The routing ID of the associated TabContents.
int routing_id_;