Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(538)

Side by Side Diff: chrome/browser/extensions/browser_event_router.cc

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

Powered by Google App Engine
This is Rietveld 408576698