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 "extensions/shell/browser/shell_desktop_controller.h"
6
7#include <string>
8#include <vector>
9
10#include "base/command_line.h"
11#include "extensions/browser/app_window/app_window.h"
12#include "extensions/browser/app_window/native_app_window.h"
13#include "extensions/shell/browser/shell_app_delegate.h"
14#include "extensions/shell/browser/shell_app_window_client.h"
15#include "extensions/shell/common/switches.h"
16#include "ui/aura/client/cursor_client.h"
17#include "ui/aura/client/default_capture_client.h"
18#include "ui/aura/layout_manager.h"
19#include "ui/aura/test/test_screen.h"
20#include "ui/aura/window.h"
21#include "ui/aura/window_event_dispatcher.h"
22#include "ui/aura/window_tree_host.h"
23#include "ui/base/cursor/cursor.h"
24#include "ui/base/cursor/image_cursors.h"
25#include "ui/base/ime/input_method_initializer.h"
26#include "ui/gfx/native_widget_types.h"
27#include "ui/gfx/screen.h"
28#include "ui/wm/core/base_focus_rules.h"
29#include "ui/wm/core/compound_event_filter.h"
30#include "ui/wm/core/cursor_manager.h"
31#include "ui/wm/core/focus_controller.h"
32#include "ui/wm/core/input_method_event_filter.h"
33#include "ui/wm/core/native_cursor_manager.h"
34#include "ui/wm/core/native_cursor_manager_delegate.h"
35#include "ui/wm/core/user_activity_detector.h"
36
37#if defined(OS_CHROMEOS)
38#include "chromeos/dbus/dbus_thread_manager.h"
39#include "ui/chromeos/user_activity_power_manager_notifier.h"
40#include "ui/display/types/display_mode.h"
41#include "ui/display/types/display_snapshot.h"
42#endif
43
44namespace extensions {
45namespace {
46
47// A simple layout manager that makes each new window fill its parent.
48class FillLayout : public aura::LayoutManager {
49 public:
50  FillLayout() {}
51  virtual ~FillLayout() {}
52
53 private:
54  // aura::LayoutManager:
55  virtual void OnWindowResized() OVERRIDE {}
56
57  virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE {
58    if (!child->parent())
59      return;
60
61    // Create a rect at 0,0 with the size of the parent.
62    gfx::Size parent_size = child->parent()->bounds().size();
63    child->SetBounds(gfx::Rect(parent_size));
64  }
65
66  virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE {}
67
68  virtual void OnWindowRemovedFromLayout(aura::Window* child) OVERRIDE {}
69
70  virtual void OnChildWindowVisibilityChanged(aura::Window* child,
71                                              bool visible) OVERRIDE {}
72
73  virtual void SetChildBounds(aura::Window* child,
74                              const gfx::Rect& requested_bounds) OVERRIDE {
75    SetChildBoundsDirect(child, requested_bounds);
76  }
77
78  DISALLOW_COPY_AND_ASSIGN(FillLayout);
79};
80
81// A class that bridges the gap between CursorManager and Aura. It borrows
82// heavily from AshNativeCursorManager.
83class ShellNativeCursorManager : public wm::NativeCursorManager {
84 public:
85  explicit ShellNativeCursorManager(aura::WindowTreeHost* host)
86      : host_(host), image_cursors_(new ui::ImageCursors) {}
87  virtual ~ShellNativeCursorManager() {}
88
89  // wm::NativeCursorManager overrides.
90  virtual void SetDisplay(const gfx::Display& display,
91                          wm::NativeCursorManagerDelegate* delegate) OVERRIDE {
92    if (image_cursors_->SetDisplay(display, display.device_scale_factor()))
93      SetCursor(delegate->GetCursor(), delegate);
94  }
95
96  virtual void SetCursor(gfx::NativeCursor cursor,
97                         wm::NativeCursorManagerDelegate* delegate) OVERRIDE {
98    image_cursors_->SetPlatformCursor(&cursor);
99    cursor.set_device_scale_factor(image_cursors_->GetScale());
100    delegate->CommitCursor(cursor);
101
102    if (delegate->IsCursorVisible())
103      ApplyCursor(cursor);
104  }
105
106  virtual void SetVisibility(
107      bool visible,
108      wm::NativeCursorManagerDelegate* delegate) OVERRIDE {
109    delegate->CommitVisibility(visible);
110
111    if (visible) {
112      SetCursor(delegate->GetCursor(), delegate);
113    } else {
114      gfx::NativeCursor invisible_cursor(ui::kCursorNone);
115      image_cursors_->SetPlatformCursor(&invisible_cursor);
116      ApplyCursor(invisible_cursor);
117    }
118  }
119
120  virtual void SetCursorSet(
121      ui::CursorSetType cursor_set,
122      wm::NativeCursorManagerDelegate* delegate) OVERRIDE {
123    image_cursors_->SetCursorSet(cursor_set);
124    delegate->CommitCursorSet(cursor_set);
125    if (delegate->IsCursorVisible())
126      SetCursor(delegate->GetCursor(), delegate);
127  }
128
129  virtual void SetMouseEventsEnabled(
130      bool enabled,
131      wm::NativeCursorManagerDelegate* delegate) OVERRIDE {
132    delegate->CommitMouseEventsEnabled(enabled);
133    SetVisibility(delegate->IsCursorVisible(), delegate);
134  }
135
136 private:
137  // Sets |cursor| as the active cursor within Aura.
138  void ApplyCursor(gfx::NativeCursor cursor) { host_->SetCursor(cursor); }
139
140  aura::WindowTreeHost* host_;  // Not owned.
141
142  scoped_ptr<ui::ImageCursors> image_cursors_;
143
144  DISALLOW_COPY_AND_ASSIGN(ShellNativeCursorManager);
145};
146
147class AppsFocusRules : public wm::BaseFocusRules {
148 public:
149  AppsFocusRules() {}
150  virtual ~AppsFocusRules() {}
151
152  virtual bool SupportsChildActivation(aura::Window* window) const OVERRIDE {
153    return true;
154  }
155
156 private:
157  DISALLOW_COPY_AND_ASSIGN(AppsFocusRules);
158};
159
160}  // namespace
161
162ShellDesktopController::ShellDesktopController()
163    : app_window_client_(new ShellAppWindowClient), app_window_(NULL) {
164  extensions::AppWindowClient::Set(app_window_client_.get());
165
166#if defined(OS_CHROMEOS)
167  chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->
168      AddObserver(this);
169  display_configurator_.reset(new ui::DisplayConfigurator);
170  display_configurator_->Init(false);
171  display_configurator_->ForceInitialConfigure(0);
172  display_configurator_->AddObserver(this);
173#endif
174  CreateRootWindow();
175}
176
177ShellDesktopController::~ShellDesktopController() {
178  CloseAppWindows();
179  DestroyRootWindow();
180#if defined(OS_CHROMEOS)
181  chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->
182      RemoveObserver(this);
183#endif
184  extensions::AppWindowClient::Set(NULL);
185}
186
187aura::WindowTreeHost* ShellDesktopController::GetHost() {
188  return host_.get();
189}
190
191AppWindow* ShellDesktopController::CreateAppWindow(
192    content::BrowserContext* context,
193    const Extension* extension) {
194  app_window_ = new AppWindow(context, new ShellAppDelegate, extension);
195  return app_window_;
196}
197
198void ShellDesktopController::AddAppWindow(aura::Window* window) {
199  aura::Window* root_window = GetHost()->window();
200  root_window->AddChild(window);
201}
202
203void ShellDesktopController::CloseAppWindows() {
204  if (app_window_) {
205    app_window_->GetBaseWindow()->Close();  // Close() deletes |app_window_|.
206    app_window_ = NULL;
207  }
208}
209
210aura::Window* ShellDesktopController::GetDefaultParent(
211    aura::Window* context,
212    aura::Window* window,
213    const gfx::Rect& bounds) {
214  return host_->window();
215}
216
217#if defined(OS_CHROMEOS)
218void ShellDesktopController::PowerButtonEventReceived(
219    bool down,
220    const base::TimeTicks& timestamp) {
221  if (down) {
222    chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->
223        RequestShutdown();
224  }
225}
226
227void ShellDesktopController::OnDisplayModeChanged(
228    const std::vector<ui::DisplayConfigurator::DisplayState>& displays) {
229  gfx::Size size = GetPrimaryDisplaySize();
230  if (!size.IsEmpty())
231    host_->UpdateRootWindowSize(size);
232}
233#endif
234
235void ShellDesktopController::OnHostCloseRequested(
236    const aura::WindowTreeHost* host) {
237  DCHECK_EQ(host_.get(), host);
238  CloseAppWindows();
239  base::MessageLoop::current()->PostTask(FROM_HERE,
240                                         base::MessageLoop::QuitClosure());
241}
242
243void ShellDesktopController::InitWindowManager() {
244  wm::FocusController* focus_controller =
245      new wm::FocusController(new AppsFocusRules());
246  aura::client::SetFocusClient(host_->window(), focus_controller);
247  host_->window()->AddPreTargetHandler(focus_controller);
248  aura::client::SetActivationClient(host_->window(), focus_controller);
249  focus_client_.reset(focus_controller);
250
251  input_method_filter_.reset(
252      new wm::InputMethodEventFilter(host_->GetAcceleratedWidget()));
253  input_method_filter_->SetInputMethodPropertyInRootWindow(host_->window());
254  root_window_event_filter_->AddHandler(input_method_filter_.get());
255
256  capture_client_.reset(
257      new aura::client::DefaultCaptureClient(host_->window()));
258
259  // Ensure new windows fill the display.
260  host_->window()->SetLayoutManager(new FillLayout);
261
262  cursor_manager_.reset(
263      new wm::CursorManager(scoped_ptr<wm::NativeCursorManager>(
264          new ShellNativeCursorManager(host_.get()))));
265  cursor_manager_->SetDisplay(
266      gfx::Screen::GetNativeScreen()->GetPrimaryDisplay());
267  cursor_manager_->SetCursor(ui::kCursorPointer);
268  aura::client::SetCursorClient(host_->window(), cursor_manager_.get());
269
270  user_activity_detector_.reset(new wm::UserActivityDetector);
271  host_->event_processor()->GetRootTarget()->AddPreTargetHandler(
272      user_activity_detector_.get());
273#if defined(OS_CHROMEOS)
274  user_activity_notifier_.reset(
275      new ui::UserActivityPowerManagerNotifier(user_activity_detector_.get()));
276#endif
277}
278
279void ShellDesktopController::CreateRootWindow() {
280  // Set up basic pieces of ui::wm.
281  gfx::Size size;
282  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
283  if (command_line->HasSwitch(switches::kAppShellHostWindowBounds)) {
284    const std::string size_str =
285        command_line->GetSwitchValueASCII(switches::kAppShellHostWindowBounds);
286    int width, height;
287    CHECK_EQ(2, sscanf(size_str.c_str(), "%dx%d", &width, &height));
288    size = gfx::Size(width, height);
289  } else {
290    size = GetPrimaryDisplaySize();
291  }
292  if (size.IsEmpty())
293    size = gfx::Size(1280, 720);
294
295  test_screen_.reset(aura::TestScreen::Create(size));
296  // TODO(jamescook): Replace this with a real Screen implementation.
297  gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, test_screen_.get());
298  // TODO(mukai): Set up input method.
299
300  host_.reset(test_screen_->CreateHostForPrimaryDisplay());
301  host_->InitHost();
302  aura::client::SetWindowTreeClient(host_->window(), this);
303  root_window_event_filter_.reset(new wm::CompoundEventFilter);
304  host_->window()->AddPreTargetHandler(root_window_event_filter_.get());
305  InitWindowManager();
306
307  host_->AddObserver(this);
308
309  // Ensure the X window gets mapped.
310  host_->Show();
311}
312
313void ShellDesktopController::DestroyRootWindow() {
314  host_->RemoveObserver(this);
315  if (input_method_filter_)
316    root_window_event_filter_->RemoveHandler(input_method_filter_.get());
317  if (user_activity_detector_) {
318    host_->event_processor()->GetRootTarget()->RemovePreTargetHandler(
319        user_activity_detector_.get());
320  }
321  wm::FocusController* focus_controller =
322      static_cast<wm::FocusController*>(focus_client_.get());
323  if (focus_controller) {
324    host_->window()->RemovePreTargetHandler(focus_controller);
325    aura::client::SetActivationClient(host_->window(), NULL);
326  }
327  root_window_event_filter_.reset();
328  capture_client_.reset();
329  input_method_filter_.reset();
330  focus_client_.reset();
331  cursor_manager_.reset();
332#if defined(OS_CHROMEOS)
333  user_activity_notifier_.reset();
334#endif
335  user_activity_detector_.reset();
336  host_.reset();
337}
338
339gfx::Size ShellDesktopController::GetPrimaryDisplaySize() {
340#if defined(OS_CHROMEOS)
341  const std::vector<ui::DisplayConfigurator::DisplayState>& displays =
342      display_configurator_->cached_displays();
343  if (displays.empty())
344    return gfx::Size();
345  const ui::DisplayMode* mode = displays[0].display->current_mode();
346  return mode ? mode->size() : gfx::Size();
347#else
348  return gfx::Size();
349#endif
350}
351
352}  // namespace extensions
353