158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)#include "chrome/browser/extensions/api/tabs/tabs_event_router.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/json/json_writer.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/values.h"
97dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "chrome/browser/chrome_notification_types.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/api/tabs/tabs_constants.h"
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/extensions/api/tabs/tabs_windows_api.h"
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/extensions/api/tabs/windows_event_router.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_tab_util.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser.h"
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/browser_iterator.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser_list.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/tabs/tab_strip_model.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/extensions/extension_constants.h"
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/favicon_status.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/navigation_controller.h"
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/navigation_entry.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_service.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_types.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/web_contents.h"
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)using base::DictionaryValue;
2858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)using base::ListValue;
2958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)using base::FundamentalValue;
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::NavigationController;
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::WebContents;
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace extensions {
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)namespace {
3658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
373551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)namespace tabs = api::tabs;
383551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void WillDispatchTabUpdatedEvent(
405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    WebContents* contents,
415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const base::DictionaryValue* changed_properties,
425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    content::BrowserContext* context,
435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const Extension* extension,
445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    base::ListValue* event_args) {
4558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // Overwrite the second argument with the appropriate properties dictionary,
4658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // depending on extension permissions.
475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::DictionaryValue* properties_value = changed_properties->DeepCopy();
4858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  ExtensionTabUtil::ScrubTabValueForExtension(contents,
4958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                              extension,
5058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                              properties_value);
5158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  event_args->Set(1, properties_value);
5258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
5358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // Overwrite the third arg with our tab value as seen by this extension.
5458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  event_args->Set(2, ExtensionTabUtil::CreateTabValue(contents, extension));
5558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
5658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
5758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}  // namespace
5858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
5958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)TabsEventRouter::TabEntry::TabEntry() : complete_waiting_on_load_(false),
6058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                        url_() {
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)base::DictionaryValue* TabsEventRouter::TabEntry::UpdateLoadState(
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const WebContents* contents) {
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The tab may go in & out of loading (for instance if iframes navigate).
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We only want to respond to the first change from loading to !loading after
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the NAV_ENTRY_COMMITTED was fired.
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!complete_waiting_on_load_ || contents->IsLoading())
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Send "complete" state change.
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  complete_waiting_on_load_ = false;
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::DictionaryValue* changed_properties = new base::DictionaryValue();
7458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  changed_properties->SetString(tabs_constants::kStatusKey,
7558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                tabs_constants::kStatusValueComplete);
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return changed_properties;
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)base::DictionaryValue* TabsEventRouter::TabEntry::DidNavigate(
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const WebContents* contents) {
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Send "loading" state change.
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  complete_waiting_on_load_ = true;
835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::DictionaryValue* changed_properties = new base::DictionaryValue();
8458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  changed_properties->SetString(tabs_constants::kStatusKey,
8558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                tabs_constants::kStatusValueLoading);
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (contents->GetURL() != url_) {
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    url_ = contents->GetURL();
8958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    changed_properties->SetString(tabs_constants::kUrlKey, url_.spec());
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return changed_properties;
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
9558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)TabsEventRouter::TabsEventRouter(Profile* profile) : profile_(profile) {
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!profile->IsOffTheRecord());
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BrowserList::AddObserver(this);
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Init() can happen after the browser is running, so catch up with any
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // windows that already exist.
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (chrome::BrowserIterator it; !it.done(); it.Next()) {
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    RegisterForBrowserNotifications(*it);
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Also catch up our internal bookkeeping of tab entries.
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Browser* browser = *it;
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (browser->tab_strip_model()) {
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (int i = 0; i < browser->tab_strip_model()->count(); ++i) {
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        WebContents* contents = browser->tab_strip_model()->GetWebContentsAt(i);
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        int tab_id = ExtensionTabUtil::GetTabId(contents);
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        tab_entries_[tab_id] = TabEntry();
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
11758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)TabsEventRouter::~TabsEventRouter() {
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BrowserList::RemoveObserver(this);
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void TabsEventRouter::OnBrowserAdded(Browser* browser) {
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RegisterForBrowserNotifications(browser);
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
12558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void TabsEventRouter::RegisterForBrowserNotifications(Browser* browser) {
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!profile_->IsSameProfile(browser->profile()))
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Start listening to TabStripModel events for this browser.
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  TabStripModel* tab_strip = browser->tab_strip_model();
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  tab_strip->AddObserver(this);
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (int i = 0; i < tab_strip->count(); ++i) {
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    RegisterForTabNotifications(tab_strip->GetWebContentsAt(i));
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void TabsEventRouter::RegisterForTabNotifications(WebContents* contents) {
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Add(
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content::Source<NavigationController>(&contents->GetController()));
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Observing NOTIFICATION_WEB_CONTENTS_DESTROYED is necessary because it's
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // possible for tabs to be created, detached and then destroyed without
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // ever having been re-attached and closed. This happens in the case of
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // a devtools WebContents that is opened in window, docked, then closed.
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 content::Source<WebContents>(contents));
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  registrar_.Add(this, chrome::NOTIFICATION_FAVICON_UPDATED,
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                 content::Source<WebContents>(contents));
151116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
152116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  ZoomController::FromWebContents(contents)->AddObserver(this);
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void TabsEventRouter::UnregisterForTabNotifications(WebContents* contents) {
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Remove(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content::Source<NavigationController>(&contents->GetController()));
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      content::Source<WebContents>(contents));
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  registrar_.Remove(this, chrome::NOTIFICATION_FAVICON_UPDATED,
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      content::Source<WebContents>(contents));
162116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
163116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  ZoomController::FromWebContents(contents)->RemoveObserver(this);
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
16658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void TabsEventRouter::OnBrowserRemoved(Browser* browser) {
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!profile_->IsSameProfile(browser->profile()))
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Stop listening to TabStripModel events for this browser.
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  browser->tab_strip_model()->RemoveObserver(this);
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
17458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void TabsEventRouter::OnBrowserSetLastActive(Browser* browser) {
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  TabsWindowsAPI* tabs_window_api = TabsWindowsAPI::Get(profile_);
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (tabs_window_api) {
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    tabs_window_api->windows_event_router()->OnActiveWindowChanged(
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        browser ? browser->extension_window_controller() : NULL);
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)static void WillDispatchTabCreatedEvent(WebContents* contents,
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                        bool active,
184f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                                        content::BrowserContext* context,
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                        const Extension* extension,
1865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                        base::ListValue* event_args) {
1875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::DictionaryValue* tab_value = ExtensionTabUtil::CreateTabValue(
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      contents, extension);
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  event_args->Clear();
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  event_args->Append(tab_value);
19158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  tab_value->SetBoolean(tabs_constants::kSelectedKey, active);
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
19458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void TabsEventRouter::TabCreatedAt(WebContents* contents,
19558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                   int index,
19658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                   bool active) {
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
1985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  scoped_ptr<base::ListValue> args(new base::ListValue);
1993551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  scoped_ptr<Event> event(new Event(tabs::OnCreated::kEventName, args.Pass()));
200f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  event->restrict_to_browser_context = profile;
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  event->user_gesture = EventRouter::USER_GESTURE_NOT_ENABLED;
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  event->will_dispatch_callback =
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::Bind(&WillDispatchTabCreatedEvent, contents, active);
2040529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch  EventRouter::Get(profile)->BroadcastEvent(event.Pass());
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RegisterForTabNotifications(contents);
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void TabsEventRouter::TabInsertedAt(WebContents* contents,
210010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                    int index,
211010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                                    bool active) {
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If tab is new, send created event.
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int tab_id = ExtensionTabUtil::GetTabId(contents);
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!GetTabEntry(contents)) {
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tab_entries_[tab_id] = TabEntry();
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TabCreatedAt(contents, index, active);
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  scoped_ptr<base::ListValue> args(new base::ListValue);
22258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  args->Append(new FundamentalValue(tab_id));
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::DictionaryValue* object_args = new base::DictionaryValue();
22558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  object_args->Set(tabs_constants::kNewWindowIdKey,
22658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                   new FundamentalValue(
22758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                       ExtensionTabUtil::GetWindowIdOfTab(contents)));
22858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  object_args->Set(tabs_constants::kNewPositionKey,
22958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                   new FundamentalValue(index));
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  args->Append(object_args);
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
2333551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  DispatchEvent(profile, tabs::OnAttached::kEventName, args.Pass(),
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                EventRouter::USER_GESTURE_UNKNOWN);
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
23758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void TabsEventRouter::TabDetachedAt(WebContents* contents, int index) {
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!GetTabEntry(contents)) {
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The tab was removed. Don't send detach event.
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  scoped_ptr<base::ListValue> args(new base::ListValue);
2443551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  args->Append(
24558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      new FundamentalValue(ExtensionTabUtil::GetTabId(contents)));
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::DictionaryValue* object_args = new base::DictionaryValue();
24858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  object_args->Set(tabs_constants::kOldWindowIdKey,
24958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                   new FundamentalValue(
25058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                       ExtensionTabUtil::GetWindowIdOfTab(contents)));
25158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  object_args->Set(tabs_constants::kOldPositionKey,
25258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                   new FundamentalValue(index));
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  args->Append(object_args);
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
25658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  DispatchEvent(profile,
25758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                tabs::OnDetached::kEventName,
25858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                args.Pass(),
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                EventRouter::USER_GESTURE_UNKNOWN);
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
26258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void TabsEventRouter::TabClosingAt(TabStripModel* tab_strip_model,
26358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                   WebContents* contents,
26458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                   int index) {
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int tab_id = ExtensionTabUtil::GetTabId(contents);
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  scoped_ptr<base::ListValue> args(new base::ListValue);
26858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  args->Append(new FundamentalValue(tab_id));
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::DictionaryValue* object_args = new base::DictionaryValue();
27158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  object_args->SetInteger(tabs_constants::kWindowIdKey,
2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                          ExtensionTabUtil::GetWindowIdOfTab(contents));
27358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  object_args->SetBoolean(tabs_constants::kWindowClosing,
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          tab_strip_model->closing_all());
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  args->Append(object_args);
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
27858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  DispatchEvent(profile,
27958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                tabs::OnRemoved::kEventName,
28058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                args.Pass(),
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                EventRouter::USER_GESTURE_UNKNOWN);
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int removed_count = tab_entries_.erase(tab_id);
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_GT(removed_count, 0);
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UnregisterForTabNotifications(contents);
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
28958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void TabsEventRouter::ActiveTabChanged(WebContents* old_contents,
29058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                       WebContents* new_contents,
29158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                       int index,
29258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                       int reason) {
2935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  scoped_ptr<base::ListValue> args(new base::ListValue);
2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int tab_id = ExtensionTabUtil::GetTabId(new_contents);
29558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  args->Append(new FundamentalValue(tab_id));
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::DictionaryValue* object_args = new base::DictionaryValue();
29858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  object_args->Set(tabs_constants::kWindowIdKey,
29958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                   new FundamentalValue(
30058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                       ExtensionTabUtil::GetWindowIdOfTab(new_contents)));
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  args->Append(object_args);
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The onActivated event replaced onActiveChanged and onSelectionChanged. The
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // deprecated events take two arguments: tabId, {windowId}.
3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Profile* profile =
3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      Profile::FromBrowserContext(new_contents->GetBrowserContext());
307c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  EventRouter::UserGestureState gesture =
308c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      reason & CHANGE_REASON_USER_GESTURE
309c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      ? EventRouter::USER_GESTURE_ENABLED
310c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      : EventRouter::USER_GESTURE_NOT_ENABLED;
31158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  DispatchEvent(profile,
31258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                tabs::OnSelectionChanged::kEventName,
3135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                scoped_ptr<base::ListValue>(args->DeepCopy()),
31458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                gesture);
31558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  DispatchEvent(profile,
31658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                tabs::OnActiveChanged::kEventName,
3175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                scoped_ptr<base::ListValue>(args->DeepCopy()),
31858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                gesture);
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The onActivated event takes one argument: {windowId, tabId}.
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  args->Remove(0, NULL);
32258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  object_args->Set(tabs_constants::kTabIdKey,
32358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                   new FundamentalValue(tab_id));
3243551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  DispatchEvent(profile, tabs::OnActivated::kEventName, args.Pass(), gesture);
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
32758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void TabsEventRouter::TabSelectionChanged(
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TabStripModel* tab_strip_model,
3292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const ui::ListSelectionModel& old_model) {
3302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ui::ListSelectionModel::SelectedIndices new_selection =
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tab_strip_model->selection_model().selected_indices();
3325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  scoped_ptr<base::ListValue> all_tabs(new base::ListValue);
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < new_selection.size(); ++i) {
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int index = new_selection[i];
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    WebContents* contents = tab_strip_model->GetWebContentsAt(index);
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!contents)
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int tab_id = ExtensionTabUtil::GetTabId(contents);
34058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    all_tabs->Append(new FundamentalValue(tab_id));
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  scoped_ptr<base::ListValue> args(new base::ListValue);
3445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  scoped_ptr<base::DictionaryValue> select_info(new base::DictionaryValue);
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
34658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  select_info->Set(
34758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      tabs_constants::kWindowIdKey,
34858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      new FundamentalValue(
34958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)          ExtensionTabUtil::GetWindowIdOfTabStripModel(tab_strip_model)));
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
35158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  select_info->Set(tabs_constants::kTabIdsKey, all_tabs.release());
35258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  args->Append(select_info.release());
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The onHighlighted event replaced onHighlightChanged.
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Profile* profile = tab_strip_model->profile();
35658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  DispatchEvent(profile,
35758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                tabs::OnHighlightChanged::kEventName,
3585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                scoped_ptr<base::ListValue>(args->DeepCopy()),
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                EventRouter::USER_GESTURE_UNKNOWN);
36058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  DispatchEvent(profile,
36158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                tabs::OnHighlighted::kEventName,
36258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                args.Pass(),
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                EventRouter::USER_GESTURE_UNKNOWN);
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
36658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void TabsEventRouter::TabMoved(WebContents* contents,
36758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                               int from_index,
36858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                               int to_index) {
3695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  scoped_ptr<base::ListValue> args(new base::ListValue);
3703551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  args->Append(
37158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      new FundamentalValue(ExtensionTabUtil::GetTabId(contents)));
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::DictionaryValue* object_args = new base::DictionaryValue();
37458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  object_args->Set(tabs_constants::kWindowIdKey,
37558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                   new FundamentalValue(
37658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                       ExtensionTabUtil::GetWindowIdOfTab(contents)));
37758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  object_args->Set(tabs_constants::kFromIndexKey,
37858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                   new FundamentalValue(from_index));
37958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  object_args->Set(tabs_constants::kToIndexKey,
38058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                   new FundamentalValue(to_index));
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  args->Append(object_args);
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
38458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  DispatchEvent(profile,
38558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                tabs::OnMoved::kEventName,
38658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                args.Pass(),
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                EventRouter::USER_GESTURE_UNKNOWN);
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
39058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void TabsEventRouter::TabUpdated(WebContents* contents, bool did_navigate) {
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TabEntry* entry = GetTabEntry(contents);
3925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  scoped_ptr<base::DictionaryValue> changed_properties;
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
39458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  CHECK(entry);
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (did_navigate)
3972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    changed_properties.reset(entry->DidNavigate(contents));
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
3992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    changed_properties.reset(entry->UpdateLoadState(contents));
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (changed_properties)
4022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DispatchTabUpdatedEvent(contents, changed_properties.Pass());
4032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
40558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void TabsEventRouter::FaviconUrlUpdated(WebContents* contents) {
4062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    content::NavigationEntry* entry =
4073551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        contents->GetController().GetVisibleEntry();
4082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!entry || !entry->GetFavicon().valid)
4092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return;
4105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    scoped_ptr<base::DictionaryValue> changed_properties(
4115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        new base::DictionaryValue);
4122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    changed_properties->SetString(
41358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        tabs_constants::kFaviconUrlKey,
4142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        entry->GetFavicon().url.possibly_invalid_spec());
4152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DispatchTabUpdatedEvent(contents, changed_properties.Pass());
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
41858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void TabsEventRouter::DispatchEvent(
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Profile* profile,
4204e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    const std::string& event_name,
4215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    scoped_ptr<base::ListValue> args,
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    EventRouter::UserGestureState user_gesture) {
4230529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch  EventRouter* event_router = EventRouter::Get(profile);
4240529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch  if (!profile_->IsSameProfile(profile) || !event_router)
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_ptr<Event> event(new Event(event_name, args.Pass()));
428f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  event->restrict_to_browser_context = profile;
4292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  event->user_gesture = user_gesture;
4300529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch  event_router->BroadcastEvent(event.Pass());
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
43358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void TabsEventRouter::DispatchSimpleBrowserEvent(
4344e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    Profile* profile, const int window_id, const std::string& event_name) {
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!profile_->IsSameProfile(profile))
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  scoped_ptr<base::ListValue> args(new base::ListValue);
43958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  args->Append(new FundamentalValue(window_id));
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
44158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  DispatchEvent(profile,
44258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                event_name,
44358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                args.Pass(),
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                EventRouter::USER_GESTURE_UNKNOWN);
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
44758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void TabsEventRouter::DispatchTabUpdatedEvent(
4485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    WebContents* contents,
4495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    scoped_ptr<base::DictionaryValue> changed_properties) {
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(changed_properties);
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(contents);
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The state of the tab (as seen from the extension point of view) has
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // changed.  Send a notification to the extension.
4555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  scoped_ptr<base::ListValue> args_base(new base::ListValue);
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // First arg: The id of the tab that changed.
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  args_base->AppendInteger(ExtensionTabUtil::GetTabId(contents));
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Second arg: An object containing the changes to the tab state.  Filled in
4612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // by WillDispatchTabUpdatedEvent as a copy of changed_properties, if the
4622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // extension has the tabs permission.
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Third arg: An object containing the state of the tab. Filled in by
4652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // WillDispatchTabUpdatedEvent.
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
46858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  scoped_ptr<Event> event(
46958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      new Event(tabs::OnUpdated::kEventName, args_base.Pass()));
470f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  event->restrict_to_browser_context = profile;
4712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  event->user_gesture = EventRouter::USER_GESTURE_NOT_ENABLED;
4722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  event->will_dispatch_callback =
4732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      base::Bind(&WillDispatchTabUpdatedEvent,
47458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                 contents,
47558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                 changed_properties.get());
4760529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch  EventRouter::Get(profile)->BroadcastEvent(event.Pass());
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
479010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)TabsEventRouter::TabEntry* TabsEventRouter::GetTabEntry(WebContents* contents) {
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int tab_id = ExtensionTabUtil::GetTabId(contents);
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::map<int, TabEntry>::iterator i = tab_entries_.find(tab_id);
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tab_entries_.end() == i)
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return &i->second;
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
48758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void TabsEventRouter::Observe(int type,
48858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                              const content::NotificationSource& source,
48958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                              const content::NotificationDetails& details) {
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (type == content::NOTIFICATION_NAV_ENTRY_COMMITTED) {
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NavigationController* source_controller =
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        content::Source<NavigationController>(source).ptr();
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TabUpdated(source_controller->GetWebContents(), true);
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (type == content::NOTIFICATION_WEB_CONTENTS_DESTROYED) {
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Tab was destroyed after being detached (without being re-attached).
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    WebContents* contents = content::Source<WebContents>(source).ptr();
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    registrar_.Remove(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        content::Source<NavigationController>(&contents->GetController()));
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        content::Source<WebContents>(contents));
5012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    registrar_.Remove(this, chrome::NOTIFICATION_FAVICON_UPDATED,
5022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        content::Source<WebContents>(contents));
5032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else if (type == chrome::NOTIFICATION_FAVICON_UPDATED) {
50458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    bool icon_url_changed = *content::Details<bool>(details).ptr();
50558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    if (icon_url_changed)
50658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      FaviconUrlUpdated(content::Source<WebContents>(source).ptr());
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED();
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
51258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void TabsEventRouter::TabChangedAt(WebContents* contents,
51358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                   int index,
51458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                   TabChangeType change_type) {
5152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  TabUpdated(contents, false);
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
51858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void TabsEventRouter::TabReplacedAt(TabStripModel* tab_strip_model,
51958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                    WebContents* old_contents,
52058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                    WebContents* new_contents,
52158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                    int index) {
5222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Notify listeners that the next tabs closing or being added are due to
5232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // WebContents being swapped.
5242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int new_tab_id = ExtensionTabUtil::GetTabId(new_contents);
5252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int old_tab_id = ExtensionTabUtil::GetTabId(old_contents);
5265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  scoped_ptr<base::ListValue> args(new base::ListValue);
52758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  args->Append(new FundamentalValue(new_tab_id));
52858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  args->Append(new FundamentalValue(old_tab_id));
5292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DispatchEvent(Profile::FromBrowserContext(new_contents->GetBrowserContext()),
5313551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                tabs::OnReplaced::kEventName,
5322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                args.Pass(),
5332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                EventRouter::USER_GESTURE_UNKNOWN);
5342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Update tab_entries_.
5362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const int removed_count = tab_entries_.erase(old_tab_id);
5372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_GT(removed_count, 0);
5382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  UnregisterForTabNotifications(old_contents);
5392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!GetTabEntry(new_contents)) {
5412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    tab_entries_[new_tab_id] = TabEntry();
5422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    RegisterForTabNotifications(new_contents);
5432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
54658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)void TabsEventRouter::TabPinnedStateChanged(WebContents* contents, int index) {
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TabStripModel* tab_strip = NULL;
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int tab_index;
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (ExtensionTabUtil::GetTabStripModel(contents, &tab_strip, &tab_index)) {
5515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    scoped_ptr<base::DictionaryValue> changed_properties(
5525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        new base::DictionaryValue());
55358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    changed_properties->SetBoolean(tabs_constants::kPinnedKey,
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   tab_strip->IsTabPinned(tab_index));
5552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DispatchTabUpdatedEvent(contents, changed_properties.Pass());
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
559116680a4aac90f2aa7413d9095a592090648e557Ben Murdochvoid TabsEventRouter::OnZoomChanged(
560116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    const ZoomController::ZoomChangedEventData& data) {
561116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  DCHECK(data.web_contents);
562116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  int tab_id = ExtensionTabUtil::GetTabId(data.web_contents);
563116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (tab_id < 0)
564116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return;
565116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
566116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // Prepare the zoom change information.
567116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  api::tabs::OnZoomChange::ZoomChangeInfo zoom_change_info;
568116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  zoom_change_info.tab_id = tab_id;
569116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  zoom_change_info.old_zoom_factor =
570116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      content::ZoomLevelToZoomFactor(data.old_zoom_level);
571116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  zoom_change_info.new_zoom_factor =
572116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      content::ZoomLevelToZoomFactor(data.new_zoom_level);
573116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  ZoomModeToZoomSettings(data.zoom_mode,
574116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                         &zoom_change_info.zoom_settings);
575116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
576116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // Dispatch the |onZoomChange| event.
577116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  Profile* profile = Profile::FromBrowserContext(
578116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      data.web_contents->GetBrowserContext());
579116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  DispatchEvent(profile,
580116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                tabs::OnZoomChange::kEventName,
581116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                api::tabs::OnZoomChange::Create(zoom_change_info),
582116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                EventRouter::USER_GESTURE_UNKNOWN);
583116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
584116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace extensions
586