Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 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 "mash/browser/browser.h" | |
| 6 | |
| 7 #include "base/macros.h" | |
| 8 #include "base/memory/weak_ptr.h" | |
| 9 #include "base/message_loop/message_loop.h" | |
| 10 #include "base/strings/string16.h" | |
| 11 #include "base/strings/string_util.h" | |
| 12 #include "base/strings/utf_string_conversions.h" | |
| 13 #include "base/timer/timer.h" | |
| 14 #include "components/mus/public/cpp/window.h" | |
| 15 #include "components/mus/public/cpp/window_tree_connection.h" | |
| 16 #include "mash/public/interfaces/launchable.mojom.h" | |
| 17 #include "mojo/converters/geometry/geometry_type_converters.h" | |
| 18 #include "mojo/public/c/system/main.h" | |
| 19 #include "services/navigation/public/interfaces/view.mojom.h" | |
| 20 #include "services/shell/public/cpp/application_runner.h" | |
| 21 #include "services/shell/public/cpp/connector.h" | |
| 22 #include "services/shell/public/cpp/shell_client.h" | |
| 23 #include "services/tracing/public/cpp/tracing_impl.h" | |
| 24 #include "ui/aura/mus/mus_util.h" | |
| 25 #include "ui/gfx/canvas.h" | |
| 26 #include "ui/gfx/paint_throbber.h" | |
| 27 #include "ui/gfx/text_constants.h" | |
| 28 #include "ui/native_theme/native_theme.h" | |
| 29 #include "ui/views/background.h" | |
| 30 #include "ui/views/controls/button/label_button.h" | |
| 31 #include "ui/views/controls/textfield/textfield.h" | |
| 32 #include "ui/views/controls/textfield/textfield_controller.h" | |
| 33 #include "ui/views/mus/aura_init.h" | |
| 34 #include "ui/views/mus/window_manager_connection.h" | |
| 35 #include "ui/views/widget/widget_delegate.h" | |
| 36 #include "url/gurl.h" | |
| 37 | |
| 38 namespace views { | |
| 39 class AuraInit; | |
|
sky
2016/05/16 15:31:58
You shouldn't need to forward declare this as you
| |
| 40 } | |
| 41 | |
| 42 namespace mash { | |
| 43 namespace browser { | |
| 44 | |
| 45 void EnableButton(views::CustomButton* button, bool enabled) { | |
| 46 button->SetState(enabled ? views::Button::STATE_NORMAL | |
| 47 : views::Button::STATE_DISABLED); | |
| 48 } | |
| 49 | |
| 50 class ProgressBar : public views::View { | |
|
sky
2016/05/16 15:31:58
Did you try using the ProgressBar in views?
| |
| 51 public: | |
| 52 ProgressBar() {} | |
| 53 ~ProgressBar() override {} | |
| 54 | |
| 55 void SetProgress(double progress) { | |
| 56 progress_ = progress; | |
| 57 SchedulePaint(); | |
| 58 } | |
| 59 | |
| 60 private: | |
| 61 void OnPaint(gfx::Canvas* canvas) override { | |
| 62 if (progress_ == 0.f) { | |
| 63 gfx::Rect progress_rect = GetLocalBounds(); | |
| 64 progress_rect.set_y(progress_rect.bottom() - 1); | |
| 65 progress_rect.set_height(1); | |
| 66 canvas->FillRect(progress_rect, SK_ColorGRAY); | |
| 67 } else { | |
| 68 gfx::Rect progress_rect = GetLocalBounds(); | |
| 69 progress_rect.set_width(progress_rect.width() * progress_); | |
| 70 canvas->FillRect(progress_rect, SK_ColorRED); | |
| 71 } | |
| 72 } | |
| 73 | |
| 74 double progress_ = 0.f; | |
| 75 | |
| 76 DISALLOW_COPY_AND_ASSIGN(ProgressBar); | |
| 77 }; | |
| 78 | |
| 79 class Throbber : public views::View { | |
| 80 public: | |
| 81 Throbber() : timer_(false, true), weak_factory_(this) {} | |
| 82 ~Throbber() override {} | |
| 83 | |
| 84 void Throb() { | |
|
sky
2016/05/16 15:31:58
nit: you have a Stop(), I would be inclined to cal
| |
| 85 throbbing_ = true; | |
|
sky
2016/05/16 15:31:58
DCHECK(!throbbing_), or perhaps early out if alrea
| |
| 86 start_time_ = base::TimeTicks(); | |
|
sky
2016/05/16 15:31:58
base::TimeTicks::Now()?
| |
| 87 SchedulePaint(); | |
| 88 timer_.Start( | |
| 89 FROM_HERE, base::TimeDelta::FromMilliseconds(16), | |
|
sky
2016/05/16 15:31:58
You sure you want 16? We changed chrome to 30 a wh
| |
| 90 base::Bind(&Throbber::SchedulePaint, | |
| 91 weak_factory_.GetWeakPtr())); | |
| 92 } | |
| 93 | |
| 94 void Stop() { | |
| 95 throbbing_ = false; | |
| 96 if (timer_.IsRunning()) | |
| 97 timer_.Stop(); | |
| 98 SchedulePaint(); | |
| 99 } | |
| 100 | |
| 101 private: | |
| 102 void OnPaint(gfx::Canvas* canvas) override { | |
| 103 if (!throbbing_) | |
| 104 return; | |
| 105 | |
| 106 if (start_time_ == base::TimeTicks()) | |
|
sky
2016/05/16 15:31:58
How come you set this here and not in Throb?
| |
| 107 start_time_ = base::TimeTicks::Now(); | |
| 108 | |
| 109 gfx::PaintThrobberSpinning( | |
| 110 canvas, GetLocalBounds(), | |
| 111 GetNativeTheme()->GetSystemColor( | |
| 112 ui::NativeTheme::kColorId_ThrobberSpinningColor), | |
| 113 base::TimeTicks::Now() - start_time_); | |
| 114 } | |
| 115 | |
| 116 bool throbbing_ = false; | |
| 117 base::TimeTicks start_time_; | |
| 118 base::Timer timer_; | |
| 119 base::WeakPtrFactory<Throbber> weak_factory_; | |
| 120 | |
| 121 DISALLOW_COPY_AND_ASSIGN(Throbber); | |
| 122 }; | |
| 123 | |
| 124 class UI : public views::WidgetDelegateView, | |
| 125 public views::ButtonListener, | |
| 126 public views::TextfieldController, | |
| 127 public navigation::mojom::ViewClient { | |
| 128 public: | |
| 129 enum class Type { | |
| 130 WINDOW, | |
| 131 POPUP | |
| 132 }; | |
| 133 | |
| 134 UI(Browser* browser, | |
| 135 Type type, | |
| 136 navigation::mojom::ViewPtr view, | |
| 137 navigation::mojom::ViewClientRequest request) | |
| 138 : browser_(browser), | |
| 139 type_(type), | |
| 140 back_button_(new views::LabelButton(this, L"Back")), | |
| 141 forward_button_(new views::LabelButton(this, L"Forward")), | |
| 142 reload_button_(new views::LabelButton(this, L"Reload")), | |
| 143 prompt_(new views::Textfield), | |
| 144 throbber_(new Throbber), | |
| 145 progress_bar_(new ProgressBar), | |
| 146 view_(std::move(view)), | |
| 147 view_client_binding_(this, std::move(request)) { | |
| 148 set_background(views::Background::CreateStandardPanelBackground()); | |
| 149 prompt_->set_controller(this); | |
| 150 back_button_->set_request_focus_on_press(false); | |
| 151 forward_button_->set_request_focus_on_press(false); | |
| 152 reload_button_->set_request_focus_on_press(false); | |
| 153 AddChildView(back_button_); | |
| 154 AddChildView(forward_button_); | |
| 155 AddChildView(reload_button_); | |
| 156 AddChildView(prompt_); | |
| 157 AddChildView(throbber_); | |
| 158 AddChildView(progress_bar_); | |
| 159 } | |
| 160 ~UI() override { | |
| 161 browser_->RemoveWindow(GetWidget()); | |
| 162 } | |
| 163 | |
| 164 void NavigateTo(const GURL& url) { | |
| 165 view_->NavigateTo(url); | |
| 166 } | |
| 167 | |
| 168 private: | |
| 169 // Overridden from views::WidgetDelegate: | |
| 170 views::View* GetContentsView() override { return this; } | |
| 171 base::string16 GetWindowTitle() const override { | |
| 172 // TODO(beng): use resources. | |
| 173 if (current_title_.empty()) | |
| 174 return base::ASCIIToUTF16("Browser"); | |
| 175 base::string16 format = base::ASCIIToUTF16("%s - Browser"); | |
| 176 base::ReplaceFirstSubstringAfterOffset( | |
| 177 &format, 0, base::ASCIIToUTF16("%s"), current_title_); | |
| 178 return format; | |
| 179 } | |
| 180 bool CanResize() const override { return true; } | |
| 181 bool CanMaximize() const override { return true; } | |
| 182 bool CanMinimize() const override { return true; } | |
| 183 | |
| 184 // views::ButtonListener: | |
| 185 void ButtonPressed(views::Button* sender, | |
| 186 const ui::Event& event) override { | |
| 187 if (sender == back_button_) { | |
| 188 view_->GoBack(); | |
| 189 } else if (sender == forward_button_) { | |
| 190 view_->GoForward(); | |
| 191 } else if (sender == reload_button_) { | |
| 192 if (is_loading_) | |
| 193 view_->Stop(); | |
| 194 else | |
| 195 view_->Reload(false); | |
| 196 } | |
| 197 } | |
| 198 | |
| 199 // Overridden from views::View: | |
| 200 void Layout() override { | |
| 201 gfx::Rect local_bounds = GetLocalBounds(); | |
| 202 gfx::Rect bounds = local_bounds; | |
| 203 bounds.Inset(5, 5); | |
| 204 | |
| 205 gfx::Size ps = back_button_->GetPreferredSize(); | |
| 206 back_button_->SetBoundsRect( | |
| 207 gfx::Rect(bounds.x(), bounds.y(), ps.width(), ps.height())); | |
|
sky
2016/05/16 15:31:58
nit: run git cl format
| |
| 208 ps = forward_button_->GetPreferredSize(); | |
| 209 forward_button_->SetBoundsRect( | |
| 210 gfx::Rect(back_button_->bounds().right() + 5, bounds.y(), | |
| 211 ps.width(), ps.height())); | |
| 212 ps = reload_button_->GetPreferredSize(); | |
| 213 reload_button_->SetBoundsRect( | |
| 214 gfx::Rect(forward_button_->bounds().right() + 5, bounds.y(), | |
| 215 ps.width(), ps.height())); | |
| 216 | |
| 217 ps = prompt_->GetPreferredSize(); | |
| 218 int prompt_y = | |
| 219 bounds.y() + (reload_button_->bounds().height() - ps.height()) / 2; | |
| 220 int width = | |
| 221 bounds.width() - reload_button_->bounds().right() - ps.height() - 10; | |
| 222 prompt_->SetBoundsRect( | |
| 223 gfx::Rect(reload_button_->bounds().right() + 5, prompt_y, width, | |
| 224 ps.height())); | |
| 225 throbber_->SetBoundsRect( | |
| 226 gfx::Rect(prompt_->bounds().right() + 5, prompt_->bounds().y(), | |
| 227 ps.height(), ps.height())); | |
| 228 | |
| 229 gfx::Rect progress_bar_rect( | |
| 230 local_bounds.x(), back_button_->bounds().bottom() + 5, | |
| 231 local_bounds.width(), 2); | |
| 232 progress_bar_->SetBoundsRect(progress_bar_rect); | |
| 233 | |
| 234 if (content_area_) { | |
| 235 int x = local_bounds.x(); | |
| 236 int y = type_ == Type::POPUP ? 0 : progress_bar_->bounds().bottom(); | |
| 237 gfx::Point offset(x, y); | |
| 238 ConvertPointToWidget(this, &offset); | |
| 239 int width = local_bounds.width(); | |
| 240 int height = local_bounds.height() - y; | |
| 241 content_area_->SetBounds( | |
| 242 gfx::Rect(offset.x(), offset.y(), width, height)); | |
| 243 } | |
| 244 } | |
| 245 void ViewHierarchyChanged( | |
| 246 const views::View::ViewHierarchyChangedDetails& details) override { | |
| 247 if (details.is_add && GetWidget() && !content_area_) { | |
| 248 mus::Window* window = aura::GetMusWindow(GetWidget()->GetNativeWindow()); | |
| 249 content_area_ = window->connection()->NewWindow(nullptr); | |
| 250 window->AddChild(content_area_); | |
| 251 | |
| 252 mus::mojom::WindowTreeClientPtr client; | |
| 253 view_->GetWindowTreeClient(GetProxy(&client)); | |
| 254 content_area_->Embed(std::move(client)); | |
| 255 } | |
| 256 } | |
| 257 | |
| 258 // Overridden from views::TextFieldController: | |
| 259 bool HandleKeyEvent(views::Textfield* sender, | |
| 260 const ui::KeyEvent& key_event) override { | |
| 261 switch (key_event.key_code()) { | |
| 262 case ui::VKEY_RETURN: { | |
| 263 view_->NavigateTo(GURL(prompt_->text())); | |
| 264 } break; | |
| 265 default: | |
| 266 break; | |
| 267 } | |
| 268 return false; | |
| 269 } | |
| 270 | |
| 271 // navigation::mojom::ViewClient: | |
| 272 void LoadingStateChanged(bool is_loading) override { | |
| 273 is_loading_ = is_loading; | |
| 274 if (is_loading_) { | |
| 275 //reload_button_->SetText(L"Stop"); | |
|
sky
2016/05/16 15:31:58
?
| |
| 276 throbber_->Throb(); | |
| 277 } else { | |
| 278 reload_button_->SetText(L"Reload"); | |
| 279 throbber_->Stop(); | |
| 280 progress_bar_->SetProgress(0.f); | |
| 281 } | |
| 282 } | |
| 283 void NavigationStateChanged(const GURL& url, | |
| 284 const mojo::String& title, | |
| 285 bool can_go_back, | |
| 286 bool can_go_forward) override { | |
| 287 EnableButton(back_button_, can_go_back); | |
| 288 EnableButton(forward_button_, can_go_forward); | |
| 289 prompt_->SetText(base::UTF8ToUTF16(url.spec())); | |
| 290 current_title_ = base::UTF8ToUTF16(title.get()); | |
| 291 GetWidget()->UpdateWindowTitle(); | |
| 292 } | |
| 293 void LoadProgressChanged(double progress) override { | |
| 294 progress_bar_->SetProgress(progress); | |
| 295 } | |
| 296 void ViewCreated(navigation::mojom::ViewPtr view, | |
| 297 navigation::mojom::ViewClientRequest request, | |
| 298 bool is_popup, | |
| 299 mojo::RectPtr initial_rect, | |
| 300 bool user_gesture) override { | |
| 301 views::Widget* window = views::Widget::CreateWindowWithContextAndBounds( | |
| 302 new UI(browser_, is_popup ? UI::Type::POPUP : UI::Type::WINDOW, | |
| 303 std::move(view), std::move(request)), nullptr, | |
| 304 initial_rect.To<gfx::Rect>()); | |
| 305 window->Show(); | |
| 306 browser_->AddWindow(window); | |
| 307 } | |
| 308 void Close() override { | |
| 309 GetWidget()->Close(); | |
| 310 } | |
| 311 | |
| 312 Browser* browser_; | |
| 313 | |
| 314 Type type_; | |
| 315 | |
| 316 views::LabelButton* back_button_; | |
| 317 views::LabelButton* forward_button_; | |
| 318 views::LabelButton* reload_button_; | |
| 319 views::Textfield* prompt_; | |
| 320 Throbber* throbber_; | |
| 321 ProgressBar* progress_bar_; | |
| 322 | |
| 323 mus::Window* content_area_ = nullptr; | |
| 324 | |
| 325 navigation::mojom::ViewPtr view_; | |
| 326 mojo::Binding<navigation::mojom::ViewClient> view_client_binding_; | |
| 327 | |
| 328 bool is_loading_ = false; | |
| 329 base::string16 current_title_; | |
| 330 | |
| 331 DISALLOW_COPY_AND_ASSIGN(UI); | |
| 332 }; | |
| 333 | |
| 334 Browser::Browser() {} | |
| 335 Browser::~Browser() {} | |
| 336 | |
| 337 void Browser::AddWindow(views::Widget* window) { | |
| 338 windows_.push_back(window); | |
| 339 } | |
| 340 | |
| 341 void Browser::RemoveWindow(views::Widget* window) { | |
| 342 auto it = std::find(windows_.begin(), windows_.end(), window); | |
| 343 DCHECK(it != windows_.end()); | |
| 344 windows_.erase(it); | |
| 345 if (windows_.empty()) | |
| 346 base::MessageLoop::current()->QuitWhenIdle(); | |
| 347 } | |
| 348 | |
| 349 void Browser::Initialize(shell::Connector* connector, | |
| 350 const shell::Identity& identity, | |
| 351 uint32_t id) { | |
| 352 connector_ = connector; | |
| 353 tracing_.Initialize(connector, identity.name()); | |
| 354 | |
| 355 aura_init_.reset(new views::AuraInit(connector, "views_mus_resources.pak")); | |
| 356 views::WindowManagerConnection::Create(connector, identity); | |
| 357 } | |
| 358 | |
| 359 bool Browser::AcceptConnection(shell::Connection* connection) { | |
| 360 connection->AddInterface<mojom::Launchable>(this); | |
| 361 return true; | |
| 362 } | |
| 363 | |
| 364 void Browser::Launch(uint32_t what, mojom::LaunchMode how) { | |
| 365 bool reuse = how == mojom::LaunchMode::REUSE || | |
| 366 how == mojom::LaunchMode::DEFAULT; | |
| 367 if (reuse && !windows_.empty()) { | |
| 368 windows_.back()->Activate(); | |
| 369 return; | |
| 370 } | |
| 371 | |
| 372 navigation::mojom::ViewFactoryPtr view_factory; | |
| 373 connector_->ConnectToInterface("exe:navigation", &view_factory); | |
| 374 navigation::mojom::ViewPtr view; | |
| 375 navigation::mojom::ViewClientPtr view_client; | |
| 376 navigation::mojom::ViewClientRequest view_client_request = | |
| 377 GetProxy(&view_client); | |
| 378 view_factory->CreateView(std::move(view_client), GetProxy(&view)); | |
| 379 UI* ui = new UI(this, UI::Type::WINDOW, std::move(view), | |
| 380 std::move(view_client_request)); | |
| 381 views::Widget* window = views::Widget::CreateWindowWithContextAndBounds( | |
| 382 ui, nullptr, gfx::Rect(10, 10, 1024, 600)); | |
| 383 ui->NavigateTo(GURL("http://www.google.com/")); | |
| 384 window->Show(); | |
| 385 AddWindow(window); | |
| 386 } | |
| 387 | |
| 388 void Browser::Create(shell::Connection* connection, | |
| 389 mojom::LaunchableRequest request) { | |
| 390 bindings_.AddBinding(this, std::move(request)); | |
| 391 } | |
| 392 | |
| 393 } // namespace browser | |
| 394 } // namespace mash | |
| OLD | NEW |