windows_event_router.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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/api/tabs/windows_event_router.h"
6
7#include "base/values.h"
8#include "chrome/browser/extensions/event_names.h"
9#include "chrome/browser/extensions/event_router.h"
10#include "chrome/browser/extensions/extension_service.h"
11#include "chrome/browser/extensions/extension_system.h"
12#include "chrome/browser/extensions/window_controller.h"
13#include "chrome/browser/extensions/window_controller_list.h"
14#include "chrome/browser/profiles/profile.h"
15#include "chrome/common/chrome_notification_types.h"
16#include "chrome/common/extensions/extension_constants.h"
17#include "content/public/browser/notification_service.h"
18
19#if defined(TOOLKIT_GTK)
20#include "ui/base/x/active_window_watcher_x.h"
21#endif
22
23namespace event_names = extensions::event_names;
24
25namespace extensions {
26
27WindowsEventRouter::WindowsEventRouter(Profile* profile)
28    : profile_(profile),
29      focused_profile_(NULL),
30      focused_window_id_(extension_misc::kUnknownWindowId) {
31  DCHECK(!profile->IsOffTheRecord());
32
33  WindowControllerList::GetInstance()->AddObserver(this);
34#if defined(TOOLKIT_VIEWS)
35  views::WidgetFocusManager::GetInstance()->AddFocusChangeListener(this);
36#elif defined(TOOLKIT_GTK)
37  ui::ActiveWindowWatcherX::AddObserver(this);
38#elif defined(OS_MACOSX)
39  // Needed for when no suitable window can be passed to an extension as the
40  // currently focused window.
41  registrar_.Add(this, chrome::NOTIFICATION_NO_KEY_WINDOW,
42                 content::NotificationService::AllSources());
43#endif
44}
45
46WindowsEventRouter::~WindowsEventRouter() {
47  WindowControllerList::GetInstance()->RemoveObserver(this);
48#if defined(TOOLKIT_VIEWS)
49  views::WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(this);
50#elif defined(TOOLKIT_GTK)
51  ui::ActiveWindowWatcherX::RemoveObserver(this);
52#endif
53}
54
55void WindowsEventRouter::OnWindowControllerAdded(
56    WindowController* window_controller) {
57  if (!profile_->IsSameProfile(window_controller->profile()))
58    return;
59
60  scoped_ptr<base::ListValue> args(new base::ListValue());
61  base::DictionaryValue* window_dictionary =
62      window_controller->CreateWindowValue();
63  args->Append(window_dictionary);
64  DispatchEvent(event_names::kOnWindowCreated, window_controller->profile(),
65                args.Pass());
66}
67
68void WindowsEventRouter::OnWindowControllerRemoved(
69    WindowController* window_controller) {
70  if (!profile_->IsSameProfile(window_controller->profile()))
71    return;
72
73  int window_id = window_controller->GetWindowId();
74  scoped_ptr<base::ListValue> args(new base::ListValue());
75  args->Append(Value::CreateIntegerValue(window_id));
76  DispatchEvent(event_names::kOnWindowRemoved, window_controller->profile(),
77                args.Pass());
78}
79
80#if defined(TOOLKIT_VIEWS)
81void WindowsEventRouter::OnNativeFocusChange(
82    gfx::NativeView focused_before,
83    gfx::NativeView focused_now) {
84  if (!focused_now)
85    OnActiveWindowChanged(NULL);
86}
87#elif defined(TOOLKIT_GTK)
88void WindowsEventRouter::ActiveWindowChanged(
89    GdkWindow* active_window) {
90  if (!active_window)
91    OnActiveWindowChanged(NULL);
92}
93#endif
94
95void WindowsEventRouter::Observe(
96    int type,
97    const content::NotificationSource& source,
98    const content::NotificationDetails& details) {
99#if defined(OS_MACOSX)
100  if (chrome::NOTIFICATION_NO_KEY_WINDOW == type) {
101      OnActiveWindowChanged(NULL);
102      return;
103  }
104#endif
105}
106
107static void WillDispatchWindowFocusedEvent(Profile* new_active_profile,
108                                           int window_id,
109                                           Profile* profile,
110                                           const Extension* extension,
111                                           base::ListValue* event_args) {
112  // When switching between windows in the default and incognito profiles,
113  // dispatch WINDOW_ID_NONE to extensions whose profile lost focus that
114  // can't see the new focused window across the incognito boundary.
115  // See crbug.com/46610.
116  if (new_active_profile && new_active_profile != profile &&
117      !extensions::ExtensionSystem::Get(profile)->extension_service()->
118          CanCrossIncognito(extension)) {
119    event_args->Clear();
120    event_args->Append(Value::CreateIntegerValue(
121        extension_misc::kUnknownWindowId));
122  } else {
123    event_args->Clear();
124    event_args->Append(Value::CreateIntegerValue(window_id));
125  }
126}
127
128void WindowsEventRouter::OnActiveWindowChanged(
129    WindowController* window_controller) {
130  Profile* window_profile = NULL;
131  int window_id = extension_misc::kUnknownWindowId;
132  if (window_controller &&
133      profile_->IsSameProfile(window_controller->profile())) {
134    window_profile = window_controller->profile();
135    window_id = window_controller->GetWindowId();
136  }
137
138  if (focused_window_id_ == window_id)
139    return;
140
141  // window_profile is either the default profile for the active window, its
142  // incognito profile, or NULL iff the previous profile is losing focus.
143  focused_profile_ = window_profile;
144  focused_window_id_ = window_id;
145
146  scoped_ptr<Event> event(new Event(event_names::kOnWindowFocusedChanged,
147                                    make_scoped_ptr(new base::ListValue())));
148  event->will_dispatch_callback =
149      base::Bind(&WillDispatchWindowFocusedEvent, window_profile, window_id);
150  ExtensionSystem::Get(profile_)->event_router()->BroadcastEvent(event.Pass());
151}
152
153void WindowsEventRouter::DispatchEvent(const char* event_name,
154                                      Profile* profile,
155                                      scoped_ptr<base::ListValue> args) {
156  scoped_ptr<Event> event(new Event(event_name, args.Pass()));
157  event->restrict_to_profile = profile;
158  ExtensionSystem::Get(profile)->event_router()->BroadcastEvent(event.Pass());
159}
160
161}  // namespace extensions
162