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.
5 #include "chrome/browser/extensions/api/tabs/windows_event_router.h"
7 #include "base/values.h"
8 #include "chrome/browser/chrome_notification_types.h"
9 #include "chrome/browser/extensions/api/tabs/app_base_window.h"
10 #include "chrome/browser/extensions/api/tabs/app_window_controller.h"
11 #include "chrome/browser/extensions/api/tabs/tabs_constants.h"
12 #include "chrome/browser/extensions/api/tabs/windows_util.h"
13 #include "chrome/browser/extensions/extension_service.h"
14 #include "chrome/browser/extensions/extension_util.h"
15 #include "chrome/browser/extensions/window_controller.h"
16 #include "chrome/browser/extensions/window_controller_list.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/common/extensions/api/windows.h"
19 #include "chrome/common/extensions/extension_constants.h"
20 #include "content/public/browser/notification_service.h"
21 #include "extensions/browser/app_window/app_window.h"
22 #include "extensions/browser/event_router.h"
23 #include "extensions/common/constants.h"
25 using content::BrowserContext
;
27 namespace extensions
{
29 namespace keys
= extensions::tabs_constants
;
30 namespace windows
= extensions::api::windows
;
34 bool ControllerVisibleToListener(WindowController
* window_controller
,
35 const Extension
* extension
,
36 const base::DictionaryValue
* listener_filter
) {
37 if (!window_controller
)
40 // If there is no filter the visibility is based on the extension.
41 const base::ListValue
* filter_value
= nullptr;
42 if (!listener_filter
||
43 !listener_filter
->GetList(keys::kWindowTypesKey
, &filter_value
))
44 return window_controller
->IsVisibleToExtension(extension
);
46 // Otherwise it's based on the type filter.
47 WindowController::TypeFilter filter
=
48 WindowController::GetFilterFromWindowTypesValues(filter_value
);
49 return window_controller
->MatchesFilter(filter
);
52 bool WillDispatchWindowEvent(WindowController
* window_controller
,
53 BrowserContext
* context
,
54 const Extension
* extension
,
56 const base::DictionaryValue
* listener_filter
) {
58 listener_filter
&& listener_filter
->HasKey(keys::kWindowTypesKey
);
59 // Cleanup previous values.
60 event
->filter_info
= EventFilteringInfo();
61 // Only set the window type if the listener has set a filter.
62 // Otherwise we set the window visibility relative to the extension.
64 event
->filter_info
.SetWindowType(window_controller
->GetWindowTypeText());
66 event
->filter_info
.SetWindowExposedByDefault(
67 window_controller
->IsVisibleToExtension(extension
));
71 bool WillDispatchWindowFocusedEvent(
72 WindowController
* window_controller
,
73 BrowserContext
* context
,
74 const Extension
* extension
,
76 const base::DictionaryValue
* listener_filter
) {
77 int window_id
= extension_misc::kUnknownWindowId
;
78 Profile
* new_active_context
= nullptr;
80 listener_filter
&& listener_filter
->HasKey(keys::kWindowTypesKey
);
82 // We might not have a window controller if the focus moves away
83 // from chromium's windows.
84 if (window_controller
) {
85 window_id
= window_controller
->GetWindowId();
86 new_active_context
= window_controller
->profile();
89 // Cleanup previous values.
90 event
->filter_info
= EventFilteringInfo();
91 // Only set the window type if the listener has set a filter,
92 // otherwise set the visibility to true (if the window is not
93 // supposed to be visible by the extension, we will clear out the
96 event
->filter_info
.SetWindowType(
97 window_controller
? window_controller
->GetWindowTypeText()
98 : keys::kWindowTypeValueNormal
);
100 event
->filter_info
.SetWindowExposedByDefault(true);
102 // When switching between windows in the default and incognito profiles,
103 // dispatch WINDOW_ID_NONE to extensions whose profile lost focus that
104 // can't see the new focused window across the incognito boundary.
105 // See crbug.com/46610.
106 bool cant_cross_incognito
= new_active_context
&&
107 new_active_context
!= context
&&
108 !util::CanCrossIncognito(extension
, context
);
109 // If the window is not visible by the listener, we also need to
110 // clear out the window id from the event.
111 bool visible_to_listener
= ControllerVisibleToListener(
112 window_controller
, extension
, listener_filter
);
114 if (cant_cross_incognito
|| !visible_to_listener
) {
115 event
->event_args
->Clear();
116 event
->event_args
->Append(
117 new base::FundamentalValue(extension_misc::kUnknownWindowId
));
119 event
->event_args
->Clear();
120 event
->event_args
->Append(new base::FundamentalValue(window_id
));
127 WindowsEventRouter::WindowsEventRouter(Profile
* profile
)
129 focused_profile_(nullptr),
130 focused_window_id_(extension_misc::kUnknownWindowId
),
131 observed_app_registry_(this),
132 observed_controller_list_(this) {
133 DCHECK(!profile
->IsOffTheRecord());
135 observed_app_registry_
.Add(AppWindowRegistry::Get(profile_
));
136 observed_controller_list_
.Add(WindowControllerList::GetInstance());
137 // Needed for when no suitable window can be passed to an extension as the
138 // currently focused window. On Mac (even in a toolkit-views build) always
139 // rely on the notification sent by AppControllerMac after AppKit sends
140 // NSWindowDidBecomeKeyNotification and there is no [NSApp keyWindo7w]. This
141 // allows windows not created by toolkit-views to be tracked.
142 // TODO(tapted): Remove the ifdefs (and NOTIFICATION_NO_KEY_WINDOW) when
143 // Chrome on Mac only makes windows with toolkit-views.
144 #if defined(OS_MACOSX)
145 registrar_
.Add(this, chrome::NOTIFICATION_NO_KEY_WINDOW
,
146 content::NotificationService::AllSources());
147 #elif defined(TOOLKIT_VIEWS)
148 views::WidgetFocusManager::GetInstance()->AddFocusChangeListener(this);
153 AppWindowRegistry
* registry
= AppWindowRegistry::Get(profile_
);
154 for (AppWindow
* app_window
: registry
->app_windows())
155 AddAppWindow(app_window
);
158 WindowsEventRouter::~WindowsEventRouter() {
159 #if !defined(OS_MACOSX)
160 views::WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(this);
164 void WindowsEventRouter::OnAppWindowAdded(extensions::AppWindow
* app_window
) {
165 if (!profile_
->IsSameProfile(
166 Profile::FromBrowserContext(app_window
->browser_context())))
168 AddAppWindow(app_window
);
171 void WindowsEventRouter::OnAppWindowRemoved(extensions::AppWindow
* app_window
) {
172 if (!profile_
->IsSameProfile(
173 Profile::FromBrowserContext(app_window
->browser_context())))
176 scoped_ptr
<WindowController
> controller
=
177 app_windows_
.take_and_erase(app_window
->session_id().id());
180 void WindowsEventRouter::OnAppWindowActivated(
181 extensions::AppWindow
* app_window
) {
182 AppWindowMap::const_iterator iter
=
183 app_windows_
.find(app_window
->session_id().id());
184 OnActiveWindowChanged(iter
!= app_windows_
.end() ? iter
->second
: nullptr);
187 void WindowsEventRouter::OnWindowControllerAdded(
188 WindowController
* window_controller
) {
189 if (!HasEventListener(windows::OnCreated::kEventName
))
191 if (!profile_
->IsSameProfile(window_controller
->profile()))
194 scoped_ptr
<base::ListValue
> args(new base::ListValue());
195 base::DictionaryValue
* window_dictionary
=
196 window_controller
->CreateWindowValue();
197 args
->Append(window_dictionary
);
198 DispatchEvent(events::WINDOWS_ON_CREATED
, windows::OnCreated::kEventName
,
199 window_controller
, args
.Pass());
202 void WindowsEventRouter::OnWindowControllerRemoved(
203 WindowController
* window_controller
) {
204 if (!HasEventListener(windows::OnRemoved::kEventName
))
206 if (!profile_
->IsSameProfile(window_controller
->profile()))
209 int window_id
= window_controller
->GetWindowId();
210 scoped_ptr
<base::ListValue
> args(new base::ListValue());
211 args
->Append(new base::FundamentalValue(window_id
));
212 DispatchEvent(events::WINDOWS_ON_REMOVED
, windows::OnRemoved::kEventName
,
213 window_controller
, args
.Pass());
216 #if !defined(OS_MACOSX)
217 void WindowsEventRouter::OnNativeFocusChanged(gfx::NativeView focused_now
) {
219 OnActiveWindowChanged(nullptr);
223 void WindowsEventRouter::Observe(
225 const content::NotificationSource
& source
,
226 const content::NotificationDetails
& details
) {
227 #if defined(OS_MACOSX)
228 if (chrome::NOTIFICATION_NO_KEY_WINDOW
== type
) {
229 OnActiveWindowChanged(nullptr);
235 void WindowsEventRouter::OnActiveWindowChanged(
236 WindowController
* window_controller
) {
237 Profile
* window_profile
= nullptr;
238 int window_id
= extension_misc::kUnknownWindowId
;
239 if (window_controller
&&
240 profile_
->IsSameProfile(window_controller
->profile())) {
241 window_profile
= window_controller
->profile();
242 window_id
= window_controller
->GetWindowId();
245 if (focused_window_id_
== window_id
)
248 // window_profile is either the default profile for the active window, its
249 // incognito profile, or nullptr if the previous profile is losing focus.
250 focused_profile_
= window_profile
;
251 focused_window_id_
= window_id
;
253 if (!HasEventListener(windows::OnFocusChanged::kEventName
))
256 scoped_ptr
<Event
> event(new Event(events::WINDOWS_ON_FOCUS_CHANGED
,
257 windows::OnFocusChanged::kEventName
,
258 make_scoped_ptr(new base::ListValue())));
259 event
->will_dispatch_callback
=
260 base::Bind(&WillDispatchWindowFocusedEvent
, window_controller
);
261 EventRouter::Get(profile_
)->BroadcastEvent(event
.Pass());
264 void WindowsEventRouter::DispatchEvent(events::HistogramValue histogram_value
,
265 const std::string
& event_name
,
266 WindowController
* window_controller
,
267 scoped_ptr
<base::ListValue
> args
) {
268 scoped_ptr
<Event
> event(new Event(histogram_value
, event_name
, args
.Pass()));
269 event
->restrict_to_browser_context
= window_controller
->profile();
270 event
->will_dispatch_callback
=
271 base::Bind(&WillDispatchWindowEvent
, window_controller
);
272 EventRouter::Get(profile_
)->BroadcastEvent(event
.Pass());
275 bool WindowsEventRouter::HasEventListener(const std::string
& event_name
) {
276 return EventRouter::Get(profile_
)->HasEventListener(event_name
);
279 void WindowsEventRouter::AddAppWindow(extensions::AppWindow
* app_window
) {
280 scoped_ptr
<AppWindowController
> controller(new AppWindowController(
281 app_window
, make_scoped_ptr(new AppBaseWindow(app_window
)), profile_
));
282 app_windows_
.set(app_window
->session_id().id(), controller
.Pass());
285 } // namespace extensions