1// Copyright 2014 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 "apps/app_window.h"
6#include "apps/app_window_registry.h"
7#include "apps/apps_client.h"
8#include "apps/ui/native_app_window.h"
9#include "components/keyed_service/content/browser_context_dependency_manager.h"
10#include "content/public/browser/browser_context.h"
11#include "content/public/browser/devtools_agent_host.h"
12#include "content/public/browser/devtools_manager.h"
13#include "content/public/browser/render_process_host.h"
14#include "content/public/browser/render_view_host.h"
15#include "content/public/browser/site_instance.h"
16#include "content/public/browser/web_contents.h"
17#include "extensions/browser/extensions_browser_client.h"
18#include "extensions/common/extension.h"
19
20namespace {
21
22// Create a key that identifies a AppWindow in a RenderViewHost across App
23// reloads. If the window was given an id in CreateParams, the key is the
24// extension id, a colon separator, and the AppWindow's |id|. If there is no
25// |id|, the chrome-extension://extension-id/page.html URL will be used. If the
26// RenderViewHost is not for a AppWindow, return an empty string.
27std::string GetWindowKeyForRenderViewHost(
28    const apps::AppWindowRegistry* registry,
29    content::RenderViewHost* render_view_host) {
30  apps::AppWindow* app_window =
31      registry->GetAppWindowForRenderViewHost(render_view_host);
32  if (!app_window)
33    return std::string();  // Not a AppWindow.
34
35  if (app_window->window_key().empty())
36    return app_window->web_contents()->GetURL().possibly_invalid_spec();
37
38  std::string key = app_window->extension_id();
39  key += ':';
40  key += app_window->window_key();
41  return key;
42}
43
44}  // namespace
45
46namespace apps {
47
48void AppWindowRegistry::Observer::OnAppWindowAdded(AppWindow* app_window) {
49}
50
51void AppWindowRegistry::Observer::OnAppWindowIconChanged(
52    AppWindow* app_window) {
53}
54
55void AppWindowRegistry::Observer::OnAppWindowRemoved(AppWindow* app_window) {
56}
57
58void AppWindowRegistry::Observer::OnAppWindowHidden(AppWindow* app_window) {
59}
60
61void AppWindowRegistry::Observer::OnAppWindowShown(AppWindow* app_window) {
62}
63
64AppWindowRegistry::Observer::~Observer() {
65}
66
67AppWindowRegistry::AppWindowRegistry(content::BrowserContext* context)
68    : context_(context),
69      devtools_callback_(base::Bind(&AppWindowRegistry::OnDevToolsStateChanged,
70                                    base::Unretained(this))) {
71  content::DevToolsManager::GetInstance()->AddAgentStateCallback(
72      devtools_callback_);
73}
74
75AppWindowRegistry::~AppWindowRegistry() {
76  content::DevToolsManager::GetInstance()->RemoveAgentStateCallback(
77      devtools_callback_);
78}
79
80// static
81AppWindowRegistry* AppWindowRegistry::Get(content::BrowserContext* context) {
82  return Factory::GetForBrowserContext(context, true /* create */);
83}
84
85void AppWindowRegistry::AddAppWindow(AppWindow* app_window) {
86  BringToFront(app_window);
87  FOR_EACH_OBSERVER(Observer, observers_, OnAppWindowAdded(app_window));
88}
89
90void AppWindowRegistry::AppWindowIconChanged(AppWindow* app_window) {
91  AddAppWindowToList(app_window);
92  FOR_EACH_OBSERVER(Observer, observers_, OnAppWindowIconChanged(app_window));
93}
94
95void AppWindowRegistry::AppWindowActivated(AppWindow* app_window) {
96  BringToFront(app_window);
97}
98
99void AppWindowRegistry::AppWindowHidden(AppWindow* app_window) {
100  FOR_EACH_OBSERVER(Observer, observers_, OnAppWindowHidden(app_window));
101}
102
103void AppWindowRegistry::AppWindowShown(AppWindow* app_window) {
104  FOR_EACH_OBSERVER(Observer, observers_, OnAppWindowShown(app_window));
105}
106
107void AppWindowRegistry::RemoveAppWindow(AppWindow* app_window) {
108  const AppWindowList::iterator it =
109      std::find(app_windows_.begin(), app_windows_.end(), app_window);
110  if (it != app_windows_.end())
111    app_windows_.erase(it);
112  FOR_EACH_OBSERVER(Observer, observers_, OnAppWindowRemoved(app_window));
113}
114
115void AppWindowRegistry::AddObserver(Observer* observer) {
116  observers_.AddObserver(observer);
117}
118
119void AppWindowRegistry::RemoveObserver(Observer* observer) {
120  observers_.RemoveObserver(observer);
121}
122
123AppWindowRegistry::AppWindowList AppWindowRegistry::GetAppWindowsForApp(
124    const std::string& app_id) const {
125  AppWindowList app_windows;
126  for (AppWindowList::const_iterator i = app_windows_.begin();
127       i != app_windows_.end();
128       ++i) {
129    if ((*i)->extension_id() == app_id)
130      app_windows.push_back(*i);
131  }
132  return app_windows;
133}
134
135void AppWindowRegistry::CloseAllAppWindowsForApp(const std::string& app_id) {
136  const AppWindowList windows = GetAppWindowsForApp(app_id);
137  for (AppWindowRegistry::const_iterator it = windows.begin();
138       it != windows.end();
139       ++it) {
140    (*it)->GetBaseWindow()->Close();
141  }
142}
143
144AppWindow* AppWindowRegistry::GetAppWindowForRenderViewHost(
145    content::RenderViewHost* render_view_host) const {
146  for (AppWindowList::const_iterator i = app_windows_.begin();
147       i != app_windows_.end();
148       ++i) {
149    if ((*i)->web_contents()->GetRenderViewHost() == render_view_host)
150      return *i;
151  }
152
153  return NULL;
154}
155
156AppWindow* AppWindowRegistry::GetAppWindowForNativeWindow(
157    gfx::NativeWindow window) const {
158  for (AppWindowList::const_iterator i = app_windows_.begin();
159       i != app_windows_.end();
160       ++i) {
161    if ((*i)->GetNativeWindow() == window)
162      return *i;
163  }
164
165  return NULL;
166}
167
168AppWindow* AppWindowRegistry::GetCurrentAppWindowForApp(
169    const std::string& app_id) const {
170  AppWindow* result = NULL;
171  for (AppWindowList::const_iterator i = app_windows_.begin();
172       i != app_windows_.end();
173       ++i) {
174    if ((*i)->extension_id() == app_id) {
175      result = *i;
176      if (result->GetBaseWindow()->IsActive())
177        return result;
178    }
179  }
180
181  return result;
182}
183
184AppWindow* AppWindowRegistry::GetAppWindowForAppAndKey(
185    const std::string& app_id,
186    const std::string& window_key) const {
187  AppWindow* result = NULL;
188  for (AppWindowList::const_iterator i = app_windows_.begin();
189       i != app_windows_.end();
190       ++i) {
191    if ((*i)->extension_id() == app_id && (*i)->window_key() == window_key) {
192      result = *i;
193      if (result->GetBaseWindow()->IsActive())
194        return result;
195    }
196  }
197  return result;
198}
199
200bool AppWindowRegistry::HadDevToolsAttached(
201    content::RenderViewHost* render_view_host) const {
202  std::string key = GetWindowKeyForRenderViewHost(this, render_view_host);
203  return key.empty() ? false : inspected_windows_.count(key) != 0;
204}
205
206// static
207AppWindow* AppWindowRegistry::GetAppWindowForNativeWindowAnyProfile(
208    gfx::NativeWindow window) {
209  std::vector<content::BrowserContext*> contexts =
210      AppsClient::Get()->GetLoadedBrowserContexts();
211  for (std::vector<content::BrowserContext*>::const_iterator i =
212           contexts.begin();
213       i != contexts.end();
214       ++i) {
215    AppWindowRegistry* registry =
216        Factory::GetForBrowserContext(*i, false /* create */);
217    if (!registry)
218      continue;
219
220    AppWindow* app_window = registry->GetAppWindowForNativeWindow(window);
221    if (app_window)
222      return app_window;
223  }
224
225  return NULL;
226}
227
228// static
229bool AppWindowRegistry::IsAppWindowRegisteredInAnyProfile(
230    int window_type_mask) {
231  std::vector<content::BrowserContext*> contexts =
232      AppsClient::Get()->GetLoadedBrowserContexts();
233  for (std::vector<content::BrowserContext*>::const_iterator i =
234           contexts.begin();
235       i != contexts.end();
236       ++i) {
237    AppWindowRegistry* registry =
238        Factory::GetForBrowserContext(*i, false /* create */);
239    if (!registry)
240      continue;
241
242    const AppWindowList& app_windows = registry->app_windows();
243    if (app_windows.empty())
244      continue;
245
246    if (window_type_mask == 0)
247      return true;
248
249    for (const_iterator j = app_windows.begin(); j != app_windows.end(); ++j) {
250      if ((*j)->window_type() & window_type_mask)
251        return true;
252    }
253  }
254
255  return false;
256}
257
258// static
259void AppWindowRegistry::CloseAllAppWindows() {
260  std::vector<content::BrowserContext*> contexts =
261      AppsClient::Get()->GetLoadedBrowserContexts();
262  for (std::vector<content::BrowserContext*>::const_iterator i =
263           contexts.begin();
264       i != contexts.end();
265       ++i) {
266    AppWindowRegistry* registry =
267        Factory::GetForBrowserContext(*i, false /* create */);
268    if (!registry)
269      continue;
270
271    while (!registry->app_windows().empty())
272      registry->app_windows().front()->GetBaseWindow()->Close();
273  }
274}
275
276void AppWindowRegistry::OnDevToolsStateChanged(
277    content::DevToolsAgentHost* agent_host,
278    bool attached) {
279  content::RenderViewHost* rvh = agent_host->GetRenderViewHost();
280  // Ignore unrelated notifications.
281  if (!rvh ||
282      rvh->GetSiteInstance()->GetProcess()->GetBrowserContext() != context_)
283    return;
284
285  std::string key = GetWindowKeyForRenderViewHost(this, rvh);
286  if (key.empty())
287    return;
288
289  if (attached)
290    inspected_windows_.insert(key);
291  else
292    inspected_windows_.erase(key);
293}
294
295void AppWindowRegistry::AddAppWindowToList(AppWindow* app_window) {
296  const AppWindowList::iterator it =
297      std::find(app_windows_.begin(), app_windows_.end(), app_window);
298  if (it != app_windows_.end())
299    return;
300  app_windows_.push_back(app_window);
301}
302
303void AppWindowRegistry::BringToFront(AppWindow* app_window) {
304  const AppWindowList::iterator it =
305      std::find(app_windows_.begin(), app_windows_.end(), app_window);
306  if (it != app_windows_.end())
307    app_windows_.erase(it);
308  app_windows_.push_front(app_window);
309}
310
311///////////////////////////////////////////////////////////////////////////////
312// Factory boilerplate
313
314// static
315AppWindowRegistry* AppWindowRegistry::Factory::GetForBrowserContext(
316    content::BrowserContext* context,
317    bool create) {
318  return static_cast<AppWindowRegistry*>(
319      GetInstance()->GetServiceForBrowserContext(context, create));
320}
321
322AppWindowRegistry::Factory* AppWindowRegistry::Factory::GetInstance() {
323  return Singleton<AppWindowRegistry::Factory>::get();
324}
325
326AppWindowRegistry::Factory::Factory()
327    : BrowserContextKeyedServiceFactory(
328          "AppWindowRegistry",
329          BrowserContextDependencyManager::GetInstance()) {}
330
331AppWindowRegistry::Factory::~Factory() {}
332
333KeyedService* AppWindowRegistry::Factory::BuildServiceInstanceFor(
334    content::BrowserContext* context) const {
335  return new AppWindowRegistry(context);
336}
337
338bool AppWindowRegistry::Factory::ServiceIsCreatedWithBrowserContext() const {
339  return true;
340}
341
342bool AppWindowRegistry::Factory::ServiceIsNULLWhileTesting() const {
343  return false;
344}
345
346content::BrowserContext* AppWindowRegistry::Factory::GetBrowserContextToUse(
347    content::BrowserContext* context) const {
348  return extensions::ExtensionsBrowserClient::Get()->GetOriginalContext(
349      context);
350}
351
352}  // namespace apps
353