• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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/extension_browser_event_router.h"
6 
7 #include "base/json/json_writer.h"
8 #include "base/values.h"
9 #include "chrome/browser/extensions/extension_event_names.h"
10 #include "chrome/browser/extensions/extension_event_router.h"
11 #include "chrome/browser/extensions/extension_page_actions_module_constants.h"
12 #include "chrome/browser/extensions/extension_tabs_module_constants.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/browser/tabs/tab_strip_model.h"
15 #include "chrome/browser/ui/browser.h"
16 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
17 #include "chrome/common/extensions/extension.h"
18 #include "chrome/common/extensions/extension_constants.h"
19 #include "content/browser/tab_contents/navigation_entry.h"
20 #include "content/browser/tab_contents/tab_contents.h"
21 #include "content/common/notification_service.h"
22 
23 namespace events = extension_event_names;
24 namespace tab_keys = extension_tabs_module_constants;
25 namespace page_action_keys = extension_page_actions_module_constants;
26 
TabEntry()27 ExtensionBrowserEventRouter::TabEntry::TabEntry()
28     : complete_waiting_on_load_(false),
29       url_() {
30 }
31 
UpdateLoadState(const TabContents * contents)32 DictionaryValue* ExtensionBrowserEventRouter::TabEntry::UpdateLoadState(
33     const TabContents* contents) {
34   // The tab may go in & out of loading (for instance if iframes navigate).
35   // We only want to respond to the first change from loading to !loading after
36   // the NAV_ENTRY_COMMITTED was fired.
37   if (!complete_waiting_on_load_ || contents->is_loading())
38     return NULL;
39 
40   // Send "complete" state change.
41   complete_waiting_on_load_ = false;
42   DictionaryValue* changed_properties = new DictionaryValue();
43   changed_properties->SetString(tab_keys::kStatusKey,
44       tab_keys::kStatusValueComplete);
45   return changed_properties;
46 }
47 
DidNavigate(const TabContents * contents)48 DictionaryValue* ExtensionBrowserEventRouter::TabEntry::DidNavigate(
49     const TabContents* contents) {
50   // Send "loading" state change.
51   complete_waiting_on_load_ = true;
52   DictionaryValue* changed_properties = new DictionaryValue();
53   changed_properties->SetString(tab_keys::kStatusKey,
54       tab_keys::kStatusValueLoading);
55 
56   if (contents->GetURL() != url_) {
57     url_ = contents->GetURL();
58     changed_properties->SetString(tab_keys::kUrlKey, url_.spec());
59   }
60 
61   return changed_properties;
62 }
63 
DispatchEvent(Profile * profile,const char * event_name,const std::string & json_args)64 static void DispatchEvent(Profile* profile,
65                           const char* event_name,
66                           const std::string& json_args) {
67   if (profile->GetExtensionEventRouter()) {
68     profile->GetExtensionEventRouter()->DispatchEventToRenderers(
69         event_name, json_args, profile, GURL());
70   }
71 }
72 
DispatchEventToExtension(Profile * profile,const std::string & extension_id,const char * event_name,const std::string & json_args)73 static void DispatchEventToExtension(Profile* profile,
74                                      const std::string& extension_id,
75                                      const char* event_name,
76                                      const std::string& json_args) {
77   if (profile->GetExtensionEventRouter()) {
78     profile->GetExtensionEventRouter()->DispatchEventToExtension(
79         extension_id, event_name, json_args, profile, GURL());
80   }
81 }
82 
DispatchEventWithTab(Profile * profile,const std::string & extension_id,const char * event_name,const TabContents * tab_contents)83 static void DispatchEventWithTab(Profile* profile,
84                                  const std::string& extension_id,
85                                  const char* event_name,
86                                  const TabContents* tab_contents) {
87   ListValue args;
88   args.Append(ExtensionTabUtil::CreateTabValue(tab_contents));
89   std::string json_args;
90   base::JSONWriter::Write(&args, false, &json_args);
91   if (!extension_id.empty()) {
92     DispatchEventToExtension(profile, extension_id, event_name, json_args);
93   } else {
94     DispatchEvent(profile, event_name, json_args);
95   }
96 }
97 
DispatchSimpleBrowserEvent(Profile * profile,const int window_id,const char * event_name)98 static void DispatchSimpleBrowserEvent(Profile* profile,
99                                        const int window_id,
100                                        const char* event_name) {
101   ListValue args;
102   args.Append(Value::CreateIntegerValue(window_id));
103 
104   std::string json_args;
105   base::JSONWriter::Write(&args, false, &json_args);
106 
107   DispatchEvent(profile, event_name, json_args);
108 }
109 
Init()110 void ExtensionBrowserEventRouter::Init() {
111   if (initialized_)
112     return;
113   BrowserList::AddObserver(this);
114 #if defined(TOOLKIT_VIEWS)
115   views::FocusManager::GetWidgetFocusManager()->AddFocusChangeListener(this);
116 #elif defined(TOOLKIT_GTK)
117   ui::ActiveWindowWatcherX::AddObserver(this);
118 #elif defined(OS_MACOSX)
119   // Needed for when no suitable window can be passed to an extension as the
120   // currently focused window.
121   registrar_.Add(this, NotificationType::NO_KEY_WINDOW,
122                  NotificationService::AllSources());
123 #endif
124 
125   // Init() can happen after the browser is running, so catch up with any
126   // windows that already exist.
127   for (BrowserList::const_iterator iter = BrowserList::begin();
128        iter != BrowserList::end(); ++iter) {
129     RegisterForBrowserNotifications(*iter);
130 
131     // Also catch up our internal bookkeeping of tab entries.
132     Browser* browser = *iter;
133     if (browser->tabstrip_model()) {
134       for (int i = 0; i < browser->tabstrip_model()->count(); ++i) {
135         TabContents* contents = browser->GetTabContentsAt(i);
136         int tab_id = ExtensionTabUtil::GetTabId(contents);
137         tab_entries_[tab_id] = TabEntry();
138       }
139     }
140   }
141 
142   initialized_ = true;
143 }
144 
ExtensionBrowserEventRouter(Profile * profile)145 ExtensionBrowserEventRouter::ExtensionBrowserEventRouter(Profile* profile)
146     : initialized_(false),
147       focused_window_id_(extension_misc::kUnknownWindowId),
148       profile_(profile) {
149   DCHECK(!profile->IsOffTheRecord());
150 }
151 
~ExtensionBrowserEventRouter()152 ExtensionBrowserEventRouter::~ExtensionBrowserEventRouter() {
153   BrowserList::RemoveObserver(this);
154 #if defined(TOOLKIT_VIEWS)
155   views::FocusManager::GetWidgetFocusManager()->RemoveFocusChangeListener(this);
156 #elif defined(TOOLKIT_GTK)
157   ui::ActiveWindowWatcherX::RemoveObserver(this);
158 #endif
159 }
160 
OnBrowserAdded(const Browser * browser)161 void ExtensionBrowserEventRouter::OnBrowserAdded(const Browser* browser) {
162   RegisterForBrowserNotifications(browser);
163 }
164 
RegisterForBrowserNotifications(const Browser * browser)165 void ExtensionBrowserEventRouter::RegisterForBrowserNotifications(
166     const Browser* browser) {
167   // Start listening to TabStripModel events for this browser.
168   browser->tabstrip_model()->AddObserver(this);
169 
170   // If this is a new window, it isn't ready at this point, so we register to be
171   // notified when it is. If this is an existing window, this is a no-op that we
172   // just do to reduce code complexity.
173   registrar_.Add(this, NotificationType::BROWSER_WINDOW_READY,
174       Source<const Browser>(browser));
175 
176   if (browser->tabstrip_model()) {
177     for (int i = 0; i < browser->tabstrip_model()->count(); ++i)
178       RegisterForTabNotifications(browser->GetTabContentsAt(i));
179   }
180 }
181 
RegisterForTabNotifications(TabContents * contents)182 void ExtensionBrowserEventRouter::RegisterForTabNotifications(
183     TabContents* contents) {
184   registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED,
185                  Source<NavigationController>(&contents->controller()));
186 
187   // Observing TAB_CONTENTS_DESTROYED is necessary because it's
188   // possible for tabs to be created, detached and then destroyed without
189   // ever having been re-attached and closed. This happens in the case of
190   // a devtools TabContents that is opened in window, docked, then closed.
191   registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED,
192                  Source<TabContents>(contents));
193 }
194 
UnregisterForTabNotifications(TabContents * contents)195 void ExtensionBrowserEventRouter::UnregisterForTabNotifications(
196     TabContents* contents) {
197   registrar_.Remove(this, NotificationType::NAV_ENTRY_COMMITTED,
198       Source<NavigationController>(&contents->controller()));
199   registrar_.Remove(this, NotificationType::TAB_CONTENTS_DESTROYED,
200       Source<TabContents>(contents));
201 }
202 
OnBrowserWindowReady(const Browser * browser)203 void ExtensionBrowserEventRouter::OnBrowserWindowReady(const Browser* browser) {
204   ListValue args;
205 
206   DictionaryValue* window_dictionary = ExtensionTabUtil::CreateWindowValue(
207       browser, false);
208   args.Append(window_dictionary);
209 
210   std::string json_args;
211   base::JSONWriter::Write(&args, false, &json_args);
212 
213   DispatchEvent(browser->profile(), events::kOnWindowCreated, json_args);
214 }
215 
OnBrowserRemoved(const Browser * browser)216 void ExtensionBrowserEventRouter::OnBrowserRemoved(const Browser* browser) {
217   // Stop listening to TabStripModel events for this browser.
218   browser->tabstrip_model()->RemoveObserver(this);
219 
220   registrar_.Remove(this, NotificationType::BROWSER_WINDOW_READY,
221       Source<const Browser>(browser));
222 
223   DispatchSimpleBrowserEvent(browser->profile(),
224                              ExtensionTabUtil::GetWindowId(browser),
225                              events::kOnWindowRemoved);
226 }
227 
228 #if defined(TOOLKIT_VIEWS)
NativeFocusWillChange(gfx::NativeView focused_before,gfx::NativeView focused_now)229 void ExtensionBrowserEventRouter::NativeFocusWillChange(
230     gfx::NativeView focused_before,
231     gfx::NativeView focused_now) {
232   if (!focused_now)
233     OnBrowserSetLastActive(NULL);
234 }
235 #elif defined(TOOLKIT_GTK)
ActiveWindowChanged(GdkWindow * active_window)236 void ExtensionBrowserEventRouter::ActiveWindowChanged(
237     GdkWindow* active_window) {
238   if (!active_window)
239     OnBrowserSetLastActive(NULL);
240 }
241 #endif
242 
OnBrowserSetLastActive(const Browser * browser)243 void ExtensionBrowserEventRouter::OnBrowserSetLastActive(
244     const Browser* browser) {
245   int window_id = extension_misc::kUnknownWindowId;
246   if (browser)
247     window_id = ExtensionTabUtil::GetWindowId(browser);
248 
249   if (focused_window_id_ == window_id)
250     return;
251 
252   focused_window_id_ = window_id;
253   // Note: because we use the default profile when |browser| is NULL, it means
254   // that all extensions hear about the event regardless of whether the browser
255   // that lost focus was OTR or if the extension is OTR-enabled.
256   // See crbug.com/46610.
257   DispatchSimpleBrowserEvent(browser ? browser->profile() : profile_,
258                              focused_window_id_,
259                              events::kOnWindowFocusedChanged);
260 }
261 
TabCreatedAt(TabContents * contents,int index,bool foreground)262 void ExtensionBrowserEventRouter::TabCreatedAt(TabContents* contents,
263                                                int index,
264                                                bool foreground) {
265   DispatchEventWithTab(contents->profile(), "", events::kOnTabCreated,
266                        contents);
267 
268   RegisterForTabNotifications(contents);
269 }
270 
TabInsertedAt(TabContentsWrapper * contents,int index,bool foreground)271 void ExtensionBrowserEventRouter::TabInsertedAt(TabContentsWrapper* contents,
272                                                 int index,
273                                                 bool foreground) {
274   // If tab is new, send created event.
275   int tab_id = ExtensionTabUtil::GetTabId(contents->tab_contents());
276   if (!GetTabEntry(contents->tab_contents())) {
277     tab_entries_[tab_id] = TabEntry();
278 
279     TabCreatedAt(contents->tab_contents(), index, foreground);
280     return;
281   }
282 
283   ListValue args;
284   args.Append(Value::CreateIntegerValue(tab_id));
285 
286   DictionaryValue* object_args = new DictionaryValue();
287   object_args->Set(tab_keys::kNewWindowIdKey, Value::CreateIntegerValue(
288       ExtensionTabUtil::GetWindowIdOfTab(contents->tab_contents())));
289   object_args->Set(tab_keys::kNewPositionKey, Value::CreateIntegerValue(
290       index));
291   args.Append(object_args);
292 
293   std::string json_args;
294   base::JSONWriter::Write(&args, false, &json_args);
295 
296   DispatchEvent(contents->profile(), events::kOnTabAttached, json_args);
297 }
298 
TabDetachedAt(TabContentsWrapper * contents,int index)299 void ExtensionBrowserEventRouter::TabDetachedAt(TabContentsWrapper* contents,
300                                                 int index) {
301   if (!GetTabEntry(contents->tab_contents())) {
302     // The tab was removed. Don't send detach event.
303     return;
304   }
305 
306   ListValue args;
307   args.Append(Value::CreateIntegerValue(
308       ExtensionTabUtil::GetTabId(contents->tab_contents())));
309 
310   DictionaryValue* object_args = new DictionaryValue();
311   object_args->Set(tab_keys::kOldWindowIdKey, Value::CreateIntegerValue(
312       ExtensionTabUtil::GetWindowIdOfTab(contents->tab_contents())));
313   object_args->Set(tab_keys::kOldPositionKey, Value::CreateIntegerValue(
314       index));
315   args.Append(object_args);
316 
317   std::string json_args;
318   base::JSONWriter::Write(&args, false, &json_args);
319 
320   DispatchEvent(contents->profile(), events::kOnTabDetached, json_args);
321 }
322 
TabClosingAt(TabStripModel * tab_strip_model,TabContentsWrapper * contents,int index)323 void ExtensionBrowserEventRouter::TabClosingAt(TabStripModel* tab_strip_model,
324                                                TabContentsWrapper* contents,
325                                                int index) {
326   int tab_id = ExtensionTabUtil::GetTabId(contents->tab_contents());
327 
328   ListValue args;
329   args.Append(Value::CreateIntegerValue(tab_id));
330 
331   DictionaryValue* object_args = new DictionaryValue();
332   object_args->SetBoolean(tab_keys::kWindowClosing,
333                           tab_strip_model->closing_all());
334   args.Append(object_args);
335 
336   std::string json_args;
337   base::JSONWriter::Write(&args, false, &json_args);
338 
339   DispatchEvent(contents->profile(), events::kOnTabRemoved, json_args);
340 
341   int removed_count = tab_entries_.erase(tab_id);
342   DCHECK_GT(removed_count, 0);
343 
344   UnregisterForTabNotifications(contents->tab_contents());
345 }
346 
TabSelectedAt(TabContentsWrapper * old_contents,TabContentsWrapper * new_contents,int index,bool user_gesture)347 void ExtensionBrowserEventRouter::TabSelectedAt(
348     TabContentsWrapper* old_contents,
349     TabContentsWrapper* new_contents,
350     int index,
351     bool user_gesture) {
352   if (old_contents == new_contents)
353     return;
354 
355   ListValue args;
356   args.Append(Value::CreateIntegerValue(
357       ExtensionTabUtil::GetTabId(new_contents->tab_contents())));
358 
359   DictionaryValue* object_args = new DictionaryValue();
360   object_args->Set(tab_keys::kWindowIdKey, Value::CreateIntegerValue(
361       ExtensionTabUtil::GetWindowIdOfTab(new_contents->tab_contents())));
362   args.Append(object_args);
363 
364   std::string json_args;
365   base::JSONWriter::Write(&args, false, &json_args);
366 
367   DispatchEvent(new_contents->profile(), events::kOnTabSelectionChanged,
368                 json_args);
369 }
370 
TabMoved(TabContentsWrapper * contents,int from_index,int to_index)371 void ExtensionBrowserEventRouter::TabMoved(TabContentsWrapper* contents,
372                                            int from_index,
373                                            int to_index) {
374   ListValue args;
375   args.Append(Value::CreateIntegerValue(
376       ExtensionTabUtil::GetTabId(contents->tab_contents())));
377 
378   DictionaryValue* object_args = new DictionaryValue();
379   object_args->Set(tab_keys::kWindowIdKey, Value::CreateIntegerValue(
380       ExtensionTabUtil::GetWindowIdOfTab(contents->tab_contents())));
381   object_args->Set(tab_keys::kFromIndexKey, Value::CreateIntegerValue(
382       from_index));
383   object_args->Set(tab_keys::kToIndexKey, Value::CreateIntegerValue(
384       to_index));
385   args.Append(object_args);
386 
387   std::string json_args;
388   base::JSONWriter::Write(&args, false, &json_args);
389 
390   DispatchEvent(contents->profile(), events::kOnTabMoved, json_args);
391 }
392 
TabUpdated(TabContents * contents,bool did_navigate)393 void ExtensionBrowserEventRouter::TabUpdated(TabContents* contents,
394                                              bool did_navigate) {
395   TabEntry* entry = GetTabEntry(contents);
396   DictionaryValue* changed_properties = NULL;
397 
398   DCHECK(entry);
399 
400   if (did_navigate)
401     changed_properties = entry->DidNavigate(contents);
402   else
403     changed_properties = entry->UpdateLoadState(contents);
404 
405   if (changed_properties)
406     DispatchTabUpdatedEvent(contents, changed_properties);
407 }
408 
DispatchTabUpdatedEvent(TabContents * contents,DictionaryValue * changed_properties)409 void ExtensionBrowserEventRouter::DispatchTabUpdatedEvent(
410     TabContents* contents, DictionaryValue* changed_properties) {
411   DCHECK(changed_properties);
412   DCHECK(contents);
413 
414   // The state of the tab (as seen from the extension point of view) has
415   // changed.  Send a notification to the extension.
416   ListValue args;
417 
418   // First arg: The id of the tab that changed.
419   args.Append(Value::CreateIntegerValue(ExtensionTabUtil::GetTabId(contents)));
420 
421   // Second arg: An object containing the changes to the tab state.
422   args.Append(changed_properties);
423 
424   // Third arg: An object containing the state of the tab.
425   args.Append(ExtensionTabUtil::CreateTabValue(contents));
426 
427   std::string json_args;
428   base::JSONWriter::Write(&args, false, &json_args);
429 
430   DispatchEvent(contents->profile(), events::kOnTabUpdated, json_args);
431 }
432 
GetTabEntry(const TabContents * contents)433 ExtensionBrowserEventRouter::TabEntry* ExtensionBrowserEventRouter::GetTabEntry(
434     const TabContents* contents) {
435   int tab_id = ExtensionTabUtil::GetTabId(contents);
436   std::map<int, TabEntry>::iterator i = tab_entries_.find(tab_id);
437   if (tab_entries_.end() == i)
438     return NULL;
439   return &i->second;
440 }
441 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)442 void ExtensionBrowserEventRouter::Observe(NotificationType type,
443                                           const NotificationSource& source,
444                                           const NotificationDetails& details) {
445   if (type == NotificationType::NAV_ENTRY_COMMITTED) {
446     NavigationController* source_controller =
447         Source<NavigationController>(source).ptr();
448     TabUpdated(source_controller->tab_contents(), true);
449   } else if (type == NotificationType::TAB_CONTENTS_DESTROYED) {
450     // Tab was destroyed after being detached (without being re-attached).
451     TabContents* contents = Source<TabContents>(source).ptr();
452     registrar_.Remove(this, NotificationType::NAV_ENTRY_COMMITTED,
453         Source<NavigationController>(&contents->controller()));
454     registrar_.Remove(this, NotificationType::TAB_CONTENTS_DESTROYED,
455         Source<TabContents>(contents));
456   } else if (type == NotificationType::BROWSER_WINDOW_READY) {
457     const Browser* browser = Source<const Browser>(source).ptr();
458     OnBrowserWindowReady(browser);
459 #if defined(OS_MACOSX)
460   } else if (type == NotificationType::NO_KEY_WINDOW) {
461     OnBrowserSetLastActive(NULL);
462 #endif
463   } else {
464     NOTREACHED();
465   }
466 }
467 
TabChangedAt(TabContentsWrapper * contents,int index,TabChangeType change_type)468 void ExtensionBrowserEventRouter::TabChangedAt(TabContentsWrapper* contents,
469                                                int index,
470                                                TabChangeType change_type) {
471   TabUpdated(contents->tab_contents(), false);
472 }
473 
TabReplacedAt(TabStripModel * tab_strip_model,TabContentsWrapper * old_contents,TabContentsWrapper * new_contents,int index)474 void ExtensionBrowserEventRouter::TabReplacedAt(
475     TabStripModel* tab_strip_model,
476     TabContentsWrapper* old_contents,
477     TabContentsWrapper* new_contents,
478     int index) {
479   TabClosingAt(tab_strip_model, old_contents, index);
480   TabInsertedAt(new_contents, index, tab_strip_model->active_index() == index);
481 }
482 
TabPinnedStateChanged(TabContentsWrapper * contents,int index)483 void ExtensionBrowserEventRouter::TabPinnedStateChanged(
484     TabContentsWrapper* contents,
485     int index) {
486   TabStripModel* tab_strip = NULL;
487   int tab_index;
488 
489   if (ExtensionTabUtil::GetTabStripModel(
490         contents->tab_contents(), &tab_strip, &tab_index)) {
491     DictionaryValue* changed_properties = new DictionaryValue();
492     changed_properties->SetBoolean(tab_keys::kPinnedKey,
493                                    tab_strip->IsTabPinned(tab_index));
494     DispatchTabUpdatedEvent(contents->tab_contents(), changed_properties);
495   }
496 }
497 
TabStripEmpty()498 void ExtensionBrowserEventRouter::TabStripEmpty() {}
499 
DispatchOldPageActionEvent(Profile * profile,const std::string & extension_id,const std::string & page_action_id,int tab_id,const std::string & url,int button)500 void ExtensionBrowserEventRouter::DispatchOldPageActionEvent(
501     Profile* profile,
502     const std::string& extension_id,
503     const std::string& page_action_id,
504     int tab_id,
505     const std::string& url,
506     int button) {
507   ListValue args;
508   args.Append(Value::CreateStringValue(page_action_id));
509 
510   DictionaryValue* data = new DictionaryValue();
511   data->Set(tab_keys::kTabIdKey, Value::CreateIntegerValue(tab_id));
512   data->Set(tab_keys::kTabUrlKey, Value::CreateStringValue(url));
513   data->Set(page_action_keys::kButtonKey, Value::CreateIntegerValue(button));
514   args.Append(data);
515 
516   std::string json_args;
517   base::JSONWriter::Write(&args, false, &json_args);
518 
519   DispatchEventToExtension(profile, extension_id, "pageActions", json_args);
520 }
521 
PageActionExecuted(Profile * profile,const std::string & extension_id,const std::string & page_action_id,int tab_id,const std::string & url,int button)522 void ExtensionBrowserEventRouter::PageActionExecuted(
523     Profile* profile,
524     const std::string& extension_id,
525     const std::string& page_action_id,
526     int tab_id,
527     const std::string& url,
528     int button) {
529   DispatchOldPageActionEvent(profile, extension_id, page_action_id, tab_id, url,
530                              button);
531   TabContentsWrapper* tab_contents = NULL;
532   if (!ExtensionTabUtil::GetTabById(tab_id, profile, profile->IsOffTheRecord(),
533                                     NULL, NULL, &tab_contents, NULL)) {
534     return;
535   }
536   DispatchEventWithTab(profile, extension_id, "pageAction.onClicked",
537                        tab_contents->tab_contents());
538 }
539 
BrowserActionExecuted(Profile * profile,const std::string & extension_id,Browser * browser)540 void ExtensionBrowserEventRouter::BrowserActionExecuted(
541     Profile* profile, const std::string& extension_id, Browser* browser) {
542   TabContentsWrapper* tab_contents = NULL;
543   int tab_id = 0;
544   if (!ExtensionTabUtil::GetDefaultTab(browser, &tab_contents, &tab_id))
545     return;
546   DispatchEventWithTab(profile, extension_id, "browserAction.onClicked",
547                        tab_contents->tab_contents());
548 }
549