Remove the RenderProcessHost observer and attach the WebContentsObserver earlier...
[chromium-blink-merge.git] / mojo / examples / window_manager / window_manager.cc
blobfbbf0744d8543335f529e6492fbb24ab8e7016b9
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.
5 #include "base/bind.h"
6 #include "base/macros.h"
7 #include "mojo/application/application_runner_chromium.h"
8 #include "mojo/examples/keyboard/keyboard.mojom.h"
9 #include "mojo/examples/window_manager/debug_panel.h"
10 #include "mojo/examples/window_manager/window_manager.mojom.h"
11 #include "mojo/public/c/system/main.h"
12 #include "mojo/public/cpp/application/application_connection.h"
13 #include "mojo/public/cpp/application/application_delegate.h"
14 #include "mojo/public/cpp/application/application_impl.h"
15 #include "mojo/public/cpp/application/interface_factory_impl.h"
16 #include "mojo/public/cpp/application/service_provider_impl.h"
17 #include "mojo/services/public/cpp/geometry/geometry_type_converters.h"
18 #include "mojo/services/public/cpp/input_events/input_events_type_converters.h"
19 #include "mojo/services/public/cpp/view_manager/view.h"
20 #include "mojo/services/public/cpp/view_manager/view_manager.h"
21 #include "mojo/services/public/cpp/view_manager/view_manager_delegate.h"
22 #include "mojo/services/public/cpp/view_manager/view_observer.h"
23 #include "mojo/services/public/cpp/view_manager/window_manager_delegate.h"
24 #include "mojo/services/public/interfaces/input_events/input_events.mojom.h"
25 #include "mojo/services/public/interfaces/navigation/navigation.mojom.h"
26 #include "mojo/services/window_manager/window_manager_app.h"
27 #include "mojo/views/views_init.h"
28 #include "ui/aura/window.h"
29 #include "ui/events/event.h"
30 #include "ui/events/event_constants.h"
31 #include "ui/gfx/geometry/size_conversions.h"
32 #include "ui/wm/core/focus_rules.h"
34 #if defined CreateWindow
35 #undef CreateWindow
36 #endif
38 namespace mojo {
39 namespace examples {
41 class WindowManager;
43 namespace {
45 const int kBorderInset = 25;
46 const int kControlPanelWidth = 200;
47 const int kTextfieldHeight = 25;
49 class WMFocusRules : public wm::FocusRules {
50 public:
51 WMFocusRules(mojo::WindowManagerApp* window_manager_app,
52 mojo::View* window_container)
53 : window_container_(window_container),
54 window_manager_app_(window_manager_app) {}
55 virtual ~WMFocusRules() {}
57 private:
58 // Overridden from wm::FocusRules:
59 virtual bool IsToplevelWindow(aura::Window* window) const override {
60 return mojo::WindowManagerApp::GetViewForWindow(window)->parent() ==
61 window_container_;
63 virtual bool CanActivateWindow(aura::Window* window) const override {
64 return mojo::WindowManagerApp::GetViewForWindow(window)->parent() ==
65 window_container_;
67 virtual bool CanFocusWindow(aura::Window* window) const override {
68 return true;
70 virtual aura::Window* GetToplevelWindow(aura::Window* window) const override {
71 mojo::View* view = mojo::WindowManagerApp::GetViewForWindow(window);
72 while (view->parent() != window_container_) {
73 view = view->parent();
74 // Unparented hierarchy, there is no "top level" window.
75 if (!view)
76 return NULL;
79 return window_manager_app_->GetWindowForViewId(view->id());
81 virtual aura::Window* GetActivatableWindow(
82 aura::Window* window) const override {
83 return GetToplevelWindow(window);
85 virtual aura::Window* GetFocusableWindow(
86 aura::Window* window) const override {
87 return window;
89 virtual aura::Window* GetNextActivatableWindow(
90 aura::Window* ignore) const override {
91 aura::Window* activatable = GetActivatableWindow(ignore);
92 const aura::Window::Windows& children = activatable->parent()->children();
93 for (aura::Window::Windows::const_reverse_iterator it = children.rbegin();
94 it != children.rend(); ++it) {
95 if (*it != ignore)
96 return *it;
98 return NULL;
101 mojo::View* window_container_;
102 mojo::WindowManagerApp* window_manager_app_;
104 DISALLOW_COPY_AND_ASSIGN(WMFocusRules);
107 } // namespace
109 class WindowManagerConnection : public InterfaceImpl<IWindowManager> {
110 public:
111 explicit WindowManagerConnection(WindowManager* window_manager)
112 : window_manager_(window_manager) {}
113 virtual ~WindowManagerConnection() {}
115 private:
116 // Overridden from IWindowManager:
117 virtual void CloseWindow(Id view_id) override;
118 virtual void ShowKeyboard(Id view_id, RectPtr bounds) override;
119 virtual void HideKeyboard(Id view_id) override;
121 WindowManager* window_manager_;
123 DISALLOW_COPY_AND_ASSIGN(WindowManagerConnection);
126 class NavigatorHostImpl : public InterfaceImpl<NavigatorHost> {
127 public:
128 explicit NavigatorHostImpl(WindowManager* window_manager, Id view_id)
129 : window_manager_(window_manager), view_id_(view_id) {}
130 virtual ~NavigatorHostImpl() {
133 private:
134 virtual void DidNavigateLocally(const mojo::String& url) override;
135 virtual void RequestNavigate(Target target, URLRequestPtr request) override;
137 WindowManager* window_manager_;
138 Id view_id_;
140 DISALLOW_COPY_AND_ASSIGN(NavigatorHostImpl);
143 class KeyboardManager : public KeyboardClient,
144 public ViewObserver {
145 public:
146 KeyboardManager() : view_manager_(NULL), view_(NULL) {
148 virtual ~KeyboardManager() {
149 if (view_)
150 view_->parent()->RemoveObserver(this);
153 View* view() { return view_; }
155 void Init(ApplicationImpl* application,
156 ViewManager* view_manager,
157 View* parent,
158 const gfx::Rect& bounds) {
159 view_manager_ = view_manager;
160 view_ = View::Create(view_manager);
161 view_->SetBounds(bounds);
162 parent->AddChild(view_);
163 view_->Embed("mojo:mojo_keyboard");
164 application->ConnectToService("mojo:mojo_keyboard", &keyboard_service_);
165 keyboard_service_.set_client(this);
166 parent->AddObserver(this);
169 void Show(Id view_id, const gfx::Rect& bounds) {
170 keyboard_service_->SetTarget(view_id);
171 view_->SetVisible(true);
174 void Hide(Id view_id) {
175 keyboard_service_->SetTarget(0);
176 view_->SetVisible(false);
179 private:
180 // KeyboardClient:
181 virtual void OnKeyboardEvent(Id view_id,
182 int32_t code,
183 int32_t flags) override {
184 View* view = view_manager_->GetViewById(view_id);
185 if (!view)
186 return;
187 #if defined(OS_WIN)
188 const bool is_char = code != ui::VKEY_BACK && code != ui::VKEY_RETURN;
189 #else
190 const bool is_char = false;
191 #endif
192 if (is_char) {
193 view_manager_->DispatchEvent(
194 view,
195 Event::From(ui::KeyEvent(ui::ET_KEY_PRESSED,
196 static_cast<ui::KeyboardCode>(code),
197 flags)));
198 } else {
199 view_manager_->DispatchEvent(
200 view,
201 Event::From(ui::KeyEvent(static_cast<base::char16>(code),
202 static_cast<ui::KeyboardCode>(code),
203 flags)));
205 view_manager_->DispatchEvent(
206 view,
207 Event::From(ui::KeyEvent(ui::ET_KEY_RELEASED,
208 static_cast<ui::KeyboardCode>(code),
209 flags)));
212 // Overridden from ViewObserver:
213 virtual void OnViewBoundsChanged(View* parent,
214 const gfx::Rect& old_bounds,
215 const gfx::Rect& new_bounds) override {
216 gfx::Rect keyboard_bounds(view_->bounds());
217 keyboard_bounds.set_y(new_bounds.bottom() - keyboard_bounds.height());
218 keyboard_bounds.set_width(keyboard_bounds.width() +
219 new_bounds.width() - old_bounds.width());
220 view_->SetBounds(keyboard_bounds);
222 virtual void OnViewDestroyed(View* parent) override {
223 DCHECK_EQ(parent, view_->parent());
224 parent->RemoveObserver(this);
225 view_ = NULL;
228 KeyboardServicePtr keyboard_service_;
229 ViewManager* view_manager_;
231 // View the keyboard is attached to.
232 View* view_;
234 DISALLOW_COPY_AND_ASSIGN(KeyboardManager);
237 class RootLayoutManager : public ViewObserver {
238 public:
239 RootLayoutManager(ViewManager* view_manager,
240 View* root,
241 Id content_view_id,
242 Id launcher_ui_view_id,
243 Id control_panel_view_id)
244 : root_(root),
245 view_manager_(view_manager),
246 content_view_id_(content_view_id),
247 launcher_ui_view_id_(launcher_ui_view_id),
248 control_panel_view_id_(control_panel_view_id) {}
249 virtual ~RootLayoutManager() {
250 if (root_)
251 root_->RemoveObserver(this);
254 private:
255 // Overridden from ViewObserver:
256 virtual void OnViewBoundsChanged(View* view,
257 const gfx::Rect& old_bounds,
258 const gfx::Rect& new_bounds) override {
259 DCHECK_EQ(view, root_);
261 View* content_view = view_manager_->GetViewById(content_view_id_);
262 content_view->SetBounds(new_bounds);
264 int delta_width = new_bounds.width() - old_bounds.width();
265 int delta_height = new_bounds.height() - old_bounds.height();
267 View* launcher_ui_view =
268 view_manager_->GetViewById(launcher_ui_view_id_);
269 gfx::Rect launcher_ui_bounds(launcher_ui_view->bounds());
270 launcher_ui_bounds.set_width(launcher_ui_bounds.width() + delta_width);
271 launcher_ui_view->SetBounds(launcher_ui_bounds);
273 View* control_panel_view =
274 view_manager_->GetViewById(control_panel_view_id_);
275 gfx::Rect control_panel_bounds(control_panel_view->bounds());
276 control_panel_bounds.set_x(control_panel_bounds.x() + delta_width);
277 control_panel_view->SetBounds(control_panel_bounds);
279 const View::Children& content_views = content_view->children();
280 View::Children::const_iterator iter = content_views.begin();
281 for(; iter != content_views.end(); ++iter) {
282 View* view = *iter;
283 if (view->id() == control_panel_view->id() ||
284 view->id() == launcher_ui_view->id())
285 continue;
286 gfx::Rect view_bounds(view->bounds());
287 view_bounds.set_width(view_bounds.width() + delta_width);
288 view_bounds.set_height(view_bounds.height() + delta_height);
289 view->SetBounds(view_bounds);
292 virtual void OnViewDestroyed(View* view) override {
293 DCHECK_EQ(view, root_);
294 root_->RemoveObserver(this);
295 root_ = NULL;
298 View* root_;
299 ViewManager* view_manager_;
300 const Id content_view_id_;
301 const Id launcher_ui_view_id_;
302 const Id control_panel_view_id_;
304 DISALLOW_COPY_AND_ASSIGN(RootLayoutManager);
307 class Window : public InterfaceFactory<NavigatorHost> {
308 public:
309 Window(WindowManager* window_manager, View* view)
310 : window_manager_(window_manager), view_(view) {}
312 virtual ~Window() {}
314 View* view() const { return view_; }
316 void Embed(const std::string& url) {
317 scoped_ptr<ServiceProviderImpl> service_provider_impl(
318 new ServiceProviderImpl());
319 service_provider_impl->AddService<NavigatorHost>(this);
320 view_->Embed(url, service_provider_impl.Pass());
323 private:
324 // InterfaceFactory<NavigatorHost>
325 virtual void Create(ApplicationConnection* connection,
326 InterfaceRequest<NavigatorHost> request) override {
327 BindToRequest(new NavigatorHostImpl(window_manager_, view_->id()),
328 &request);
331 WindowManager* window_manager_;
332 View* view_;
335 class WindowManager
336 : public ApplicationDelegate,
337 public DebugPanel::Delegate,
338 public ViewManagerDelegate,
339 public WindowManagerDelegate,
340 public ui::EventHandler {
341 public:
342 WindowManager()
343 : shell_(nullptr),
344 window_manager_factory_(this),
345 launcher_ui_(NULL),
346 view_manager_(NULL),
347 window_manager_app_(new WindowManagerApp(this, this)),
348 app_(NULL) {}
350 virtual ~WindowManager() {
351 // host() may be destroyed by the time we get here.
352 // TODO: figure out a way to always cleanly remove handler.
353 if (window_manager_app_->host())
354 window_manager_app_->host()->window()->RemovePreTargetHandler(this);
357 void CloseWindow(Id view_id) {
358 WindowVector::iterator iter = GetWindowByViewId(view_id);
359 DCHECK(iter != windows_.end());
360 Window* window = *iter;
361 windows_.erase(iter);
362 window->view()->Destroy();
365 void ShowKeyboard(Id view_id, const gfx::Rect& bounds) {
366 // TODO: this needs to validate |view_id|. That is, it shouldn't assume
367 // |view_id| is valid and it also needs to make sure the client that sent
368 // this really owns |view_id|.
369 // TODO: honor |bounds|.
370 if (!keyboard_manager_) {
371 keyboard_manager_.reset(new KeyboardManager);
372 View* parent = view_manager_->GetRoots().back();
373 int ideal_height = 200;
374 // TODO(sky): 10 is a bit of a hack here. There is a bug that causes
375 // white strips to appear when 0 is used. Figure this out!
376 const gfx::Rect keyboard_bounds(
377 10, parent->bounds().height() - ideal_height,
378 parent->bounds().width() - 20, ideal_height);
379 keyboard_manager_->Init(app_, view_manager_, parent, keyboard_bounds);
381 keyboard_manager_->Show(view_id, bounds);
384 void HideKeyboard(Id view_id) {
385 // See comment in ShowKeyboard() about validating args.
386 if (keyboard_manager_)
387 keyboard_manager_->Hide(view_id);
390 void DidNavigateLocally(uint32 source_view_id, const mojo::String& url) {
391 LOG(ERROR) << "DidNavigateLocally: source_view_id: " << source_view_id
392 << " url: " << url.To<std::string>();
395 // Overridden from DebugPanel::Delegate:
396 virtual void CloseTopWindow() override {
397 if (!windows_.empty())
398 CloseWindow(windows_.back()->view()->id());
401 virtual void RequestNavigate(uint32 source_view_id,
402 Target target,
403 URLRequestPtr request) override {
404 OnLaunch(source_view_id, target, request->url);
407 private:
408 typedef std::vector<Window*> WindowVector;
410 // Overridden from ApplicationDelegate:
411 virtual void Initialize(ApplicationImpl* app) override {
412 shell_ = app->shell();
413 app_ = app;
414 views_init_.reset(new ViewsInit);
415 window_manager_app_->Initialize(app);
418 virtual bool ConfigureIncomingConnection(
419 ApplicationConnection* connection) override {
420 connection->AddService(&window_manager_factory_);
421 window_manager_app_->ConfigureIncomingConnection(connection);
422 return true;
425 // Overridden from ViewManagerDelegate:
426 virtual void OnEmbed(ViewManager* view_manager,
427 View* root,
428 ServiceProviderImpl* exported_services,
429 scoped_ptr<ServiceProvider> imported_services) override {
430 DCHECK(!view_manager_);
431 view_manager_ = view_manager;
433 View* view = View::Create(view_manager_);
434 root->AddChild(view);
435 view->SetBounds(gfx::Rect(root->bounds().size()));
436 content_view_id_ = view->id();
438 Id launcher_ui_id = CreateLauncherUI();
439 Id control_panel_id = CreateControlPanel(view);
441 root_layout_manager_.reset(
442 new RootLayoutManager(view_manager, root,
443 content_view_id_,
444 launcher_ui_id,
445 control_panel_id));
446 root->AddObserver(root_layout_manager_.get());
448 window_manager_app_->host()->window()->AddPreTargetHandler(this);
450 window_manager_app_->InitFocus(new WMFocusRules(window_manager_app_.get(),
451 view));
453 virtual void OnViewManagerDisconnected(ViewManager* view_manager) override {
454 DCHECK_EQ(view_manager_, view_manager);
455 view_manager_ = NULL;
456 base::MessageLoop::current()->Quit();
459 // Overridden from WindowManagerDelegate:
460 virtual void Embed(
461 const String& url,
462 InterfaceRequest<ServiceProvider> service_provider) override {
463 const Id kInvalidSourceViewId = 0;
464 OnLaunch(kInvalidSourceViewId, TARGET_DEFAULT, url);
466 virtual void DispatchEvent(EventPtr event) override {}
468 // Overridden from ui::EventHandler:
469 virtual void OnEvent(ui::Event* event) override {
470 View* view = WindowManagerApp::GetViewForWindow(
471 static_cast<aura::Window*>(event->target()));
472 if (event->type() == ui::ET_MOUSE_PRESSED &&
473 !IsDescendantOfKeyboard(view)) {
474 view->SetFocus();
478 void OnLaunch(uint32 source_view_id,
479 Target requested_target,
480 const mojo::String& url) {
481 Target target = debug_panel_->navigation_target();
482 if (target == TARGET_DEFAULT) {
483 if (requested_target != TARGET_DEFAULT) {
484 target = requested_target;
485 } else {
486 // TODO(aa): Should be TARGET_NEW_NODE if source origin and dest origin
487 // are different?
488 target = TARGET_SOURCE_NODE;
492 Window* dest_view = NULL;
493 if (target == TARGET_SOURCE_NODE) {
494 WindowVector::iterator source_view = GetWindowByViewId(source_view_id);
495 bool app_initiated = source_view != windows_.end();
496 if (app_initiated)
497 dest_view = *source_view;
498 else if (!windows_.empty())
499 dest_view = windows_.back();
502 if (!dest_view) {
503 dest_view = CreateWindow();
504 windows_.push_back(dest_view);
507 dest_view->Embed(url);
510 // TODO(beng): proper layout manager!!
511 Id CreateLauncherUI() {
512 View* view = view_manager_->GetViewById(content_view_id_);
513 gfx::Rect bounds = view->bounds();
514 bounds.Inset(kBorderInset, kBorderInset);
515 bounds.set_height(kTextfieldHeight);
516 launcher_ui_ = CreateWindow(bounds);
517 launcher_ui_->Embed("mojo:mojo_browser");
518 return launcher_ui_->view()->id();
521 Window* CreateWindow() {
522 View* view = view_manager_->GetViewById(content_view_id_);
523 gfx::Rect bounds(kBorderInset,
524 2 * kBorderInset + kTextfieldHeight,
525 view->bounds().width() - 3 * kBorderInset -
526 kControlPanelWidth,
527 view->bounds().height() -
528 (3 * kBorderInset + kTextfieldHeight));
529 if (!windows_.empty()) {
530 gfx::Point position = windows_.back()->view()->bounds().origin();
531 position.Offset(35, 35);
532 bounds.set_origin(position);
534 return CreateWindow(bounds);
537 Window* CreateWindow(const gfx::Rect& bounds) {
538 View* content = view_manager_->GetViewById(content_view_id_);
539 View* view = View::Create(view_manager_);
540 content->AddChild(view);
541 view->SetBounds(bounds);
542 view->SetFocus();
543 return new Window(this, view);
546 bool IsDescendantOfKeyboard(View* target) {
547 return keyboard_manager_.get() &&
548 keyboard_manager_->view()->Contains(target);
551 Id CreateControlPanel(View* root) {
552 View* view = View::Create(view_manager_);
553 root->AddChild(view);
555 gfx::Rect bounds(root->bounds().width() - kControlPanelWidth -
556 kBorderInset,
557 kBorderInset * 2 + kTextfieldHeight,
558 kControlPanelWidth,
559 root->bounds().height() - kBorderInset * 3 -
560 kTextfieldHeight);
561 view->SetBounds(bounds);
563 debug_panel_ = new DebugPanel(this, shell_, view);
564 return view->id();
567 WindowVector::iterator GetWindowByViewId(Id view_id) {
568 for (std::vector<Window*>::iterator iter = windows_.begin();
569 iter != windows_.end();
570 ++iter) {
571 if ((*iter)->view()->id() == view_id) {
572 return iter;
575 return windows_.end();
578 Shell* shell_;
580 InterfaceFactoryImplWithContext<WindowManagerConnection, WindowManager>
581 window_manager_factory_;
583 scoped_ptr<ViewsInit> views_init_;
584 DebugPanel* debug_panel_;
585 Window* launcher_ui_;
586 WindowVector windows_;
587 ViewManager* view_manager_;
588 scoped_ptr<RootLayoutManager> root_layout_manager_;
590 scoped_ptr<WindowManagerApp> window_manager_app_;
592 // Id of the view most content is added to. The keyboard is NOT added here.
593 Id content_view_id_;
595 scoped_ptr<KeyboardManager> keyboard_manager_;
596 ApplicationImpl* app_;
598 DISALLOW_COPY_AND_ASSIGN(WindowManager);
601 void WindowManagerConnection::CloseWindow(Id view_id) {
602 window_manager_->CloseWindow(view_id);
605 void WindowManagerConnection::ShowKeyboard(Id view_id, RectPtr bounds) {
606 window_manager_->ShowKeyboard(view_id, bounds.To<gfx::Rect>());
609 void WindowManagerConnection::HideKeyboard(Id view_id) {
610 window_manager_->HideKeyboard(view_id);
613 void NavigatorHostImpl::DidNavigateLocally(const mojo::String& url) {
614 window_manager_->DidNavigateLocally(view_id_, url);
617 void NavigatorHostImpl::RequestNavigate(Target target, URLRequestPtr request) {
618 window_manager_->RequestNavigate(view_id_, target, request.Pass());
621 } // namespace examples
622 } // namespace mojo
624 MojoResult MojoMain(MojoHandle shell_handle) {
625 mojo::ApplicationRunnerChromium runner(new mojo::examples::WindowManager);
626 return runner.Run(shell_handle);