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.
5 #include "mandoline/ui/desktop_ui/browser_window.h"
7 #include "base/command_line.h"
8 #include "base/strings/string16.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/time/time.h"
11 #include "components/mus/public/cpp/scoped_view_ptr.h"
12 #include "components/mus/public/cpp/view_tree_host_factory.h"
13 #include "mandoline/ui/aura/native_widget_view_manager.h"
14 #include "mandoline/ui/desktop_ui/browser_commands.h"
15 #include "mandoline/ui/desktop_ui/browser_manager.h"
16 #include "mandoline/ui/desktop_ui/public/interfaces/omnibox.mojom.h"
17 #include "mandoline/ui/desktop_ui/toolbar_view.h"
18 #include "mojo/common/common_type_converters.h"
19 #include "mojo/converters/geometry/geometry_type_converters.h"
20 #include "mojo/services/tracing/public/cpp/switches.h"
21 #include "mojo/services/tracing/public/interfaces/tracing.mojom.h"
22 #include "ui/gfx/canvas.h"
23 #include "ui/views/background.h"
24 #include "ui/views/controls/button/label_button.h"
25 #include "ui/views/widget/widget_delegate.h"
29 class ProgressView
: public views::View
{
31 ProgressView() : progress_(0.f
), loading_(false) {}
32 ~ProgressView() override
{}
34 void SetProgress(double progress
) {
39 void SetIsLoading(bool loading
) {
40 if (loading
== loading_
)
49 void OnPaint(gfx::Canvas
* canvas
) override
{
51 canvas
->FillRect(GetLocalBounds(), SK_ColorGREEN
);
52 gfx::Rect progress_rect
= GetLocalBounds();
53 progress_rect
.set_width(progress_rect
.width() * progress_
);
54 canvas
->FillRect(progress_rect
, SK_ColorRED
);
56 canvas
->FillRect(GetLocalBounds(), SK_ColorGRAY
);
63 DISALLOW_COPY_AND_ASSIGN(ProgressView
);
66 ////////////////////////////////////////////////////////////////////////////////
67 // BrowserWindow, public:
69 BrowserWindow::BrowserWindow(mojo::ApplicationImpl
* app
,
70 mojo::ViewTreeHostFactory
* host_factory
,
71 BrowserManager
* manager
)
73 host_client_binding_(this),
75 toolbar_view_(nullptr),
76 progress_bar_(nullptr),
79 omnibox_view_(nullptr),
81 mojo::ViewTreeHostClientPtr host_client
;
82 host_client_binding_
.Bind(GetProxy(&host_client
));
83 mus::CreateViewTreeHost(host_factory
, host_client
.Pass(), this, &host_
);
86 void BrowserWindow::LoadURL(const GURL
& url
) {
87 // Haven't been embedded yet, can't embed.
88 // TODO(beng): remove this.
94 if (!url
.is_valid()) {
99 mojo::URLRequestPtr
request(mojo::URLRequest::New());
100 request
->url
= url
.spec();
101 Embed(request
.Pass());
104 void BrowserWindow::Close() {
106 mus::ScopedViewPtr::DeleteViewOrViewManager(root_
);
111 void BrowserWindow::ShowOmnibox() {
112 if (!omnibox_
.get()) {
113 mojo::URLRequestPtr
request(mojo::URLRequest::New());
114 request
->url
= mojo::String::From("mojo:omnibox");
115 omnibox_connection_
= app_
->ConnectToApplication(request
.Pass());
116 omnibox_connection_
->AddService
<ViewEmbedder
>(this);
117 omnibox_connection_
->ConnectToService(&omnibox_
);
118 omnibox_connection_
->SetRemoteServiceProviderConnectionErrorHandler(
120 // This will cause the connection to be re-established the next time
121 // we come through this codepath.
125 omnibox_
->ShowForURL(mojo::String::From(current_url_
.spec()));
128 void BrowserWindow::GoBack() {
129 web_view_
.web_view()->GoBack();
132 void BrowserWindow::GoForward() {
133 web_view_
.web_view()->GoForward();
136 BrowserWindow::~BrowserWindow() {
138 manager_
->BrowserWindowClosed(this);
141 float BrowserWindow::DIPSToPixels(float value
) const {
142 return value
* root_
->viewport_metrics().device_pixel_ratio
;
145 ////////////////////////////////////////////////////////////////////////////////
146 // BrowserWindow, mus::ViewTreeDelegate implementation:
148 void BrowserWindow::OnEmbed(mus::View
* root
) {
149 // BrowserWindow does not support being embedded more than once.
152 // Record when the browser window was displayed, used for performance testing.
153 const base::Time display_time
= base::Time::Now();
157 host_
->SetTitle("Mandoline");
159 content_
= root_
->connection()->CreateView();
162 host_
->SetSize(mojo::Size::From(gfx::Size(1280, 800)));
164 root_
->AddChild(content_
);
165 content_
->SetVisible(true);
167 web_view_
.Init(app_
, content_
);
169 host_
->AddAccelerator(static_cast<uint32_t>(BrowserCommand::CLOSE
),
170 mojo::KEYBOARD_CODE_W
, mojo::EVENT_FLAGS_CONTROL_DOWN
);
171 host_
->AddAccelerator(static_cast<uint32_t>(BrowserCommand::FOCUS_OMNIBOX
),
172 mojo::KEYBOARD_CODE_L
, mojo::EVENT_FLAGS_CONTROL_DOWN
);
173 host_
->AddAccelerator(static_cast<uint32_t>(BrowserCommand::NEW_WINDOW
),
174 mojo::KEYBOARD_CODE_N
, mojo::EVENT_FLAGS_CONTROL_DOWN
);
175 host_
->AddAccelerator(static_cast<uint32_t>(BrowserCommand::GO_BACK
),
176 mojo::KEYBOARD_CODE_LEFT
, mojo::EVENT_FLAGS_ALT_DOWN
);
177 host_
->AddAccelerator(static_cast<uint32_t>(BrowserCommand::GO_FORWARD
),
178 mojo::KEYBOARD_CODE_RIGHT
, mojo::EVENT_FLAGS_ALT_DOWN
);
180 // Now that we're ready, load the default url.
181 LoadURL(default_url_
);
183 // Record the time spent opening initial tabs, used for performance testing.
184 const base::TimeDelta open_tabs_delta
= base::Time::Now() - display_time
;
186 // Record the browser startup time metrics, used for performance testing.
187 static bool recorded_browser_startup_metrics
= false;
188 if (!recorded_browser_startup_metrics
&&
189 base::CommandLine::ForCurrentProcess()->HasSwitch(
190 tracing::kEnableStatsCollectionBindings
)) {
191 mojo::URLRequestPtr
request(mojo::URLRequest::New());
192 request
->url
= mojo::String::From("mojo:tracing");
193 tracing::StartupPerformanceDataCollectorPtr collector
;
194 app_
->ConnectToService(request
.Pass(), &collector
);
195 collector
->SetBrowserWindowDisplayTime(display_time
.ToInternalValue());
196 collector
->SetBrowserOpenTabsTimeDelta(open_tabs_delta
.ToInternalValue());
197 collector
->SetBrowserMessageLoopStartTime(
198 manager_
->startup_time().ToInternalValue());
199 recorded_browser_startup_metrics
= true;
203 void BrowserWindow::OnConnectionLost(mus::ViewTreeConnection
* connection
) {
208 ////////////////////////////////////////////////////////////////////////////////
209 // BrowserWindow, mus::ViewTreeHostClient implementation:
211 void BrowserWindow::OnAccelerator(uint32_t id
, mojo::EventPtr event
) {
212 switch (static_cast<BrowserCommand
>(id
)) {
213 case BrowserCommand::CLOSE
:
216 case BrowserCommand::NEW_WINDOW
:
217 manager_
->CreateBrowser(GURL());
219 case BrowserCommand::FOCUS_OMNIBOX
:
222 case BrowserCommand::GO_BACK
:
225 case BrowserCommand::GO_FORWARD
:
234 ////////////////////////////////////////////////////////////////////////////////
235 // BrowserWindow, web_view::mojom::WebViewClient implementation:
237 void BrowserWindow::TopLevelNavigate(mojo::URLRequestPtr request
) {
238 Embed(request
.Pass());
241 void BrowserWindow::LoadingStateChanged(bool is_loading
, double progress
) {
242 progress_bar_
->SetIsLoading(is_loading
);
243 progress_bar_
->SetProgress(progress
);
246 void BrowserWindow::BackForwardChanged(
247 web_view::mojom::ButtonState back_button
,
248 web_view::mojom::ButtonState forward_button
) {
249 toolbar_view_
->SetBackForwardEnabled(
250 back_button
== web_view::mojom::ButtonState::BUTTON_STATE_ENABLED
,
251 forward_button
== web_view::mojom::ButtonState::BUTTON_STATE_ENABLED
);
254 void BrowserWindow::TitleChanged(const mojo::String
& title
) {
255 base::string16 formatted
=
256 title
.is_null() ? base::ASCIIToUTF16("Untitled")
257 : title
.To
<base::string16
>() +
258 base::ASCIIToUTF16(" - Mandoline");
259 host_
->SetTitle(mojo::String::From(formatted
));
262 ////////////////////////////////////////////////////////////////////////////////
263 // BrowserWindow, ViewEmbedder implementation:
265 void BrowserWindow::Embed(mojo::URLRequestPtr request
) {
266 const std::string string_url
= request
->url
.To
<std::string
>();
267 if (string_url
== "mojo:omnibox") {
272 GURL
gurl(string_url
);
273 bool changed
= current_url_
!= gurl
;
276 toolbar_view_
->SetOmniboxText(base::UTF8ToUTF16(current_url_
.spec()));
278 web_view_
.web_view()->LoadRequest(request
.Pass());
281 ////////////////////////////////////////////////////////////////////////////////
282 // BrowserWindow, mojo::InterfaceFactory<ViewEmbedder> implementation:
284 void BrowserWindow::Create(mojo::ApplicationConnection
* connection
,
285 mojo::InterfaceRequest
<ViewEmbedder
> request
) {
286 view_embedder_bindings_
.AddBinding(this, request
.Pass());
289 ////////////////////////////////////////////////////////////////////////////////
290 // BrowserWindow, views::LayoutManager implementation:
292 gfx::Size
BrowserWindow::GetPreferredSize(const views::View
* view
) const {
296 void BrowserWindow::Layout(views::View
* host
) {
297 // TODO(fsamuel): All bounds should be in physical pixels.
298 gfx::Rect
bounds_in_physical_pixels(host
->bounds());
299 float inverse_device_pixel_ratio
=
300 1.0f
/ root_
->viewport_metrics().device_pixel_ratio
;
302 gfx::Rect toolbar_bounds
= gfx::ScaleToEnclosingRect(
303 bounds_in_physical_pixels
, inverse_device_pixel_ratio
);
304 toolbar_bounds
.Inset(10, 10, 10, toolbar_bounds
.height() - 40);
305 toolbar_view_
->SetBoundsRect(toolbar_bounds
);
307 gfx::Rect
progress_bar_bounds(toolbar_bounds
.x(), toolbar_bounds
.bottom() + 2,
308 toolbar_bounds
.width(), 5);
310 // The content view bounds are in physical pixels.
311 mojo::Rect content_bounds_mojo
;
312 content_bounds_mojo
.x
= DIPSToPixels(progress_bar_bounds
.x());
313 content_bounds_mojo
.y
= DIPSToPixels(progress_bar_bounds
.bottom()+ 10);
314 content_bounds_mojo
.width
= DIPSToPixels(progress_bar_bounds
.width());
315 content_bounds_mojo
.height
=
316 host
->bounds().height() - content_bounds_mojo
.y
- DIPSToPixels(10);
317 content_
->SetBounds(content_bounds_mojo
);
319 // The omnibox view bounds are in physical pixels.
320 omnibox_view_
->SetBounds(
321 mojo::TypeConverter
<mojo::Rect
, gfx::Rect
>::Convert(
322 bounds_in_physical_pixels
));
325 ////////////////////////////////////////////////////////////////////////////////
326 // BrowserWindow, private:
328 void BrowserWindow::Init(mus::View
* root
) {
329 DCHECK_GT(root
->viewport_metrics().device_pixel_ratio
, 0);
331 aura_init_
.reset(new AuraInit(root
, app_
->shell()));
334 omnibox_view_
= root_
->connection()->CreateView();
335 root_
->AddChild(omnibox_view_
);
337 views::WidgetDelegateView
* widget_delegate
= new views::WidgetDelegateView
;
338 widget_delegate
->GetContentsView()->set_background(
339 views::Background::CreateSolidBackground(0xFFDDDDDD));
340 toolbar_view_
= new ToolbarView(this);
341 progress_bar_
= new ProgressView
;
342 widget_delegate
->GetContentsView()->AddChildView(toolbar_view_
);
343 widget_delegate
->GetContentsView()->AddChildView(progress_bar_
);
344 widget_delegate
->GetContentsView()->SetLayoutManager(this);
346 views::Widget
* widget
= new views::Widget
;
347 views::Widget::InitParams
params(
348 views::Widget::InitParams::TYPE_WINDOW_FRAMELESS
);
349 params
.native_widget
=
350 new NativeWidgetViewManager(widget
, app_
->shell(), root_
);
351 params
.delegate
= widget_delegate
;
352 params
.bounds
= root_
->bounds().To
<gfx::Rect
>();
353 widget
->Init(params
);
358 void BrowserWindow::EmbedOmnibox() {
359 mojo::ViewTreeClientPtr view_tree_client
;
360 omnibox_
->GetViewTreeClient(GetProxy(&view_tree_client
));
361 omnibox_view_
->Embed(view_tree_client
.Pass());
363 // TODO(beng): This should be handled sufficiently by
364 // OmniboxImpl::ShowWindow() but unfortunately view manager policy
365 // currently prevents the embedded app from changing window z for
367 omnibox_view_
->MoveToFront();
370 } // namespace mandoline