Home | History | Annotate | Download | only in extensions
      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