OLD | NEW |
| (Empty) |
1 // Copyright 2015 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 "mandoline/ui/desktop_ui/browser_window.h" | |
6 | |
7 #include <stdint.h> | |
8 | |
9 #include <utility> | |
10 | |
11 #include "base/command_line.h" | |
12 #include "base/macros.h" | |
13 #include "base/strings/string16.h" | |
14 #include "base/strings/utf_string_conversions.h" | |
15 #include "base/time/time.h" | |
16 #include "base/trace_event/trace_event.h" | |
17 #include "components/mus/public/cpp/event_matcher.h" | |
18 #include "components/mus/public/cpp/scoped_window_ptr.h" | |
19 #include "components/mus/public/cpp/window_tree_host_factory.h" | |
20 #include "mandoline/ui/desktop_ui/browser_commands.h" | |
21 #include "mandoline/ui/desktop_ui/browser_manager.h" | |
22 #include "mandoline/ui/desktop_ui/find_bar_view.h" | |
23 #include "mandoline/ui/desktop_ui/public/interfaces/omnibox.mojom.h" | |
24 #include "mandoline/ui/desktop_ui/toolbar_view.h" | |
25 #include "mojo/common/common_type_converters.h" | |
26 #include "mojo/converters/geometry/geometry_type_converters.h" | |
27 #include "mojo/services/tracing/public/cpp/switches.h" | |
28 #include "mojo/services/tracing/public/interfaces/tracing.mojom.h" | |
29 #include "mojo/shell/public/cpp/shell.h" | |
30 #include "ui/gfx/canvas.h" | |
31 #include "ui/mojo/init/ui_init.h" | |
32 #include "ui/views/background.h" | |
33 #include "ui/views/controls/button/label_button.h" | |
34 #include "ui/views/layout/layout_manager.h" | |
35 #include "ui/views/mus/aura_init.h" | |
36 #include "ui/views/mus/display_converter.h" | |
37 #include "ui/views/mus/native_widget_mus.h" | |
38 #include "ui/views/widget/widget_delegate.h" | |
39 | |
40 namespace mandoline { | |
41 namespace { | |
42 | |
43 void OnAcceleratorAdded(bool success) { | |
44 DCHECK(success); | |
45 } | |
46 | |
47 } // namespace | |
48 | |
49 class ProgressView : public views::View { | |
50 public: | |
51 ProgressView() : progress_(0.f), loading_(false) {} | |
52 ~ProgressView() override {} | |
53 | |
54 void SetProgress(double progress) { | |
55 progress_ = progress; | |
56 SchedulePaint(); | |
57 } | |
58 | |
59 void SetIsLoading(bool loading) { | |
60 if (loading == loading_) | |
61 return; | |
62 | |
63 loading_ = loading; | |
64 progress_ = 0.f; | |
65 SchedulePaint(); | |
66 } | |
67 | |
68 private: | |
69 void OnPaint(gfx::Canvas* canvas) override { | |
70 if (loading_) { | |
71 canvas->FillRect(GetLocalBounds(), SK_ColorGREEN); | |
72 gfx::Rect progress_rect = GetLocalBounds(); | |
73 progress_rect.set_width(progress_rect.width() * progress_); | |
74 canvas->FillRect(progress_rect, SK_ColorRED); | |
75 } else { | |
76 canvas->FillRect(GetLocalBounds(), SK_ColorGRAY); | |
77 } | |
78 } | |
79 | |
80 double progress_; | |
81 bool loading_; | |
82 | |
83 DISALLOW_COPY_AND_ASSIGN(ProgressView); | |
84 }; | |
85 | |
86 class BrowserWindow::LayoutManagerImpl : public views::LayoutManager { | |
87 public: | |
88 explicit LayoutManagerImpl(BrowserWindow* window) : window_(window) {} | |
89 ~LayoutManagerImpl() override {} | |
90 | |
91 private: | |
92 // views::LayoutManager: | |
93 gfx::Size GetPreferredSize(const views::View* view) const override { | |
94 return gfx::Size(); | |
95 } | |
96 void Layout(views::View* host) override { | |
97 window_->Layout(host); | |
98 } | |
99 | |
100 BrowserWindow* window_; | |
101 | |
102 DISALLOW_COPY_AND_ASSIGN(LayoutManagerImpl); | |
103 }; | |
104 | |
105 //////////////////////////////////////////////////////////////////////////////// | |
106 // BrowserWindow, public: | |
107 | |
108 BrowserWindow::BrowserWindow(mojo::Shell* shell, | |
109 mus::mojom::WindowTreeHostFactory* host_factory, | |
110 BrowserManager* manager) | |
111 : shell_(shell), | |
112 window_manager_client_(nullptr), | |
113 manager_(manager), | |
114 toolbar_view_(nullptr), | |
115 progress_bar_(nullptr), | |
116 find_bar_view_(nullptr), | |
117 root_(nullptr), | |
118 content_(nullptr), | |
119 omnibox_view_(nullptr), | |
120 find_active_(0), | |
121 find_count_(0), | |
122 web_view_(this) { | |
123 mus::CreateWindowTreeHost(host_factory, this, &host_, this); | |
124 } | |
125 | |
126 void BrowserWindow::LoadURL(const GURL& url) { | |
127 // Haven't been embedded yet, can't embed. | |
128 // TODO(beng): remove this. | |
129 if (!root_) { | |
130 default_url_ = url; | |
131 return; | |
132 } | |
133 | |
134 if (!url.is_valid()) { | |
135 ShowOmnibox(); | |
136 return; | |
137 } | |
138 | |
139 mojo::URLRequestPtr request(mojo::URLRequest::New()); | |
140 request->url = url.spec(); | |
141 Embed(std::move(request)); | |
142 } | |
143 | |
144 void BrowserWindow::Close() { | |
145 if (root_) | |
146 mus::ScopedWindowPtr::DeleteWindowOrWindowManager(root_); | |
147 else | |
148 delete this; | |
149 } | |
150 | |
151 void BrowserWindow::ShowOmnibox() { | |
152 TRACE_EVENT0("desktop_ui", "BrowserWindow::ShowOmnibox"); | |
153 if (!omnibox_.get()) { | |
154 omnibox_connection_ = shell_->Connect("mojo:omnibox"); | |
155 omnibox_connection_->AddService<ViewEmbedder>(this); | |
156 omnibox_connection_->ConnectToService(&omnibox_); | |
157 omnibox_connection_->SetRemoteServiceProviderConnectionErrorHandler( | |
158 [this]() { | |
159 // This will cause the connection to be re-established the next time | |
160 // we come through this codepath. | |
161 omnibox_.reset(); | |
162 }); | |
163 } | |
164 omnibox_->ShowForURL(mojo::String::From(current_url_.spec())); | |
165 } | |
166 | |
167 void BrowserWindow::ShowFind() { | |
168 TRACE_EVENT0("desktop_ui", "BrowserWindow::ShowFind"); | |
169 toolbar_view_->SetVisible(false); | |
170 find_bar_view_->Show(); | |
171 } | |
172 | |
173 void BrowserWindow::GoBack() { | |
174 TRACE_EVENT0("desktop_ui", "BrowserWindow::GoBack"); | |
175 web_view_.web_view()->GoBack(); | |
176 } | |
177 | |
178 void BrowserWindow::GoForward() { | |
179 TRACE_EVENT0("desktop_ui", "BrowserWindow::GoForward"); | |
180 web_view_.web_view()->GoForward(); | |
181 } | |
182 | |
183 BrowserWindow::~BrowserWindow() { | |
184 DCHECK(!root_); | |
185 manager_->BrowserWindowClosed(this); | |
186 } | |
187 | |
188 float BrowserWindow::DIPSToPixels(float value) const { | |
189 return value * root_->viewport_metrics().device_pixel_ratio; | |
190 } | |
191 | |
192 //////////////////////////////////////////////////////////////////////////////// | |
193 // BrowserWindow, mus::ViewTreeDelegate implementation: | |
194 | |
195 void BrowserWindow::OnEmbed(mus::Window* root) { | |
196 TRACE_EVENT0("desktop_ui", "BrowserWindow::OnEmbed"); | |
197 // BrowserWindow does not support being embedded more than once. | |
198 CHECK(!root_); | |
199 | |
200 // Record when the browser window was displayed, used for performance testing. | |
201 const base::TimeTicks display_ticks = base::TimeTicks::Now(); | |
202 | |
203 root_ = root; | |
204 | |
205 host_->SetTitle("Mandoline"); | |
206 | |
207 content_ = root_->connection()->NewWindow(); | |
208 Init(root_); | |
209 | |
210 host_->SetSize(mojo::Size::From(gfx::Size(1280, 800))); | |
211 | |
212 root_->AddChild(content_); | |
213 window_manager_client_->AddActivationParent(root_); | |
214 content_->SetVisible(true); | |
215 | |
216 web_view_.Init(shell_, content_); | |
217 | |
218 const base::Callback<void(bool)> add_accelerator_callback = | |
219 base::Bind(&OnAcceleratorAdded); | |
220 DCHECK(window_manager_client_); | |
221 window_manager_client_->AddAccelerator( | |
222 static_cast<uint32_t>(BrowserCommand::CLOSE), | |
223 mus::CreateKeyMatcher(mus::mojom::KeyboardCode::W, | |
224 mus::mojom::kEventFlagControlDown), | |
225 add_accelerator_callback); | |
226 window_manager_client_->AddAccelerator( | |
227 static_cast<uint32_t>(BrowserCommand::FOCUS_OMNIBOX), | |
228 mus::CreateKeyMatcher(mus::mojom::KeyboardCode::L, | |
229 mus::mojom::kEventFlagControlDown), | |
230 add_accelerator_callback); | |
231 window_manager_client_->AddAccelerator( | |
232 static_cast<uint32_t>(BrowserCommand::NEW_WINDOW), | |
233 mus::CreateKeyMatcher(mus::mojom::KeyboardCode::N, | |
234 mus::mojom::kEventFlagControlDown), | |
235 add_accelerator_callback); | |
236 window_manager_client_->AddAccelerator( | |
237 static_cast<uint32_t>(BrowserCommand::SHOW_FIND), | |
238 mus::CreateKeyMatcher(mus::mojom::KeyboardCode::F, | |
239 mus::mojom::kEventFlagControlDown), | |
240 add_accelerator_callback); | |
241 window_manager_client_->AddAccelerator( | |
242 static_cast<uint32_t>(BrowserCommand::GO_BACK), | |
243 mus::CreateKeyMatcher(mus::mojom::KeyboardCode::LEFT, | |
244 mus::mojom::kEventFlagAltDown), | |
245 add_accelerator_callback); | |
246 window_manager_client_->AddAccelerator( | |
247 static_cast<uint32_t>(BrowserCommand::GO_FORWARD), | |
248 mus::CreateKeyMatcher(mus::mojom::KeyboardCode::RIGHT, | |
249 mus::mojom::kEventFlagAltDown), | |
250 add_accelerator_callback); | |
251 | |
252 // Now that we're ready, load the default url. | |
253 LoadURL(default_url_); | |
254 | |
255 // Record the time spent opening initial tabs, used for performance testing. | |
256 const base::TimeDelta open_tabs_delta = | |
257 base::TimeTicks::Now() - display_ticks; | |
258 | |
259 // Record the browser startup time metrics, used for performance testing. | |
260 static bool recorded_browser_startup_metrics = false; | |
261 if (!recorded_browser_startup_metrics && | |
262 base::CommandLine::ForCurrentProcess()->HasSwitch( | |
263 tracing::kEnableStatsCollectionBindings)) { | |
264 tracing::StartupPerformanceDataCollectorPtr collector; | |
265 shell_->ConnectToService("mojo:tracing", &collector); | |
266 collector->SetBrowserWindowDisplayTicks(display_ticks.ToInternalValue()); | |
267 collector->SetBrowserOpenTabsTimeDelta(open_tabs_delta.ToInternalValue()); | |
268 collector->SetBrowserMessageLoopStartTicks( | |
269 manager_->startup_ticks().ToInternalValue()); | |
270 recorded_browser_startup_metrics = true; | |
271 } | |
272 } | |
273 | |
274 void BrowserWindow::OnConnectionLost(mus::WindowTreeConnection* connection) { | |
275 root_ = nullptr; | |
276 delete this; | |
277 } | |
278 | |
279 //////////////////////////////////////////////////////////////////////////////// | |
280 // BrowserWindow, mus::WindowManagerDelegate implementation: | |
281 | |
282 void BrowserWindow::SetWindowManagerClient(mus::WindowManagerClient* client) { | |
283 window_manager_client_ = client; | |
284 } | |
285 | |
286 bool BrowserWindow::OnWmSetBounds(mus::Window* window, gfx::Rect* bounds) { | |
287 return true; | |
288 } | |
289 | |
290 bool BrowserWindow::OnWmSetProperty( | |
291 mus::Window* window, | |
292 const std::string& name, | |
293 scoped_ptr<std::vector<uint8_t>>* new_data) { | |
294 return true; | |
295 } | |
296 | |
297 mus::Window* BrowserWindow::OnWmCreateTopLevelWindow( | |
298 std::map<std::string, std::vector<uint8_t>>* properties) { | |
299 NOTREACHED(); | |
300 return nullptr; | |
301 } | |
302 | |
303 void BrowserWindow::OnAccelerator(uint32_t id, mus::mojom::EventPtr event) { | |
304 switch (static_cast<BrowserCommand>(id)) { | |
305 case BrowserCommand::CLOSE: | |
306 Close(); | |
307 break; | |
308 case BrowserCommand::NEW_WINDOW: | |
309 manager_->CreateBrowser(GURL()); | |
310 break; | |
311 case BrowserCommand::FOCUS_OMNIBOX: | |
312 ShowOmnibox(); | |
313 break; | |
314 case BrowserCommand::SHOW_FIND: | |
315 ShowFind(); | |
316 break; | |
317 case BrowserCommand::GO_BACK: | |
318 GoBack(); | |
319 break; | |
320 case BrowserCommand::GO_FORWARD: | |
321 GoForward(); | |
322 break; | |
323 default: | |
324 NOTREACHED(); | |
325 break; | |
326 } | |
327 } | |
328 | |
329 //////////////////////////////////////////////////////////////////////////////// | |
330 // BrowserWindow, web_view::mojom::WebViewClient implementation: | |
331 | |
332 void BrowserWindow::TopLevelNavigateRequest(mojo::URLRequestPtr request) { | |
333 OnHideFindBar(); | |
334 Embed(std::move(request)); | |
335 } | |
336 | |
337 void BrowserWindow::TopLevelNavigationStarted(const mojo::String& url) { | |
338 GURL gurl(url.get()); | |
339 bool changed = current_url_ != gurl; | |
340 current_url_ = gurl; | |
341 if (changed) | |
342 toolbar_view_->SetOmniboxText(base::UTF8ToUTF16(current_url_.spec())); | |
343 } | |
344 | |
345 void BrowserWindow::LoadingStateChanged(bool is_loading, double progress) { | |
346 progress_bar_->SetIsLoading(is_loading); | |
347 progress_bar_->SetProgress(progress); | |
348 } | |
349 | |
350 void BrowserWindow::BackForwardChanged( | |
351 web_view::mojom::ButtonState back_button, | |
352 web_view::mojom::ButtonState forward_button) { | |
353 toolbar_view_->SetBackForwardEnabled( | |
354 back_button == web_view::mojom::ButtonState::ENABLED, | |
355 forward_button == web_view::mojom::ButtonState::ENABLED); | |
356 } | |
357 | |
358 void BrowserWindow::TitleChanged(const mojo::String& title) { | |
359 base::string16 formatted = | |
360 title.is_null() ? base::ASCIIToUTF16("Untitled") | |
361 : title.To<base::string16>() + | |
362 base::ASCIIToUTF16(" - Mandoline"); | |
363 host_->SetTitle(mojo::String::From(formatted)); | |
364 } | |
365 | |
366 void BrowserWindow::FindInPageMatchCountUpdated(int32_t request_id, | |
367 int32_t count, | |
368 bool final_update) { | |
369 find_count_ = count; | |
370 find_bar_view_->SetMatchLabel(find_active_, find_count_); | |
371 } | |
372 | |
373 void BrowserWindow::FindInPageSelectionUpdated(int32_t request_id, | |
374 int32_t active_match_ordinal) { | |
375 find_active_ = active_match_ordinal; | |
376 find_bar_view_->SetMatchLabel(find_active_, find_count_); | |
377 } | |
378 | |
379 //////////////////////////////////////////////////////////////////////////////// | |
380 // BrowserWindow, ViewEmbedder implementation: | |
381 | |
382 void BrowserWindow::Embed(mojo::URLRequestPtr request) { | |
383 const std::string string_url = request->url.To<std::string>(); | |
384 if (string_url == "mojo:omnibox") { | |
385 EmbedOmnibox(); | |
386 return; | |
387 } | |
388 web_view_.web_view()->LoadRequest(std::move(request)); | |
389 } | |
390 | |
391 //////////////////////////////////////////////////////////////////////////////// | |
392 // BrowserWindow, mojo::InterfaceFactory<ViewEmbedder> implementation: | |
393 | |
394 void BrowserWindow::Create(mojo::Connection* connection, | |
395 mojo::InterfaceRequest<ViewEmbedder> request) { | |
396 view_embedder_bindings_.AddBinding(this, std::move(request)); | |
397 } | |
398 | |
399 //////////////////////////////////////////////////////////////////////////////// | |
400 // BrowserWindow, FindBarDelegate implementation: | |
401 | |
402 void BrowserWindow::OnDoFind(const std::string& find, bool forward) { | |
403 web_view_.web_view()->Find(mojo::String::From(find), forward); | |
404 } | |
405 | |
406 void BrowserWindow::OnHideFindBar() { | |
407 toolbar_view_->SetVisible(true); | |
408 find_bar_view_->Hide(); | |
409 web_view_.web_view()->StopFinding(); | |
410 } | |
411 | |
412 //////////////////////////////////////////////////////////////////////////////// | |
413 // BrowserWindow, private: | |
414 | |
415 void BrowserWindow::Init(mus::Window* root) { | |
416 DCHECK_GT(root->viewport_metrics().device_pixel_ratio, 0); | |
417 if (!aura_init_) { | |
418 ui_init_.reset(new ui::mojo::UIInit(views::GetDisplaysFromWindow(root))); | |
419 aura_init_.reset(new views::AuraInit(shell_, "mandoline_ui.pak")); | |
420 } | |
421 | |
422 root_ = root; | |
423 omnibox_view_ = root_->connection()->NewWindow(); | |
424 root_->AddChild(omnibox_view_); | |
425 | |
426 views::WidgetDelegateView* widget_delegate = new views::WidgetDelegateView; | |
427 widget_delegate->GetContentsView()->set_background( | |
428 views::Background::CreateSolidBackground(0xFFDDDDDD)); | |
429 toolbar_view_ = new ToolbarView(this); | |
430 progress_bar_ = new ProgressView; | |
431 widget_delegate->GetContentsView()->AddChildView(toolbar_view_); | |
432 widget_delegate->GetContentsView()->AddChildView(progress_bar_); | |
433 widget_delegate->GetContentsView()->SetLayoutManager( | |
434 new LayoutManagerImpl(this)); | |
435 | |
436 find_bar_view_ = new FindBarView(this); | |
437 widget_delegate->GetContentsView()->AddChildView(find_bar_view_); | |
438 | |
439 views::Widget* widget = new views::Widget; | |
440 views::Widget::InitParams params( | |
441 views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); | |
442 params.native_widget = new views::NativeWidgetMus( | |
443 widget, shell_, root, mus::mojom::SurfaceType::DEFAULT); | |
444 params.delegate = widget_delegate; | |
445 params.bounds = root_->bounds(); | |
446 widget->Init(params); | |
447 widget->Show(); | |
448 root_->SetFocus(); | |
449 } | |
450 | |
451 void BrowserWindow::EmbedOmnibox() { | |
452 mus::mojom::WindowTreeClientPtr view_tree_client; | |
453 omnibox_->GetWindowTreeClient(GetProxy(&view_tree_client)); | |
454 omnibox_view_->Embed(std::move(view_tree_client)); | |
455 | |
456 // TODO(beng): This should be handled sufficiently by | |
457 // OmniboxImpl::ShowWindow() but unfortunately view manager policy | |
458 // currently prevents the embedded app from changing window z for | |
459 // its own window. | |
460 omnibox_view_->MoveToFront(); | |
461 } | |
462 | |
463 void BrowserWindow::Layout(views::View* host) { | |
464 // TODO(fsamuel): All bounds should be in physical pixels. | |
465 gfx::Rect bounds_in_physical_pixels(host->bounds()); | |
466 float inverse_device_pixel_ratio = | |
467 1.0f / root_->viewport_metrics().device_pixel_ratio; | |
468 | |
469 gfx::Rect toolbar_bounds = gfx::ScaleToEnclosingRect( | |
470 bounds_in_physical_pixels, inverse_device_pixel_ratio); | |
471 toolbar_bounds.Inset(10, 10, 10, toolbar_bounds.height() - 40); | |
472 toolbar_view_->SetBoundsRect(toolbar_bounds); | |
473 | |
474 find_bar_view_->SetBoundsRect(toolbar_bounds); | |
475 | |
476 gfx::Rect progress_bar_bounds(toolbar_bounds.x(), toolbar_bounds.bottom() + 2, | |
477 toolbar_bounds.width(), 5); | |
478 | |
479 // The content view bounds are in physical pixels. | |
480 gfx::Rect content_bounds(DIPSToPixels(progress_bar_bounds.x()), | |
481 DIPSToPixels(progress_bar_bounds.bottom() + 10), 0, | |
482 0); | |
483 content_bounds.set_width(DIPSToPixels(progress_bar_bounds.width())); | |
484 content_bounds.set_height(host->bounds().height() - content_bounds.y() - | |
485 DIPSToPixels(10)); | |
486 content_->SetBounds(content_bounds); | |
487 | |
488 // The omnibox view bounds are in physical pixels. | |
489 omnibox_view_->SetBounds(bounds_in_physical_pixels); | |
490 } | |
491 | |
492 } // namespace mandoline | |
OLD | NEW |