OLD | NEW |
| (Empty) |
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 "base/bind.h" | |
6 #include "base/macros.h" | |
7 #include "examples/window_manager/debug_panel_host.mojom.h" | |
8 #include "examples/window_manager/window_manager.mojom.h" | |
9 #include "mojo/application/application_runner_chromium.h" | |
10 #include "mojo/common/binding_set.h" | |
11 #include "mojo/converters/geometry/geometry_type_converters.h" | |
12 #include "mojo/converters/input_events/input_events_type_converters.h" | |
13 #include "mojo/public/c/system/main.h" | |
14 #include "mojo/public/cpp/application/application_connection.h" | |
15 #include "mojo/public/cpp/application/application_delegate.h" | |
16 #include "mojo/public/cpp/application/application_impl.h" | |
17 #include "mojo/public/cpp/application/service_provider_impl.h" | |
18 #include "mojo/public/cpp/bindings/strong_binding.h" | |
19 #include "mojo/services/input_events/interfaces/input_events.mojom.h" | |
20 #include "mojo/services/navigation/interfaces/navigation.mojom.h" | |
21 #include "mojo/services/view_manager/cpp/view.h" | |
22 #include "mojo/services/view_manager/cpp/view_manager.h" | |
23 #include "mojo/services/view_manager/cpp/view_manager_delegate.h" | |
24 #include "mojo/services/view_manager/cpp/view_observer.h" | |
25 #include "services/window_manager/basic_focus_rules.h" | |
26 #include "services/window_manager/view_target.h" | |
27 #include "services/window_manager/window_manager_app.h" | |
28 #include "services/window_manager/window_manager_delegate.h" | |
29 #include "services/window_manager/window_manager_root.h" | |
30 #include "ui/events/event.h" | |
31 #include "ui/events/event_constants.h" | |
32 #include "url/gurl.h" | |
33 | |
34 #if defined CreateWindow | |
35 #undef CreateWindow | |
36 #endif | |
37 | |
38 namespace mojo { | |
39 namespace examples { | |
40 | |
41 class WindowManagerController; | |
42 | |
43 namespace { | |
44 | |
45 const int kBorderInset = 25; | |
46 const int kControlPanelWidth = 200; | |
47 const int kTextfieldHeight = 39; | |
48 | |
49 } // namespace | |
50 | |
51 class WindowManagerConnection : public ::examples::IWindowManager { | |
52 public: | |
53 WindowManagerConnection(WindowManagerController* window_manager, | |
54 InterfaceRequest<::examples::IWindowManager> request) | |
55 : window_manager_(window_manager), binding_(this, request.Pass()) {} | |
56 ~WindowManagerConnection() override {} | |
57 | |
58 private: | |
59 // Overridden from ::examples::IWindowManager: | |
60 void CloseWindow(Id view_id) override; | |
61 | |
62 WindowManagerController* window_manager_; | |
63 StrongBinding<::examples::IWindowManager> binding_; | |
64 | |
65 DISALLOW_COPY_AND_ASSIGN(WindowManagerConnection); | |
66 }; | |
67 | |
68 class NavigatorHostImpl : public NavigatorHost { | |
69 public: | |
70 NavigatorHostImpl(WindowManagerController* window_manager, Id view_id) | |
71 : window_manager_(window_manager), | |
72 view_id_(view_id), | |
73 current_index_(-1) {} | |
74 ~NavigatorHostImpl() override {} | |
75 | |
76 void Bind(InterfaceRequest<NavigatorHost> request) { | |
77 bindings_.AddBinding(this, request.Pass()); | |
78 } | |
79 | |
80 void RecordNavigation(const std::string& url); | |
81 | |
82 private: | |
83 void DidNavigateLocally(const mojo::String& url) override; | |
84 void RequestNavigate(Target target, URLRequestPtr request) override; | |
85 void RequestNavigateHistory(int32_t delta) override; | |
86 | |
87 WindowManagerController* window_manager_; | |
88 Id view_id_; | |
89 std::vector<std::string> history_; | |
90 int32_t current_index_; | |
91 | |
92 BindingSet<NavigatorHost> bindings_; | |
93 | |
94 DISALLOW_COPY_AND_ASSIGN(NavigatorHostImpl); | |
95 }; | |
96 | |
97 class RootLayoutManager : public ViewObserver { | |
98 public: | |
99 RootLayoutManager(ViewManager* view_manager, | |
100 View* root, | |
101 Id content_view_id, | |
102 Id launcher_ui_view_id, | |
103 Id control_panel_view_id) | |
104 : root_(root), | |
105 view_manager_(view_manager), | |
106 content_view_id_(content_view_id), | |
107 launcher_ui_view_id_(launcher_ui_view_id), | |
108 control_panel_view_id_(control_panel_view_id) {} | |
109 ~RootLayoutManager() override { | |
110 if (root_) | |
111 root_->RemoveObserver(this); | |
112 } | |
113 | |
114 private: | |
115 // Overridden from ViewObserver: | |
116 void OnViewBoundsChanged(View* view, | |
117 const Rect& old_bounds, | |
118 const Rect& new_bounds) override { | |
119 DCHECK_EQ(view, root_); | |
120 | |
121 View* content_view = view_manager_->GetViewById(content_view_id_); | |
122 content_view->SetBounds(new_bounds); | |
123 | |
124 int delta_width = new_bounds.width - old_bounds.width; | |
125 int delta_height = new_bounds.height - old_bounds.height; | |
126 | |
127 View* launcher_ui_view = view_manager_->GetViewById(launcher_ui_view_id_); | |
128 Rect launcher_ui_bounds(launcher_ui_view->bounds()); | |
129 launcher_ui_bounds.width += delta_width; | |
130 launcher_ui_view->SetBounds(launcher_ui_bounds); | |
131 | |
132 View* control_panel_view = | |
133 view_manager_->GetViewById(control_panel_view_id_); | |
134 Rect control_panel_bounds(control_panel_view->bounds()); | |
135 control_panel_bounds.x += delta_width; | |
136 control_panel_view->SetBounds(control_panel_bounds); | |
137 | |
138 const View::Children& content_views = content_view->children(); | |
139 View::Children::const_iterator iter = content_views.begin(); | |
140 for (; iter != content_views.end(); ++iter) { | |
141 View* view = *iter; | |
142 if (view->id() == control_panel_view->id() || | |
143 view->id() == launcher_ui_view->id()) | |
144 continue; | |
145 Rect view_bounds(view->bounds()); | |
146 view_bounds.width += delta_width; | |
147 view_bounds.height += delta_height; | |
148 view->SetBounds(view_bounds); | |
149 } | |
150 } | |
151 void OnViewDestroyed(View* view) override { | |
152 DCHECK_EQ(view, root_); | |
153 root_->RemoveObserver(this); | |
154 root_ = NULL; | |
155 } | |
156 | |
157 View* root_; | |
158 ViewManager* view_manager_; | |
159 const Id content_view_id_; | |
160 const Id launcher_ui_view_id_; | |
161 const Id control_panel_view_id_; | |
162 | |
163 DISALLOW_COPY_AND_ASSIGN(RootLayoutManager); | |
164 }; | |
165 | |
166 class Window : public InterfaceFactory<NavigatorHost> { | |
167 public: | |
168 Window(WindowManagerController* window_manager, View* view) | |
169 : window_manager_(window_manager), | |
170 view_(view), | |
171 navigator_host_(window_manager_, view_->id()) { | |
172 exposed_services_impl_.AddService<NavigatorHost>(this); | |
173 } | |
174 | |
175 ~Window() override {} | |
176 | |
177 View* view() const { return view_; } | |
178 | |
179 NavigatorHost* navigator_host() { return &navigator_host_; } | |
180 | |
181 void Embed(const std::string& url) { | |
182 // TODO: Support embedding multiple times? | |
183 ServiceProviderPtr exposed_services; | |
184 exposed_services_impl_.Bind(GetProxy(&exposed_services)); | |
185 view_->Embed(url, nullptr, exposed_services.Pass()); | |
186 navigator_host_.RecordNavigation(url); | |
187 } | |
188 | |
189 private: | |
190 // InterfaceFactory<NavigatorHost> | |
191 void Create(ApplicationConnection* connection, | |
192 InterfaceRequest<NavigatorHost> request) override { | |
193 navigator_host_.Bind(request.Pass()); | |
194 } | |
195 | |
196 WindowManagerController* window_manager_; | |
197 View* view_; | |
198 ServiceProviderImpl exposed_services_impl_; | |
199 NavigatorHostImpl navigator_host_; | |
200 }; | |
201 | |
202 class WindowManagerController | |
203 : public examples::DebugPanelHost, | |
204 public window_manager::WindowManagerController, | |
205 public ui::EventHandler, | |
206 public ui::AcceleratorTarget, | |
207 public mojo::InterfaceFactory<examples::DebugPanelHost>, | |
208 public InterfaceFactory<::examples::IWindowManager> { | |
209 public: | |
210 WindowManagerController(Shell* shell, | |
211 ApplicationImpl* app, | |
212 ApplicationConnection* connection, | |
213 window_manager::WindowManagerRoot* wm_root) | |
214 : shell_(shell), | |
215 launcher_ui_(NULL), | |
216 view_manager_(NULL), | |
217 window_manager_root_(wm_root), | |
218 navigation_target_(Target::DEFAULT), | |
219 app_(app), | |
220 binding_(this) { | |
221 connection->AddService<::examples::IWindowManager>(this); | |
222 } | |
223 | |
224 ~WindowManagerController() override { | |
225 // host() may be destroyed by the time we get here. | |
226 // TODO: figure out a way to always cleanly remove handler. | |
227 | |
228 // TODO(erg): In the aura version, we removed ourselves from the | |
229 // PreTargetHandler list here. We may need to do something analogous when | |
230 // we get event handling without aura working. | |
231 } | |
232 | |
233 void CloseWindow(Id view_id) { | |
234 WindowVector::iterator iter = GetWindowByViewId(view_id); | |
235 DCHECK(iter != windows_.end()); | |
236 Window* window = *iter; | |
237 windows_.erase(iter); | |
238 window->view()->Destroy(); | |
239 } | |
240 | |
241 void DidNavigateLocally(uint32 source_view_id, const mojo::String& url) { | |
242 LOG(ERROR) << "DidNavigateLocally: source_view_id: " << source_view_id | |
243 << " url: " << url.To<std::string>(); | |
244 } | |
245 | |
246 void RequestNavigate(uint32 source_view_id, | |
247 Target target, | |
248 const mojo::String& url) { | |
249 OnLaunch(source_view_id, target, url); | |
250 } | |
251 | |
252 // Overridden from mojo::DebugPanelHost: | |
253 void CloseTopWindow() override { | |
254 if (!windows_.empty()) | |
255 CloseWindow(windows_.back()->view()->id()); | |
256 } | |
257 | |
258 void NavigateTo(const String& url) override { | |
259 OnLaunch(control_panel_id_, Target::NEW_NODE, url); | |
260 } | |
261 | |
262 void SetNavigationTarget(Target t) override { navigation_target_ = t; } | |
263 | |
264 // mojo::InterfaceFactory<examples::DebugPanelHost> implementation. | |
265 void Create( | |
266 mojo::ApplicationConnection* connection, | |
267 mojo::InterfaceRequest<examples::DebugPanelHost> request) override { | |
268 binding_.Bind(request.Pass()); | |
269 } | |
270 | |
271 // mojo::InterfaceFactory<::examples::IWindowManager> implementation. | |
272 void Create( | |
273 mojo::ApplicationConnection* connection, | |
274 mojo::InterfaceRequest<::examples::IWindowManager> request) override { | |
275 new WindowManagerConnection(this, request.Pass()); | |
276 } | |
277 | |
278 private: | |
279 typedef std::vector<Window*> WindowVector; | |
280 | |
281 // Overridden from ViewManagerDelegate: | |
282 void OnEmbed(View* root, | |
283 InterfaceRequest<ServiceProvider> services, | |
284 ServiceProviderPtr exposed_services) override { | |
285 DCHECK(!view_manager_); | |
286 view_manager_ = root->view_manager(); | |
287 | |
288 View* view = view_manager_->CreateView(); | |
289 root->AddChild(view); | |
290 Rect rect; | |
291 rect.width = root->bounds().width; | |
292 rect.height = root->bounds().height; | |
293 view->SetBounds(rect); | |
294 view->SetVisible(true); | |
295 content_view_id_ = view->id(); | |
296 | |
297 Id launcher_ui_id = CreateLauncherUI(); | |
298 control_panel_id_ = CreateControlPanel(view); | |
299 | |
300 root_layout_manager_.reset( | |
301 new RootLayoutManager(view_manager_, root, content_view_id_, | |
302 launcher_ui_id, control_panel_id_)); | |
303 root->AddObserver(root_layout_manager_.get()); | |
304 | |
305 // TODO(erg): In the aura version, we explicitly added ourselves as a | |
306 // PreTargetHandler to the window() here. We probably have to do something | |
307 // analogous here. | |
308 | |
309 window_manager_root_->InitFocus( | |
310 make_scoped_ptr(new window_manager::BasicFocusRules(root))); | |
311 window_manager_root_->accelerator_manager()->Register( | |
312 ui::Accelerator(ui::VKEY_BROWSER_BACK, 0), | |
313 ui::AcceleratorManager::kNormalPriority, this); | |
314 } | |
315 void OnViewManagerDisconnected(ViewManager* view_manager) override { | |
316 DCHECK_EQ(view_manager_, view_manager); | |
317 view_manager_ = NULL; | |
318 base::MessageLoop::current()->Quit(); | |
319 } | |
320 | |
321 // Overridden from WindowManagerDelegate: | |
322 void Embed(const String& url, | |
323 InterfaceRequest<ServiceProvider> services, | |
324 ServiceProviderPtr exposed_services) override { | |
325 const Id kInvalidSourceViewId = 0; | |
326 OnLaunch(kInvalidSourceViewId, Target::DEFAULT, url); | |
327 } | |
328 | |
329 // Overridden from ui::EventHandler: | |
330 void OnEvent(ui::Event* event) override { | |
331 View* view = | |
332 static_cast<window_manager::ViewTarget*>(event->target())->view(); | |
333 if (event->type() == ui::ET_MOUSE_PRESSED) | |
334 view->SetFocus(); | |
335 } | |
336 | |
337 // Overriden from ui::AcceleratorTarget: | |
338 bool AcceleratorPressed(const ui::Accelerator& accelerator, | |
339 mojo::View* view) override { | |
340 if (accelerator.key_code() != ui::VKEY_BROWSER_BACK) | |
341 return false; | |
342 | |
343 WindowVector::iterator iter = GetWindowByViewId(view->id()); | |
344 DCHECK(iter != windows_.end()); | |
345 Window* window = *iter; | |
346 window->navigator_host()->RequestNavigateHistory(-1); | |
347 return true; | |
348 } | |
349 | |
350 // Overriden from ui::AcceleratorTarget: | |
351 bool CanHandleAccelerators() const override { return true; } | |
352 | |
353 void OnLaunch(uint32 source_view_id, | |
354 Target requested_target, | |
355 const mojo::String& url) { | |
356 Target target = navigation_target_; | |
357 if (target == Target::DEFAULT) { | |
358 if (requested_target != Target::DEFAULT) { | |
359 target = requested_target; | |
360 } else { | |
361 // TODO(aa): Should be Target::NEW_NODE if source origin and dest origin | |
362 // are different? | |
363 target = Target::SOURCE_NODE; | |
364 } | |
365 } | |
366 | |
367 Window* dest_view = NULL; | |
368 if (target == Target::SOURCE_NODE) { | |
369 WindowVector::iterator source_view = GetWindowByViewId(source_view_id); | |
370 bool app_initiated = source_view != windows_.end(); | |
371 if (app_initiated) | |
372 dest_view = *source_view; | |
373 else if (!windows_.empty()) | |
374 dest_view = windows_.back(); | |
375 } | |
376 | |
377 if (!dest_view) { | |
378 dest_view = CreateWindow(); | |
379 windows_.push_back(dest_view); | |
380 } | |
381 | |
382 dest_view->Embed(url); | |
383 } | |
384 | |
385 // TODO(beng): proper layout manager!! | |
386 Id CreateLauncherUI() { | |
387 View* view = view_manager_->GetViewById(content_view_id_); | |
388 Rect bounds = view->bounds(); | |
389 bounds.x += kBorderInset; | |
390 bounds.y += kBorderInset; | |
391 bounds.width -= 2 * kBorderInset; | |
392 bounds.height = kTextfieldHeight; | |
393 launcher_ui_ = CreateWindow(bounds); | |
394 launcher_ui_->Embed("mojo:browser"); | |
395 return launcher_ui_->view()->id(); | |
396 } | |
397 | |
398 Window* CreateWindow() { | |
399 View* view = view_manager_->GetViewById(content_view_id_); | |
400 Rect bounds; | |
401 bounds.x = kBorderInset; | |
402 bounds.y = 2 * kBorderInset + kTextfieldHeight; | |
403 bounds.width = view->bounds().width - 3 * kBorderInset - kControlPanelWidth; | |
404 bounds.height = | |
405 view->bounds().height - (3 * kBorderInset + kTextfieldHeight); | |
406 if (!windows_.empty()) { | |
407 bounds.x = windows_.back()->view()->bounds().x + 35; | |
408 bounds.y = windows_.back()->view()->bounds().y + 35; | |
409 } | |
410 return CreateWindow(bounds); | |
411 } | |
412 | |
413 Window* CreateWindow(const Rect& bounds) { | |
414 View* content = view_manager_->GetViewById(content_view_id_); | |
415 View* view = view_manager_->CreateView(); | |
416 content->AddChild(view); | |
417 view->SetBounds(bounds); | |
418 view->SetVisible(true); | |
419 view->SetFocus(); | |
420 return new Window(this, view); | |
421 } | |
422 | |
423 Id CreateControlPanel(View* root) { | |
424 View* view = view_manager_->CreateView(); | |
425 root->AddChild(view); | |
426 | |
427 Rect bounds; | |
428 bounds.x = root->bounds().width - kControlPanelWidth - kBorderInset; | |
429 bounds.y = kBorderInset * 2 + kTextfieldHeight; | |
430 bounds.width = kControlPanelWidth; | |
431 bounds.height = root->bounds().height - kBorderInset * 3 - kTextfieldHeight; | |
432 view->SetBounds(bounds); | |
433 view->SetVisible(true); | |
434 | |
435 ServiceProviderPtr exposed_services; | |
436 control_panel_exposed_services_impl_.Bind(GetProxy(&exposed_services)); | |
437 control_panel_exposed_services_impl_.AddService<::examples::IWindowManager>( | |
438 this); | |
439 | |
440 GURL frame_url = url_.Resolve("/examples/window_manager/debug_panel.sky"); | |
441 view->Embed(frame_url.spec(), nullptr, exposed_services.Pass()); | |
442 | |
443 return view->id(); | |
444 } | |
445 | |
446 WindowVector::iterator GetWindowByViewId(Id view_id) { | |
447 for (std::vector<Window*>::iterator iter = windows_.begin(); | |
448 iter != windows_.end(); ++iter) { | |
449 if ((*iter)->view()->id() == view_id) { | |
450 return iter; | |
451 } | |
452 } | |
453 return windows_.end(); | |
454 } | |
455 | |
456 Shell* shell_; | |
457 | |
458 Window* launcher_ui_; | |
459 WindowVector windows_; | |
460 ViewManager* view_manager_; | |
461 scoped_ptr<RootLayoutManager> root_layout_manager_; | |
462 ServiceProviderImpl control_panel_exposed_services_impl_; | |
463 | |
464 window_manager::WindowManagerRoot* window_manager_root_; | |
465 | |
466 // Id of the view most content is added to. | |
467 Id content_view_id_; | |
468 | |
469 // Id of the debug panel. | |
470 Id control_panel_id_; | |
471 | |
472 GURL url_; | |
473 Target navigation_target_; | |
474 | |
475 ApplicationImpl* app_; | |
476 | |
477 mojo::Binding<examples::DebugPanelHost> binding_; | |
478 | |
479 DISALLOW_COPY_AND_ASSIGN(WindowManagerController); | |
480 }; | |
481 | |
482 class WindowManager : public ApplicationDelegate, | |
483 public window_manager::WindowManagerControllerFactory { | |
484 public: | |
485 WindowManager() | |
486 : window_manager_app_(new window_manager::WindowManagerApp(this)) {} | |
487 | |
488 scoped_ptr<window_manager::WindowManagerController> | |
489 CreateWindowManagerController( | |
490 ApplicationConnection* connection, | |
491 window_manager::WindowManagerRoot* wm_root) override { | |
492 return scoped_ptr<WindowManagerController>( | |
493 new WindowManagerController(shell_, app_, connection, wm_root)); | |
494 } | |
495 | |
496 private: | |
497 // Overridden from ApplicationDelegate: | |
498 void Initialize(ApplicationImpl* app) override { | |
499 window_manager_app_.reset(new window_manager::WindowManagerApp(this)); | |
500 shell_ = app->shell(); | |
501 app_ = app; | |
502 // FIXME: Mojo applications don't know their URLs yet: | |
503 // https://docs.google.com/a/chromium.org/document/d/1AQ2y6ekzvbdaMF5WrUQmne
yXJnke-MnYYL4Gz1AKDos | |
504 url_ = GURL(app->args()[1]); | |
505 window_manager_app_->Initialize(app); | |
506 } | |
507 | |
508 bool ConfigureIncomingConnection(ApplicationConnection* connection) override { | |
509 window_manager_app_->ConfigureIncomingConnection(connection); | |
510 return true; | |
511 } | |
512 | |
513 ApplicationImpl* app_; | |
514 Shell* shell_; | |
515 GURL url_; | |
516 | |
517 scoped_ptr<window_manager::WindowManagerApp> window_manager_app_; | |
518 DISALLOW_COPY_AND_ASSIGN(WindowManager); | |
519 }; | |
520 | |
521 void WindowManagerConnection::CloseWindow(Id view_id) { | |
522 window_manager_->CloseWindow(view_id); | |
523 } | |
524 | |
525 void NavigatorHostImpl::DidNavigateLocally(const mojo::String& url) { | |
526 window_manager_->DidNavigateLocally(view_id_, url); | |
527 RecordNavigation(url); | |
528 } | |
529 | |
530 void NavigatorHostImpl::RequestNavigate(Target target, URLRequestPtr request) { | |
531 window_manager_->RequestNavigate(view_id_, target, request->url); | |
532 } | |
533 | |
534 void NavigatorHostImpl::RequestNavigateHistory(int32_t delta) { | |
535 if (history_.empty()) | |
536 return; | |
537 current_index_ = | |
538 std::max(0, std::min(current_index_ + delta, | |
539 static_cast<int32_t>(history_.size()) - 1)); | |
540 window_manager_->RequestNavigate(view_id_, Target::SOURCE_NODE, | |
541 history_[current_index_]); | |
542 } | |
543 | |
544 void NavigatorHostImpl::RecordNavigation(const std::string& url) { | |
545 if (current_index_ >= 0 && history_[current_index_] == url) { | |
546 // This is a navigation to the current entry, ignore. | |
547 return; | |
548 } | |
549 history_.erase(history_.begin() + (current_index_ + 1), history_.end()); | |
550 history_.push_back(url); | |
551 ++current_index_; | |
552 } | |
553 | |
554 } // namespace examples | |
555 } // namespace mojo | |
556 | |
557 MojoResult MojoMain(MojoHandle application_request) { | |
558 mojo::ApplicationRunnerChromium runner(new mojo::examples::WindowManager); | |
559 return runner.Run(application_request); | |
560 } | |
OLD | NEW |