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