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