| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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/browser_event_router.h" | |
| 6 | |
| 7 #include "base/json/json_writer.h" | |
| 8 #include "base/values.h" | |
| 9 #include "chrome/browser/chrome_notification_types.h" | |
| 10 #include "chrome/browser/extensions/api/tabs/tabs_constants.h" | |
| 11 #include "chrome/browser/extensions/api/tabs/tabs_windows_api.h" | |
| 12 #include "chrome/browser/extensions/api/tabs/windows_event_router.h" | |
| 13 #include "chrome/browser/extensions/extension_service.h" | |
| 14 #include "chrome/browser/extensions/extension_system.h" | |
| 15 #include "chrome/browser/extensions/extension_tab_util.h" | |
| 16 #include "chrome/browser/extensions/window_controller.h" | |
| 17 #include "chrome/browser/profiles/profile.h" | |
| 18 #include "chrome/browser/ui/browser.h" | |
| 19 #include "chrome/browser/ui/browser_iterator.h" | |
| 20 #include "chrome/browser/ui/browser_list.h" | |
| 21 #include "chrome/browser/ui/tabs/tab_strip_model.h" | |
| 22 #include "chrome/common/extensions/extension_constants.h" | |
| 23 #include "content/public/browser/favicon_status.h" | |
| 24 #include "content/public/browser/navigation_controller.h" | |
| 25 #include "content/public/browser/navigation_entry.h" | |
| 26 #include "content/public/browser/notification_service.h" | |
| 27 #include "content/public/browser/notification_types.h" | |
| 28 #include "content/public/browser/web_contents.h" | |
| 29 | |
| 30 namespace tab_keys = extensions::tabs_constants; | |
| 31 | |
| 32 using content::NavigationController; | |
| 33 using content::WebContents; | |
| 34 | |
| 35 namespace extensions { | |
| 36 | |
| 37 namespace tabs = api::tabs; | |
| 38 | |
| 39 BrowserEventRouter::TabEntry::TabEntry() | |
| 40 : complete_waiting_on_load_(false), | |
| 41 url_() { | |
| 42 } | |
| 43 | |
| 44 DictionaryValue* BrowserEventRouter::TabEntry::UpdateLoadState( | |
| 45 const WebContents* contents) { | |
| 46 // The tab may go in & out of loading (for instance if iframes navigate). | |
| 47 // We only want to respond to the first change from loading to !loading after | |
| 48 // the NAV_ENTRY_COMMITTED was fired. | |
| 49 if (!complete_waiting_on_load_ || contents->IsLoading()) | |
| 50 return NULL; | |
| 51 | |
| 52 // Send "complete" state change. | |
| 53 complete_waiting_on_load_ = false; | |
| 54 DictionaryValue* changed_properties = new DictionaryValue(); | |
| 55 changed_properties->SetString(tab_keys::kStatusKey, | |
| 56 tab_keys::kStatusValueComplete); | |
| 57 return changed_properties; | |
| 58 } | |
| 59 | |
| 60 DictionaryValue* BrowserEventRouter::TabEntry::DidNavigate( | |
| 61 const WebContents* contents) { | |
| 62 // Send "loading" state change. | |
| 63 complete_waiting_on_load_ = true; | |
| 64 DictionaryValue* changed_properties = new DictionaryValue(); | |
| 65 changed_properties->SetString(tab_keys::kStatusKey, | |
| 66 tab_keys::kStatusValueLoading); | |
| 67 | |
| 68 if (contents->GetURL() != url_) { | |
| 69 url_ = contents->GetURL(); | |
| 70 changed_properties->SetString(tab_keys::kUrlKey, url_.spec()); | |
| 71 } | |
| 72 | |
| 73 return changed_properties; | |
| 74 } | |
| 75 | |
| 76 BrowserEventRouter::BrowserEventRouter(Profile* profile) | |
| 77 : profile_(profile) { | |
| 78 DCHECK(!profile->IsOffTheRecord()); | |
| 79 | |
| 80 BrowserList::AddObserver(this); | |
| 81 | |
| 82 // Init() can happen after the browser is running, so catch up with any | |
| 83 // windows that already exist. | |
| 84 for (chrome::BrowserIterator it; !it.done(); it.Next()) { | |
| 85 RegisterForBrowserNotifications(*it); | |
| 86 | |
| 87 // Also catch up our internal bookkeeping of tab entries. | |
| 88 Browser* browser = *it; | |
| 89 if (browser->tab_strip_model()) { | |
| 90 for (int i = 0; i < browser->tab_strip_model()->count(); ++i) { | |
| 91 WebContents* contents = browser->tab_strip_model()->GetWebContentsAt(i); | |
| 92 int tab_id = ExtensionTabUtil::GetTabId(contents); | |
| 93 tab_entries_[tab_id] = TabEntry(); | |
| 94 } | |
| 95 } | |
| 96 } | |
| 97 } | |
| 98 | |
| 99 BrowserEventRouter::~BrowserEventRouter() { | |
| 100 BrowserList::RemoveObserver(this); | |
| 101 } | |
| 102 | |
| 103 void BrowserEventRouter::OnBrowserAdded(Browser* browser) { | |
| 104 RegisterForBrowserNotifications(browser); | |
| 105 } | |
| 106 | |
| 107 void BrowserEventRouter::RegisterForBrowserNotifications(Browser* browser) { | |
| 108 if (!profile_->IsSameProfile(browser->profile())) | |
| 109 return; | |
| 110 // Start listening to TabStripModel events for this browser. | |
| 111 TabStripModel* tab_strip = browser->tab_strip_model(); | |
| 112 tab_strip->AddObserver(this); | |
| 113 | |
| 114 for (int i = 0; i < tab_strip->count(); ++i) { | |
| 115 RegisterForTabNotifications(tab_strip->GetWebContentsAt(i)); | |
| 116 } | |
| 117 } | |
| 118 | |
| 119 void BrowserEventRouter::RegisterForTabNotifications(WebContents* contents) { | |
| 120 registrar_.Add( | |
| 121 this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, | |
| 122 content::Source<NavigationController>(&contents->GetController())); | |
| 123 | |
| 124 // Observing NOTIFICATION_WEB_CONTENTS_DESTROYED is necessary because it's | |
| 125 // possible for tabs to be created, detached and then destroyed without | |
| 126 // ever having been re-attached and closed. This happens in the case of | |
| 127 // a devtools WebContents that is opened in window, docked, then closed. | |
| 128 registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, | |
| 129 content::Source<WebContents>(contents)); | |
| 130 | |
| 131 registrar_.Add(this, chrome::NOTIFICATION_FAVICON_UPDATED, | |
| 132 content::Source<WebContents>(contents)); | |
| 133 } | |
| 134 | |
| 135 void BrowserEventRouter::UnregisterForTabNotifications(WebContents* contents) { | |
| 136 registrar_.Remove(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, | |
| 137 content::Source<NavigationController>(&contents->GetController())); | |
| 138 registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, | |
| 139 content::Source<WebContents>(contents)); | |
| 140 registrar_.Remove(this, chrome::NOTIFICATION_FAVICON_UPDATED, | |
| 141 content::Source<WebContents>(contents)); | |
| 142 } | |
| 143 | |
| 144 void BrowserEventRouter::OnBrowserRemoved(Browser* browser) { | |
| 145 if (!profile_->IsSameProfile(browser->profile())) | |
| 146 return; | |
| 147 | |
| 148 // Stop listening to TabStripModel events for this browser. | |
| 149 browser->tab_strip_model()->RemoveObserver(this); | |
| 150 } | |
| 151 | |
| 152 void BrowserEventRouter::OnBrowserSetLastActive(Browser* browser) { | |
| 153 TabsWindowsAPI* tabs_window_api = TabsWindowsAPI::Get(profile_); | |
| 154 if (tabs_window_api) { | |
| 155 tabs_window_api->windows_event_router()->OnActiveWindowChanged( | |
| 156 browser ? browser->extension_window_controller() : NULL); | |
| 157 } | |
| 158 } | |
| 159 | |
| 160 static void WillDispatchTabCreatedEvent(WebContents* contents, | |
| 161 bool active, | |
| 162 Profile* profile, | |
| 163 const Extension* extension, | |
| 164 base::ListValue* event_args) { | |
| 165 DictionaryValue* tab_value = ExtensionTabUtil::CreateTabValue( | |
| 166 contents, extension); | |
| 167 event_args->Clear(); | |
| 168 event_args->Append(tab_value); | |
| 169 tab_value->SetBoolean(tab_keys::kSelectedKey, active); | |
| 170 } | |
| 171 | |
| 172 void BrowserEventRouter::TabCreatedAt(WebContents* contents, | |
| 173 int index, | |
| 174 bool active) { | |
| 175 Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext()); | |
| 176 scoped_ptr<base::ListValue> args(new base::ListValue()); | |
| 177 scoped_ptr<Event> event(new Event(tabs::OnCreated::kEventName, args.Pass())); | |
| 178 event->restrict_to_profile = profile; | |
| 179 event->user_gesture = EventRouter::USER_GESTURE_NOT_ENABLED; | |
| 180 event->will_dispatch_callback = | |
| 181 base::Bind(&WillDispatchTabCreatedEvent, contents, active); | |
| 182 ExtensionSystem::Get(profile)->event_router()->BroadcastEvent(event.Pass()); | |
| 183 | |
| 184 RegisterForTabNotifications(contents); | |
| 185 } | |
| 186 | |
| 187 void BrowserEventRouter::TabInsertedAt(WebContents* contents, | |
| 188 int index, | |
| 189 bool active) { | |
| 190 // If tab is new, send created event. | |
| 191 int tab_id = ExtensionTabUtil::GetTabId(contents); | |
| 192 if (!GetTabEntry(contents)) { | |
| 193 tab_entries_[tab_id] = TabEntry(); | |
| 194 | |
| 195 TabCreatedAt(contents, index, active); | |
| 196 return; | |
| 197 } | |
| 198 | |
| 199 scoped_ptr<base::ListValue> args(new base::ListValue()); | |
| 200 args->Append(new base::FundamentalValue(tab_id)); | |
| 201 | |
| 202 DictionaryValue* object_args = new DictionaryValue(); | |
| 203 object_args->Set(tab_keys::kNewWindowIdKey, new base::FundamentalValue( | |
| 204 ExtensionTabUtil::GetWindowIdOfTab(contents))); | |
| 205 object_args->Set(tab_keys::kNewPositionKey, new base::FundamentalValue( | |
| 206 index)); | |
| 207 args->Append(object_args); | |
| 208 | |
| 209 Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext()); | |
| 210 DispatchEvent(profile, tabs::OnAttached::kEventName, args.Pass(), | |
| 211 EventRouter::USER_GESTURE_UNKNOWN); | |
| 212 } | |
| 213 | |
| 214 void BrowserEventRouter::TabDetachedAt(WebContents* contents, int index) { | |
| 215 if (!GetTabEntry(contents)) { | |
| 216 // The tab was removed. Don't send detach event. | |
| 217 return; | |
| 218 } | |
| 219 | |
| 220 scoped_ptr<base::ListValue> args(new base::ListValue()); | |
| 221 args->Append( | |
| 222 new base::FundamentalValue(ExtensionTabUtil::GetTabId(contents))); | |
| 223 | |
| 224 DictionaryValue* object_args = new DictionaryValue(); | |
| 225 object_args->Set(tab_keys::kOldWindowIdKey, new base::FundamentalValue( | |
| 226 ExtensionTabUtil::GetWindowIdOfTab(contents))); | |
| 227 object_args->Set(tab_keys::kOldPositionKey, new base::FundamentalValue( | |
| 228 index)); | |
| 229 args->Append(object_args); | |
| 230 | |
| 231 Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext()); | |
| 232 DispatchEvent(profile, tabs::OnDetached::kEventName, args.Pass(), | |
| 233 EventRouter::USER_GESTURE_UNKNOWN); | |
| 234 } | |
| 235 | |
| 236 void BrowserEventRouter::TabClosingAt(TabStripModel* tab_strip_model, | |
| 237 WebContents* contents, | |
| 238 int index) { | |
| 239 int tab_id = ExtensionTabUtil::GetTabId(contents); | |
| 240 | |
| 241 scoped_ptr<base::ListValue> args(new base::ListValue()); | |
| 242 args->Append(new base::FundamentalValue(tab_id)); | |
| 243 | |
| 244 DictionaryValue* object_args = new DictionaryValue(); | |
| 245 object_args->SetInteger(tab_keys::kWindowIdKey, | |
| 246 ExtensionTabUtil::GetWindowIdOfTab(contents)); | |
| 247 object_args->SetBoolean(tab_keys::kWindowClosing, | |
| 248 tab_strip_model->closing_all()); | |
| 249 args->Append(object_args); | |
| 250 | |
| 251 Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext()); | |
| 252 DispatchEvent(profile, tabs::OnRemoved::kEventName, args.Pass(), | |
| 253 EventRouter::USER_GESTURE_UNKNOWN); | |
| 254 | |
| 255 int removed_count = tab_entries_.erase(tab_id); | |
| 256 DCHECK_GT(removed_count, 0); | |
| 257 | |
| 258 UnregisterForTabNotifications(contents); | |
| 259 } | |
| 260 | |
| 261 void BrowserEventRouter::ActiveTabChanged(WebContents* old_contents, | |
| 262 WebContents* new_contents, | |
| 263 int index, | |
| 264 int reason) { | |
| 265 scoped_ptr<base::ListValue> args(new base::ListValue()); | |
| 266 int tab_id = ExtensionTabUtil::GetTabId(new_contents); | |
| 267 args->Append(new base::FundamentalValue(tab_id)); | |
| 268 | |
| 269 DictionaryValue* object_args = new DictionaryValue(); | |
| 270 object_args->Set(tab_keys::kWindowIdKey, new base::FundamentalValue( | |
| 271 ExtensionTabUtil::GetWindowIdOfTab(new_contents))); | |
| 272 args->Append(object_args); | |
| 273 | |
| 274 // The onActivated event replaced onActiveChanged and onSelectionChanged. The | |
| 275 // deprecated events take two arguments: tabId, {windowId}. | |
| 276 Profile* profile = | |
| 277 Profile::FromBrowserContext(new_contents->GetBrowserContext()); | |
| 278 EventRouter::UserGestureState gesture = | |
| 279 reason & CHANGE_REASON_USER_GESTURE | |
| 280 ? EventRouter::USER_GESTURE_ENABLED | |
| 281 : EventRouter::USER_GESTURE_NOT_ENABLED; | |
| 282 DispatchEvent(profile, tabs::OnSelectionChanged::kEventName, | |
| 283 scoped_ptr<base::ListValue>(args->DeepCopy()), gesture); | |
| 284 DispatchEvent(profile, tabs::OnActiveChanged::kEventName, | |
| 285 scoped_ptr<base::ListValue>(args->DeepCopy()), gesture); | |
| 286 | |
| 287 // The onActivated event takes one argument: {windowId, tabId}. | |
| 288 args->Remove(0, NULL); | |
| 289 object_args->Set(tab_keys::kTabIdKey, new base::FundamentalValue(tab_id)); | |
| 290 DispatchEvent(profile, tabs::OnActivated::kEventName, args.Pass(), gesture); | |
| 291 } | |
| 292 | |
| 293 void BrowserEventRouter::TabSelectionChanged( | |
| 294 TabStripModel* tab_strip_model, | |
| 295 const ui::ListSelectionModel& old_model) { | |
| 296 ui::ListSelectionModel::SelectedIndices new_selection = | |
| 297 tab_strip_model->selection_model().selected_indices(); | |
| 298 base::ListValue* all = new base::ListValue(); | |
| 299 | |
| 300 for (size_t i = 0; i < new_selection.size(); ++i) { | |
| 301 int index = new_selection[i]; | |
| 302 WebContents* contents = tab_strip_model->GetWebContentsAt(index); | |
| 303 if (!contents) | |
| 304 break; | |
| 305 int tab_id = ExtensionTabUtil::GetTabId(contents); | |
| 306 all->Append(new base::FundamentalValue(tab_id)); | |
| 307 } | |
| 308 | |
| 309 scoped_ptr<base::ListValue> args(new base::ListValue()); | |
| 310 DictionaryValue* select_info = new DictionaryValue(); | |
| 311 | |
| 312 select_info->Set(tab_keys::kWindowIdKey, new base::FundamentalValue( | |
| 313 ExtensionTabUtil::GetWindowIdOfTabStripModel(tab_strip_model))); | |
| 314 | |
| 315 select_info->Set(tab_keys::kTabIdsKey, all); | |
| 316 args->Append(select_info); | |
| 317 | |
| 318 // The onHighlighted event replaced onHighlightChanged. | |
| 319 Profile* profile = tab_strip_model->profile(); | |
| 320 DispatchEvent(profile, tabs::OnHighlightChanged::kEventName, | |
| 321 scoped_ptr<base::ListValue>(args->DeepCopy()), | |
| 322 EventRouter::USER_GESTURE_UNKNOWN); | |
| 323 DispatchEvent(profile, tabs::OnHighlighted::kEventName, args.Pass(), | |
| 324 EventRouter::USER_GESTURE_UNKNOWN); | |
| 325 } | |
| 326 | |
| 327 void BrowserEventRouter::TabMoved(WebContents* contents, | |
| 328 int from_index, | |
| 329 int to_index) { | |
| 330 scoped_ptr<base::ListValue> args(new base::ListValue()); | |
| 331 args->Append( | |
| 332 new base::FundamentalValue(ExtensionTabUtil::GetTabId(contents))); | |
| 333 | |
| 334 DictionaryValue* object_args = new DictionaryValue(); | |
| 335 object_args->Set(tab_keys::kWindowIdKey, new base::FundamentalValue( | |
| 336 ExtensionTabUtil::GetWindowIdOfTab(contents))); | |
| 337 object_args->Set(tab_keys::kFromIndexKey, new base::FundamentalValue( | |
| 338 from_index)); | |
| 339 object_args->Set(tab_keys::kToIndexKey, new base::FundamentalValue( | |
| 340 to_index)); | |
| 341 args->Append(object_args); | |
| 342 | |
| 343 Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext()); | |
| 344 DispatchEvent(profile, tabs::OnMoved::kEventName, args.Pass(), | |
| 345 EventRouter::USER_GESTURE_UNKNOWN); | |
| 346 } | |
| 347 | |
| 348 void BrowserEventRouter::TabUpdated(WebContents* contents, bool did_navigate) { | |
| 349 TabEntry* entry = GetTabEntry(contents); | |
| 350 scoped_ptr<DictionaryValue> changed_properties; | |
| 351 | |
| 352 DCHECK(entry); | |
| 353 | |
| 354 if (did_navigate) | |
| 355 changed_properties.reset(entry->DidNavigate(contents)); | |
| 356 else | |
| 357 changed_properties.reset(entry->UpdateLoadState(contents)); | |
| 358 | |
| 359 if (changed_properties) | |
| 360 DispatchTabUpdatedEvent(contents, changed_properties.Pass()); | |
| 361 } | |
| 362 | |
| 363 void BrowserEventRouter::FaviconUrlUpdated(WebContents* contents, | |
| 364 const bool* icon_url_changed) { | |
| 365 if (!icon_url_changed || !*icon_url_changed) | |
| 366 return; | |
| 367 content::NavigationEntry* entry = | |
| 368 contents->GetController().GetVisibleEntry(); | |
| 369 if (!entry || !entry->GetFavicon().valid) | |
| 370 return; | |
| 371 scoped_ptr<DictionaryValue> changed_properties(new DictionaryValue()); | |
| 372 changed_properties->SetString( | |
| 373 tab_keys::kFaviconUrlKey, | |
| 374 entry->GetFavicon().url.possibly_invalid_spec()); | |
| 375 DispatchTabUpdatedEvent(contents, changed_properties.Pass()); | |
| 376 } | |
| 377 | |
| 378 void BrowserEventRouter::DispatchEvent( | |
| 379 Profile* profile, | |
| 380 const char* event_name, | |
| 381 scoped_ptr<base::ListValue> args, | |
| 382 EventRouter::UserGestureState user_gesture) { | |
| 383 if (!profile_->IsSameProfile(profile) || | |
| 384 !extensions::ExtensionSystem::Get(profile)->event_router()) | |
| 385 return; | |
| 386 | |
| 387 scoped_ptr<Event> event(new Event(event_name, args.Pass())); | |
| 388 event->restrict_to_profile = profile; | |
| 389 event->user_gesture = user_gesture; | |
| 390 ExtensionSystem::Get(profile)->event_router()->BroadcastEvent(event.Pass()); | |
| 391 } | |
| 392 | |
| 393 void BrowserEventRouter::DispatchSimpleBrowserEvent( | |
| 394 Profile* profile, const int window_id, const char* event_name) { | |
| 395 if (!profile_->IsSameProfile(profile)) | |
| 396 return; | |
| 397 | |
| 398 scoped_ptr<base::ListValue> args(new base::ListValue()); | |
| 399 args->Append(new base::FundamentalValue(window_id)); | |
| 400 | |
| 401 DispatchEvent(profile, event_name, args.Pass(), | |
| 402 EventRouter::USER_GESTURE_UNKNOWN); | |
| 403 } | |
| 404 | |
| 405 static void WillDispatchTabUpdatedEvent( | |
| 406 WebContents* contents, | |
| 407 const DictionaryValue* changed_properties, | |
| 408 Profile* profile, | |
| 409 const Extension* extension, | |
| 410 base::ListValue* event_args) { | |
| 411 // Overwrite the second argument with the appropriate properties dictionary, | |
| 412 // depending on extension permissions. | |
| 413 DictionaryValue* properties_value = changed_properties->DeepCopy(); | |
| 414 ExtensionTabUtil::ScrubTabValueForExtension(contents, extension, | |
| 415 properties_value); | |
| 416 event_args->Set(1, properties_value); | |
| 417 | |
| 418 // Overwrite the third arg with our tab value as seen by this extension. | |
| 419 DictionaryValue* tab_value = ExtensionTabUtil::CreateTabValue( | |
| 420 contents, extension); | |
| 421 event_args->Set(2, tab_value); | |
| 422 } | |
| 423 | |
| 424 void BrowserEventRouter::DispatchTabUpdatedEvent( | |
| 425 WebContents* contents, scoped_ptr<DictionaryValue> changed_properties) { | |
| 426 DCHECK(changed_properties); | |
| 427 DCHECK(contents); | |
| 428 | |
| 429 // The state of the tab (as seen from the extension point of view) has | |
| 430 // changed. Send a notification to the extension. | |
| 431 scoped_ptr<base::ListValue> args_base(new base::ListValue()); | |
| 432 | |
| 433 // First arg: The id of the tab that changed. | |
| 434 args_base->AppendInteger(ExtensionTabUtil::GetTabId(contents)); | |
| 435 | |
| 436 // Second arg: An object containing the changes to the tab state. Filled in | |
| 437 // by WillDispatchTabUpdatedEvent as a copy of changed_properties, if the | |
| 438 // extension has the tabs permission. | |
| 439 | |
| 440 // Third arg: An object containing the state of the tab. Filled in by | |
| 441 // WillDispatchTabUpdatedEvent. | |
| 442 Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext()); | |
| 443 | |
| 444 scoped_ptr<Event> event(new Event(tabs::OnUpdated::kEventName, | |
| 445 args_base.Pass())); | |
| 446 event->restrict_to_profile = profile; | |
| 447 event->user_gesture = EventRouter::USER_GESTURE_NOT_ENABLED; | |
| 448 event->will_dispatch_callback = | |
| 449 base::Bind(&WillDispatchTabUpdatedEvent, | |
| 450 contents, changed_properties.get()); | |
| 451 ExtensionSystem::Get(profile)->event_router()->BroadcastEvent(event.Pass()); | |
| 452 } | |
| 453 | |
| 454 BrowserEventRouter::TabEntry* BrowserEventRouter::GetTabEntry( | |
| 455 const WebContents* contents) { | |
| 456 int tab_id = ExtensionTabUtil::GetTabId(contents); | |
| 457 std::map<int, TabEntry>::iterator i = tab_entries_.find(tab_id); | |
| 458 if (tab_entries_.end() == i) | |
| 459 return NULL; | |
| 460 return &i->second; | |
| 461 } | |
| 462 | |
| 463 void BrowserEventRouter::Observe(int type, | |
| 464 const content::NotificationSource& source, | |
| 465 const content::NotificationDetails& details) { | |
| 466 if (type == content::NOTIFICATION_NAV_ENTRY_COMMITTED) { | |
| 467 NavigationController* source_controller = | |
| 468 content::Source<NavigationController>(source).ptr(); | |
| 469 TabUpdated(source_controller->GetWebContents(), true); | |
| 470 } else if (type == content::NOTIFICATION_WEB_CONTENTS_DESTROYED) { | |
| 471 // Tab was destroyed after being detached (without being re-attached). | |
| 472 WebContents* contents = content::Source<WebContents>(source).ptr(); | |
| 473 registrar_.Remove(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED, | |
| 474 content::Source<NavigationController>(&contents->GetController())); | |
| 475 registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, | |
| 476 content::Source<WebContents>(contents)); | |
| 477 registrar_.Remove(this, chrome::NOTIFICATION_FAVICON_UPDATED, | |
| 478 content::Source<WebContents>(contents)); | |
| 479 } else if (type == chrome::NOTIFICATION_FAVICON_UPDATED) { | |
| 480 WebContents* contents = content::Source<WebContents>(source).ptr(); | |
| 481 const bool* icon_url_changed = content::Details<bool>(details).ptr(); | |
| 482 FaviconUrlUpdated(contents, icon_url_changed); | |
| 483 } else { | |
| 484 NOTREACHED(); | |
| 485 } | |
| 486 } | |
| 487 | |
| 488 void BrowserEventRouter::TabChangedAt(WebContents* contents, | |
| 489 int index, | |
| 490 TabChangeType change_type) { | |
| 491 TabUpdated(contents, false); | |
| 492 } | |
| 493 | |
| 494 void BrowserEventRouter::TabReplacedAt(TabStripModel* tab_strip_model, | |
| 495 WebContents* old_contents, | |
| 496 WebContents* new_contents, | |
| 497 int index) { | |
| 498 // Notify listeners that the next tabs closing or being added are due to | |
| 499 // WebContents being swapped. | |
| 500 const int new_tab_id = ExtensionTabUtil::GetTabId(new_contents); | |
| 501 const int old_tab_id = ExtensionTabUtil::GetTabId(old_contents); | |
| 502 scoped_ptr<base::ListValue> args(new base::ListValue()); | |
| 503 args->Append(new base::FundamentalValue(new_tab_id)); | |
| 504 args->Append(new base::FundamentalValue(old_tab_id)); | |
| 505 | |
| 506 DispatchEvent(Profile::FromBrowserContext(new_contents->GetBrowserContext()), | |
| 507 tabs::OnReplaced::kEventName, | |
| 508 args.Pass(), | |
| 509 EventRouter::USER_GESTURE_UNKNOWN); | |
| 510 | |
| 511 // Update tab_entries_. | |
| 512 const int removed_count = tab_entries_.erase(old_tab_id); | |
| 513 DCHECK_GT(removed_count, 0); | |
| 514 UnregisterForTabNotifications(old_contents); | |
| 515 | |
| 516 if (!GetTabEntry(new_contents)) { | |
| 517 tab_entries_[new_tab_id] = TabEntry(); | |
| 518 RegisterForTabNotifications(new_contents); | |
| 519 } | |
| 520 } | |
| 521 | |
| 522 void BrowserEventRouter::TabPinnedStateChanged(WebContents* contents, | |
| 523 int index) { | |
| 524 TabStripModel* tab_strip = NULL; | |
| 525 int tab_index; | |
| 526 | |
| 527 if (ExtensionTabUtil::GetTabStripModel(contents, &tab_strip, &tab_index)) { | |
| 528 scoped_ptr<DictionaryValue> changed_properties(new DictionaryValue()); | |
| 529 changed_properties->SetBoolean(tab_keys::kPinnedKey, | |
| 530 tab_strip->IsTabPinned(tab_index)); | |
| 531 DispatchTabUpdatedEvent(contents, changed_properties.Pass()); | |
| 532 } | |
| 533 } | |
| 534 | |
| 535 void BrowserEventRouter::TabStripEmpty() {} | |
| 536 | |
| 537 } // namespace extensions | |
| OLD | NEW |