| 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/ptr_util.h" | |
| 9 #include "base/memory/weak_ptr.h" | |
| 10 #include "base/message_loop/message_loop.h" | |
| 11 #include "base/strings/string16.h" | |
| 12 #include "base/strings/string_util.h" | |
| 13 #include "base/strings/utf_string_conversions.h" | |
| 14 #include "base/threading/thread_task_runner_handle.h" | |
| 15 #include "base/timer/timer.h" | |
| 16 #include "content/public/common/service_names.mojom.h" | |
| 17 #include "mash/browser/debug_view.h" | |
| 18 #include "mash/public/interfaces/launchable.mojom.h" | |
| 19 #include "services/navigation/public/cpp/view.h" | |
| 20 #include "services/navigation/public/cpp/view_delegate.h" | |
| 21 #include "services/navigation/public/cpp/view_observer.h" | |
| 22 #include "services/navigation/public/interfaces/view.mojom.h" | |
| 23 #include "services/service_manager/public/c/main.h" | |
| 24 #include "services/service_manager/public/cpp/connector.h" | |
| 25 #include "services/service_manager/public/cpp/service.h" | |
| 26 #include "services/service_manager/public/cpp/service_context.h" | |
| 27 #include "services/service_manager/public/cpp/service_runner.h" | |
| 28 #include "ui/aura/window.h" | |
| 29 #include "ui/base/models/menu_model.h" | |
| 30 #include "ui/gfx/canvas.h" | |
| 31 #include "ui/gfx/paint_throbber.h" | |
| 32 #include "ui/gfx/text_constants.h" | |
| 33 #include "ui/native_theme/native_theme.h" | |
| 34 #include "ui/views/background.h" | |
| 35 #include "ui/views/controls/button/label_button.h" | |
| 36 #include "ui/views/controls/menu/menu_model_adapter.h" | |
| 37 #include "ui/views/controls/menu/menu_runner.h" | |
| 38 #include "ui/views/controls/textfield/textfield.h" | |
| 39 #include "ui/views/controls/textfield/textfield_controller.h" | |
| 40 #include "ui/views/layout/box_layout.h" | |
| 41 #include "ui/views/mus/aura_init.h" | |
| 42 #include "ui/views/widget/widget.h" | |
| 43 #include "ui/views/widget/widget_delegate.h" | |
| 44 #include "url/gurl.h" | |
| 45 | |
| 46 namespace mash { | |
| 47 namespace browser { | |
| 48 | |
| 49 void EnableButton(views::CustomButton* button, bool enabled) { | |
| 50 button->SetState(enabled ? views::Button::STATE_NORMAL | |
| 51 : views::Button::STATE_DISABLED); | |
| 52 } | |
| 53 | |
| 54 class Tab; | |
| 55 | |
| 56 class TabStripObserver { | |
| 57 public: | |
| 58 virtual void OnTabAdded(Tab* added) {} | |
| 59 virtual void OnTabRemoved(Tab* removed) {} | |
| 60 virtual void OnTabSelected(Tab* selected) {} | |
| 61 }; | |
| 62 | |
| 63 class Tab : public views::LabelButton, | |
| 64 public navigation::ViewObserver, | |
| 65 public TabStripObserver { | |
| 66 public: | |
| 67 class Background : public views::Background { | |
| 68 public: | |
| 69 explicit Background(Tab* tab) : tab_(tab) {} | |
| 70 ~Background() override {} | |
| 71 | |
| 72 private: | |
| 73 // views::Background: | |
| 74 void Paint(gfx::Canvas* canvas, views::View* view) const override { | |
| 75 DCHECK_EQ(view, tab_); | |
| 76 SkColor bg = tab_->selected() ? SK_ColorGRAY : SK_ColorLTGRAY; | |
| 77 gfx::Rect lb = view->GetLocalBounds(); | |
| 78 canvas->FillRect(lb, bg); | |
| 79 if (!tab_->selected()) { | |
| 80 lb.set_y(lb.bottom() - 1); | |
| 81 lb.set_height(1); | |
| 82 canvas->FillRect(lb, SK_ColorGRAY); | |
| 83 } | |
| 84 } | |
| 85 | |
| 86 Tab* tab_; | |
| 87 | |
| 88 DISALLOW_COPY_AND_ASSIGN(Background); | |
| 89 }; | |
| 90 | |
| 91 Tab(std::unique_ptr<navigation::View> view, | |
| 92 views::ButtonListener* listener) | |
| 93 : views::LabelButton(listener, base::ASCIIToUTF16("Blank")), | |
| 94 view_(std::move(view)) { | |
| 95 view_->AddObserver(this); | |
| 96 set_background(new Background(this)); | |
| 97 } | |
| 98 ~Tab() override { | |
| 99 view_->RemoveObserver(this); | |
| 100 } | |
| 101 | |
| 102 bool selected() const { return selected_; } | |
| 103 | |
| 104 aura::Window* window() { return window_; } | |
| 105 void SetWindow(aura::Window* window) { | |
| 106 window_ = window; | |
| 107 if (selected_) | |
| 108 window_->Show(); | |
| 109 else | |
| 110 window_->Hide(); | |
| 111 view_->EmbedInWindow(window_); | |
| 112 } | |
| 113 navigation::View* view() { return view_.get(); } | |
| 114 | |
| 115 private: | |
| 116 // views::View: | |
| 117 gfx::Size GetPreferredSize() const override { | |
| 118 gfx::Size ps = views::LabelButton::GetPreferredSize(); | |
| 119 ps.set_width(180); | |
| 120 return ps; | |
| 121 } | |
| 122 | |
| 123 // navigation::ViewObserver: | |
| 124 void NavigationStateChanged(navigation::View* view) override { | |
| 125 if (!view->title().empty()) | |
| 126 SetText(view->title()); | |
| 127 } | |
| 128 | |
| 129 // TabStripObserver: | |
| 130 void OnTabSelected(Tab* selected) override { | |
| 131 selected_ = selected == this; | |
| 132 SetTextColor(views::Button::STATE_NORMAL, | |
| 133 selected_ ? SK_ColorWHITE : SK_ColorBLACK); | |
| 134 SetTextColor(views::Button::STATE_HOVERED, | |
| 135 selected_ ? SK_ColorWHITE : SK_ColorBLACK); | |
| 136 SetTextColor(views::Button::STATE_PRESSED, | |
| 137 selected_ ? SK_ColorWHITE : SK_ColorBLACK); | |
| 138 if (window_) { | |
| 139 if (selected_) | |
| 140 window_->Show(); | |
| 141 else | |
| 142 window_->Hide(); | |
| 143 } | |
| 144 } | |
| 145 | |
| 146 aura::Window* window_ = nullptr; | |
| 147 std::unique_ptr<navigation::View> view_; | |
| 148 bool selected_ = false; | |
| 149 | |
| 150 DISALLOW_COPY_AND_ASSIGN(Tab); | |
| 151 }; | |
| 152 | |
| 153 class TabStrip : public views::View, | |
| 154 public views::ButtonListener { | |
| 155 public: | |
| 156 class Delegate { | |
| 157 public: | |
| 158 virtual void NewTab() = 0; | |
| 159 }; | |
| 160 | |
| 161 explicit TabStrip(Delegate* delegate) | |
| 162 : delegate_(delegate), | |
| 163 tab_container_(new views::View), | |
| 164 new_tab_button_( | |
| 165 new views::LabelButton(this, base::ASCIIToUTF16("+"))) { | |
| 166 views::BoxLayout* layout = | |
| 167 new views::BoxLayout(views::BoxLayout::kHorizontal, 5, 0, 0); | |
| 168 layout->set_main_axis_alignment( | |
| 169 views::BoxLayout::MAIN_AXIS_ALIGNMENT_START); | |
| 170 layout->set_cross_axis_alignment( | |
| 171 views::BoxLayout::CROSS_AXIS_ALIGNMENT_STRETCH); | |
| 172 layout->SetDefaultFlex(0); | |
| 173 SetLayoutManager(layout); | |
| 174 | |
| 175 views::BoxLayout* tab_container_layout = | |
| 176 new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0); | |
| 177 tab_container_layout->set_main_axis_alignment( | |
| 178 views::BoxLayout::MAIN_AXIS_ALIGNMENT_START); | |
| 179 tab_container_layout->set_cross_axis_alignment( | |
| 180 views::BoxLayout::CROSS_AXIS_ALIGNMENT_STRETCH); | |
| 181 tab_container_layout->SetDefaultFlex(0); | |
| 182 tab_container_->SetLayoutManager(tab_container_layout); | |
| 183 AddChildView(tab_container_); | |
| 184 layout->SetFlexForView(tab_container_, 1); | |
| 185 AddChildView(new_tab_button_); | |
| 186 } | |
| 187 ~TabStrip() override { | |
| 188 for (auto* tab : tabs_) | |
| 189 RemoveObserver(tab); | |
| 190 } | |
| 191 | |
| 192 void SetContainerWindow(aura::Window* container) { | |
| 193 DCHECK(!container_); | |
| 194 container_ = container; | |
| 195 for (auto* tab : tabs_) { | |
| 196 aura::Window* window = new aura::Window(nullptr); | |
| 197 window->Init(ui::LAYER_NOT_DRAWN); | |
| 198 container_->AddChild(window); | |
| 199 tab->SetWindow(window); | |
| 200 } | |
| 201 } | |
| 202 | |
| 203 void AddTab(std::unique_ptr<navigation::View> view) { | |
| 204 selected_index_ = static_cast<int>(tabs_.size()); | |
| 205 Tab* tab = new Tab(std::move(view), this); | |
| 206 // We won't have a WindowTree until we're added to a view hierarchy. | |
| 207 if (container_) { | |
| 208 aura::Window* window = new aura::Window(nullptr); | |
| 209 window->Init(ui::LAYER_NOT_DRAWN); | |
| 210 container_->AddChild(window); | |
| 211 tab->SetWindow(window); | |
| 212 } | |
| 213 AddObserver(tab); | |
| 214 tabs_.push_back(tab); | |
| 215 tab_container_->AddChildView(tab); | |
| 216 for (auto& observer : observers_) | |
| 217 observer.OnTabAdded(tab); | |
| 218 SelectTab(tab); | |
| 219 } | |
| 220 | |
| 221 void CloseTabForView(navigation::View* view) { | |
| 222 for (auto it = tabs_.begin(); it != tabs_.end(); ++it) { | |
| 223 Tab* tab = *it; | |
| 224 if (tab->view() == view) { | |
| 225 CloseTab(tab); | |
| 226 break; | |
| 227 } | |
| 228 } | |
| 229 } | |
| 230 | |
| 231 void CloseTab(Tab* tab) { | |
| 232 auto it = std::find(tabs_.begin(), tabs_.end(), tab); | |
| 233 int tab_index = static_cast<int>(it - tabs_.begin()); | |
| 234 if (tab_index < selected_index_) | |
| 235 --selected_index_; | |
| 236 DCHECK(it != tabs_.end()); | |
| 237 tabs_.erase(it); | |
| 238 RemoveObserver(tab); | |
| 239 tab_container_->RemoveChildView(tab); | |
| 240 if (tab->selected()) { | |
| 241 int next_selected_index = selected_index_; | |
| 242 if (selected_index_ == static_cast<int>(tabs_.size())) | |
| 243 --next_selected_index; | |
| 244 if (next_selected_index >= 0) | |
| 245 SelectTab(tabs_[next_selected_index]); | |
| 246 } | |
| 247 Layout(); | |
| 248 for (auto& observer : observers_) | |
| 249 observer.OnTabRemoved(tab); | |
| 250 delete tab; | |
| 251 } | |
| 252 | |
| 253 bool empty() const { return tabs_.empty(); } | |
| 254 | |
| 255 void SelectTab(Tab* tab) { | |
| 256 auto it = std::find(tabs_.begin(), tabs_.end(), tab); | |
| 257 DCHECK(it != tabs_.end()); | |
| 258 selected_index_ = it - tabs_.begin(); | |
| 259 for (auto& observer : observers_) | |
| 260 observer.OnTabSelected(tab); | |
| 261 } | |
| 262 Tab* selected_tab() { | |
| 263 return selected_index_ != -1 ? tabs_[selected_index_] : nullptr; | |
| 264 } | |
| 265 | |
| 266 void AddObserver(TabStripObserver* observer) { | |
| 267 observers_.AddObserver(observer); | |
| 268 } | |
| 269 void RemoveObserver(TabStripObserver* observer) { | |
| 270 observers_.RemoveObserver(observer); | |
| 271 } | |
| 272 | |
| 273 private: | |
| 274 // views::View: | |
| 275 void OnPaint(gfx::Canvas* canvas) override { | |
| 276 gfx::Rect lb = GetLocalBounds(); | |
| 277 lb.set_y(lb.bottom() - 1); | |
| 278 lb.set_height(1); | |
| 279 canvas->FillRect(lb, SK_ColorGRAY); | |
| 280 } | |
| 281 | |
| 282 // views::ButtonListener: | |
| 283 void ButtonPressed(views::Button* sender, const ui::Event& event) override { | |
| 284 auto it = std::find(tabs_.begin(), tabs_.end(), sender); | |
| 285 if (it != tabs_.end()) { | |
| 286 if (event.IsControlDown()) | |
| 287 CloseTab(*it); | |
| 288 else | |
| 289 SelectTab(*it); | |
| 290 } | |
| 291 else if (sender == new_tab_button_ && delegate_) | |
| 292 delegate_->NewTab(); | |
| 293 } | |
| 294 | |
| 295 Delegate* delegate_; | |
| 296 views::View* tab_container_; | |
| 297 views::LabelButton* new_tab_button_; | |
| 298 std::vector<Tab*> tabs_; | |
| 299 int selected_index_ = -1; | |
| 300 base::ObserverList<TabStripObserver> observers_; | |
| 301 aura::Window* container_ = nullptr; | |
| 302 | |
| 303 DISALLOW_COPY_AND_ASSIGN(TabStrip); | |
| 304 }; | |
| 305 | |
| 306 class NavMenuModel : public ui::MenuModel { | |
| 307 public: | |
| 308 class Delegate { | |
| 309 public: | |
| 310 virtual void NavigateToOffset(int offset) = 0; | |
| 311 }; | |
| 312 | |
| 313 NavMenuModel(const std::vector<navigation::NavigationListItem>& entries, | |
| 314 Delegate* delegate) | |
| 315 : navigation_delegate_(delegate), entries_(entries) {} | |
| 316 ~NavMenuModel() override {} | |
| 317 | |
| 318 private: | |
| 319 bool HasIcons() const override { return false; } | |
| 320 int GetItemCount() const override { | |
| 321 return static_cast<int>(entries_.size()); | |
| 322 } | |
| 323 ui::MenuModel::ItemType GetTypeAt(int index) const override { | |
| 324 return ui::MenuModel::TYPE_COMMAND; | |
| 325 } | |
| 326 ui::MenuSeparatorType GetSeparatorTypeAt(int index) const override { | |
| 327 return ui::NORMAL_SEPARATOR; | |
| 328 } | |
| 329 int GetCommandIdAt(int index) const override { | |
| 330 return index; | |
| 331 } | |
| 332 base::string16 GetLabelAt(int index) const override { | |
| 333 return entries_[index].title; | |
| 334 } | |
| 335 base::string16 GetSublabelAt(int index) const override { | |
| 336 return base::string16(); | |
| 337 } | |
| 338 base::string16 GetMinorTextAt(int index) const override { | |
| 339 return base::string16(); | |
| 340 } | |
| 341 bool IsItemDynamicAt(int index) const override { return false; } | |
| 342 bool GetAcceleratorAt(int index, | |
| 343 ui::Accelerator* accelerator) const override { | |
| 344 return false; | |
| 345 } | |
| 346 bool IsItemCheckedAt(int index) const override { return false; } | |
| 347 int GetGroupIdAt(int index) const override { return -1; } | |
| 348 bool GetIconAt(int index, gfx::Image* icon) override { return false; } | |
| 349 ui::ButtonMenuItemModel* GetButtonMenuItemAt(int index) const override { | |
| 350 return nullptr; | |
| 351 } | |
| 352 bool IsEnabledAt(int index) const override { return true; } | |
| 353 bool IsVisibleAt(int index) const override { return true; } | |
| 354 ui::MenuModel* GetSubmenuModelAt(int index) const override { return nullptr; } | |
| 355 void HighlightChangedTo(int index) override {} | |
| 356 void ActivatedAt(int index) override { | |
| 357 ActivatedAt(index, 0); | |
| 358 } | |
| 359 void ActivatedAt(int index, int event_flags) override { | |
| 360 navigation_delegate_->NavigateToOffset(entries_[index].offset); | |
| 361 } | |
| 362 void SetMenuModelDelegate(ui::MenuModelDelegate* delegate) override { | |
| 363 delegate_ = delegate; | |
| 364 } | |
| 365 ui::MenuModelDelegate* GetMenuModelDelegate() const override { | |
| 366 return delegate_; | |
| 367 } | |
| 368 | |
| 369 ui::MenuModelDelegate* delegate_ = nullptr; | |
| 370 Delegate* navigation_delegate_; | |
| 371 std::vector<navigation::NavigationListItem> entries_; | |
| 372 | |
| 373 DISALLOW_COPY_AND_ASSIGN(NavMenuModel); | |
| 374 }; | |
| 375 | |
| 376 class NavButton : public views::LabelButton { | |
| 377 public: | |
| 378 enum class Type { | |
| 379 BACK, | |
| 380 FORWARD | |
| 381 }; | |
| 382 | |
| 383 class ModelProvider { | |
| 384 public: | |
| 385 virtual std::unique_ptr<ui::MenuModel> CreateMenuModel(Type type) = 0; | |
| 386 }; | |
| 387 | |
| 388 NavButton(Type type, | |
| 389 ModelProvider* model_provider, | |
| 390 views::ButtonListener* listener, | |
| 391 const base::string16& label) | |
| 392 : views::LabelButton(listener, label), | |
| 393 type_(type), | |
| 394 model_provider_(model_provider), | |
| 395 show_menu_factory_(this) {} | |
| 396 ~NavButton() override {} | |
| 397 | |
| 398 private: | |
| 399 // views::LabelButton overrides: | |
| 400 bool OnMousePressed(const ui::MouseEvent& event) override { | |
| 401 if (IsTriggerableEvent(event) && enabled() && | |
| 402 HitTestPoint(event.location())) { | |
| 403 y_pos_on_lbuttondown_ = event.y(); | |
| 404 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( | |
| 405 FROM_HERE, | |
| 406 base::Bind(&NavButton::ShowMenu, show_menu_factory_.GetWeakPtr(), | |
| 407 ui::GetMenuSourceTypeForEvent(event)), | |
| 408 base::TimeDelta::FromMilliseconds(500)); | |
| 409 } | |
| 410 return LabelButton::OnMousePressed(event); | |
| 411 } | |
| 412 bool OnMouseDragged(const ui::MouseEvent& event) override { | |
| 413 bool result = LabelButton::OnMouseDragged(event); | |
| 414 if (show_menu_factory_.HasWeakPtrs()) { | |
| 415 if (event.y() > y_pos_on_lbuttondown_ + GetHorizontalDragThreshold()) { | |
| 416 show_menu_factory_.InvalidateWeakPtrs(); | |
| 417 ShowMenu(ui::GetMenuSourceTypeForEvent(event)); | |
| 418 } | |
| 419 } | |
| 420 return result; | |
| 421 } | |
| 422 void OnMouseReleased(const ui::MouseEvent& event) override { | |
| 423 if (IsTriggerableEvent(event)) | |
| 424 show_menu_factory_.InvalidateWeakPtrs(); | |
| 425 LabelButton::OnMouseReleased(event); | |
| 426 } | |
| 427 | |
| 428 void ShowMenu(ui::MenuSourceType source_type) { | |
| 429 gfx::Rect local = GetLocalBounds(); | |
| 430 gfx::Point menu_position(local.origin()); | |
| 431 menu_position.Offset(0, local.height() - 1); | |
| 432 View::ConvertPointToScreen(this, &menu_position); | |
| 433 | |
| 434 model_ = model_provider_->CreateMenuModel(type_); | |
| 435 menu_model_adapter_.reset(new views::MenuModelAdapter( | |
| 436 model_.get(), | |
| 437 base::Bind(&NavButton::OnMenuClosed, base::Unretained(this)))); | |
| 438 menu_model_adapter_->set_triggerable_event_flags(triggerable_event_flags()); | |
| 439 menu_runner_.reset(new views::MenuRunner(menu_model_adapter_->CreateMenu(), | |
| 440 views::MenuRunner::HAS_MNEMONICS)); | |
| 441 menu_runner_->RunMenuAt(GetWidget(), nullptr, | |
| 442 gfx::Rect(menu_position, gfx::Size(0, 0)), | |
| 443 views::MENU_ANCHOR_TOPLEFT, source_type); | |
| 444 } | |
| 445 | |
| 446 void OnMenuClosed() { | |
| 447 SetMouseHandler(nullptr); | |
| 448 model_.reset(); | |
| 449 menu_runner_.reset(); | |
| 450 menu_model_adapter_.reset(); | |
| 451 } | |
| 452 | |
| 453 Type type_; | |
| 454 ModelProvider* model_provider_; | |
| 455 int y_pos_on_lbuttondown_ = 0; | |
| 456 std::unique_ptr<ui::MenuModel> model_; | |
| 457 std::unique_ptr<views::MenuModelAdapter> menu_model_adapter_; | |
| 458 std::unique_ptr<views::MenuRunner> menu_runner_; | |
| 459 base::WeakPtrFactory<NavButton> show_menu_factory_; | |
| 460 | |
| 461 DISALLOW_COPY_AND_ASSIGN(NavButton); | |
| 462 }; | |
| 463 | |
| 464 class ProgressBar : public views::View { | |
| 465 public: | |
| 466 ProgressBar() {} | |
| 467 ~ProgressBar() override {} | |
| 468 | |
| 469 void SetProgress(double progress) { | |
| 470 progress_ = progress; | |
| 471 SchedulePaint(); | |
| 472 } | |
| 473 | |
| 474 private: | |
| 475 void OnPaint(gfx::Canvas* canvas) override { | |
| 476 gfx::Rect stroke_rect = GetLocalBounds(); | |
| 477 stroke_rect.set_y(stroke_rect.bottom() - 1); | |
| 478 stroke_rect.set_height(1); | |
| 479 canvas->FillRect(stroke_rect, SK_ColorGRAY); | |
| 480 if (progress_ != 0.f) { | |
| 481 gfx::Rect progress_rect = GetLocalBounds(); | |
| 482 progress_rect.set_width(progress_rect.width() * progress_); | |
| 483 canvas->FillRect(progress_rect, SK_ColorRED); | |
| 484 } | |
| 485 } | |
| 486 | |
| 487 double progress_ = 0.f; | |
| 488 | |
| 489 DISALLOW_COPY_AND_ASSIGN(ProgressBar); | |
| 490 }; | |
| 491 | |
| 492 class Throbber : public views::View { | |
| 493 public: | |
| 494 Throbber() : timer_(false, true), weak_factory_(this) {} | |
| 495 ~Throbber() override {} | |
| 496 | |
| 497 void Start() { | |
| 498 throbbing_ = true; | |
| 499 start_time_ = base::TimeTicks::Now(); | |
| 500 SchedulePaint(); | |
| 501 timer_.Start( | |
| 502 FROM_HERE, base::TimeDelta::FromMilliseconds(30), | |
| 503 base::Bind(&Throbber::SchedulePaint, weak_factory_.GetWeakPtr())); | |
| 504 } | |
| 505 | |
| 506 void Stop() { | |
| 507 throbbing_ = false; | |
| 508 if (timer_.IsRunning()) | |
| 509 timer_.Stop(); | |
| 510 SchedulePaint(); | |
| 511 } | |
| 512 | |
| 513 private: | |
| 514 void OnPaint(gfx::Canvas* canvas) override { | |
| 515 if (!throbbing_) | |
| 516 return; | |
| 517 | |
| 518 gfx::PaintThrobberSpinning( | |
| 519 canvas, GetLocalBounds(), | |
| 520 GetNativeTheme()->GetSystemColor( | |
| 521 ui::NativeTheme::kColorId_ThrobberSpinningColor), | |
| 522 base::TimeTicks::Now() - start_time_); | |
| 523 } | |
| 524 | |
| 525 bool throbbing_ = false; | |
| 526 base::TimeTicks start_time_; | |
| 527 base::Timer timer_; | |
| 528 base::WeakPtrFactory<Throbber> weak_factory_; | |
| 529 | |
| 530 DISALLOW_COPY_AND_ASSIGN(Throbber); | |
| 531 }; | |
| 532 | |
| 533 class UI : public views::WidgetDelegateView, | |
| 534 public views::ButtonListener, | |
| 535 public views::TextfieldController, | |
| 536 public TabStrip::Delegate, | |
| 537 public TabStripObserver, | |
| 538 public navigation::ViewDelegate, | |
| 539 public navigation::ViewObserver, | |
| 540 public NavButton::ModelProvider, | |
| 541 public NavMenuModel::Delegate { | |
| 542 public: | |
| 543 enum class Type { WINDOW, POPUP }; | |
| 544 | |
| 545 UI(Browser* browser, Type type, std::unique_ptr<navigation::View> view) | |
| 546 : browser_(browser), | |
| 547 type_(type), | |
| 548 tab_strip_(new TabStrip(this)), | |
| 549 back_button_(new NavButton(NavButton::Type::BACK, this, this, | |
| 550 base::ASCIIToUTF16("Back"))), | |
| 551 forward_button_(new NavButton(NavButton::Type::FORWARD, this, this, | |
| 552 base::ASCIIToUTF16("Forward"))), | |
| 553 reload_button_( | |
| 554 new views::LabelButton(this, base::ASCIIToUTF16("Reload"))), | |
| 555 prompt_(new views::Textfield), | |
| 556 debug_button_(new views::LabelButton(this, base::ASCIIToUTF16("DV"))), | |
| 557 throbber_(new Throbber), | |
| 558 progress_bar_(new ProgressBar), | |
| 559 debug_view_(new DebugView) { | |
| 560 set_background(views::Background::CreateStandardPanelBackground()); | |
| 561 prompt_->set_controller(this); | |
| 562 back_button_->set_request_focus_on_press(false); | |
| 563 forward_button_->set_request_focus_on_press(false); | |
| 564 reload_button_->set_request_focus_on_press(false); | |
| 565 AddChildView(tab_strip_); | |
| 566 AddChildView(back_button_); | |
| 567 AddChildView(forward_button_); | |
| 568 AddChildView(reload_button_); | |
| 569 AddChildView(prompt_); | |
| 570 AddChildView(debug_button_); | |
| 571 AddChildView(throbber_); | |
| 572 AddChildView(progress_bar_); | |
| 573 AddChildView(debug_view_); | |
| 574 | |
| 575 tab_strip_->AddObserver(this); | |
| 576 tab_strip_->AddTab(std::move(view)); | |
| 577 } | |
| 578 ~UI() override { | |
| 579 browser_->RemoveWindow(GetWidget()); | |
| 580 } | |
| 581 | |
| 582 void NavigateTo(const GURL& url) { | |
| 583 selected_view()->NavigateToURL(url); | |
| 584 } | |
| 585 | |
| 586 private: | |
| 587 // Overridden from views::WidgetDelegate: | |
| 588 base::string16 GetWindowTitle() const override { | |
| 589 // TODO(beng): use resources. | |
| 590 if (selected_view()->title().empty()) | |
| 591 return base::ASCIIToUTF16("Browser"); | |
| 592 base::string16 format = base::ASCIIToUTF16("%s - Browser"); | |
| 593 base::ReplaceFirstSubstringAfterOffset(&format, 0, base::ASCIIToUTF16("%s"), | |
| 594 selected_view()->title()); | |
| 595 return format; | |
| 596 } | |
| 597 bool CanResize() const override { return true; } | |
| 598 bool CanMaximize() const override { return true; } | |
| 599 bool CanMinimize() const override { return true; } | |
| 600 | |
| 601 // views::ButtonListener: | |
| 602 void ButtonPressed(views::Button* sender, const ui::Event& event) override { | |
| 603 if (sender == back_button_) { | |
| 604 selected_view()->GoBack(); | |
| 605 } else if (sender == forward_button_) { | |
| 606 selected_view()->GoForward(); | |
| 607 } else if (sender == reload_button_) { | |
| 608 if (selected_view()->is_loading()) | |
| 609 selected_view()->Stop(); | |
| 610 else | |
| 611 selected_view()->Reload(false); | |
| 612 } else if (sender == debug_button_) { | |
| 613 ToggleDebugView(); | |
| 614 } | |
| 615 } | |
| 616 | |
| 617 // Overridden from views::View: | |
| 618 void Layout() override { | |
| 619 gfx::Rect local_bounds = GetLocalBounds(); | |
| 620 | |
| 621 gfx::Size ps = tab_strip_->GetPreferredSize(); | |
| 622 tab_strip_->SetBoundsRect( | |
| 623 gfx::Rect(0, 5, local_bounds.width(), ps.height())); | |
| 624 | |
| 625 gfx::Rect bounds = local_bounds; | |
| 626 bounds.set_y(tab_strip_->bounds().bottom()); | |
| 627 bounds.Inset(5, 5); | |
| 628 | |
| 629 ps = back_button_->GetPreferredSize(); | |
| 630 back_button_->SetBoundsRect( | |
| 631 gfx::Rect(bounds.x(), bounds.y(), ps.width(), ps.height())); | |
| 632 ps = forward_button_->GetPreferredSize(); | |
| 633 forward_button_->SetBoundsRect(gfx::Rect(back_button_->bounds().right() + 5, | |
| 634 bounds.y(), ps.width(), | |
| 635 ps.height())); | |
| 636 ps = reload_button_->GetPreferredSize(); | |
| 637 reload_button_->SetBoundsRect( | |
| 638 gfx::Rect(forward_button_->bounds().right() + 5, bounds.y(), ps.width(), | |
| 639 ps.height())); | |
| 640 | |
| 641 ps = prompt_->GetPreferredSize(); | |
| 642 int throbber_size = ps.height(); | |
| 643 gfx::Size debug_ps = debug_button_->GetPreferredSize(); | |
| 644 int prompt_y = | |
| 645 bounds.y() + (reload_button_->bounds().height() - ps.height()) / 2; | |
| 646 int width = | |
| 647 bounds.width() - reload_button_->bounds().right() - throbber_size - 15 - | |
| 648 debug_ps.width(); | |
| 649 prompt_->SetBoundsRect(gfx::Rect(reload_button_->bounds().right() + 5, | |
| 650 prompt_y, width, ps.height())); | |
| 651 | |
| 652 debug_button_->SetBoundsRect( | |
| 653 gfx::Rect(prompt_->bounds().right() + 5, | |
| 654 prompt_->bounds().y(), debug_ps.width(), debug_ps.height())); | |
| 655 | |
| 656 throbber_->SetBoundsRect(gfx::Rect(debug_button_->bounds().right() + 5, | |
| 657 prompt_->bounds().y(), throbber_size, | |
| 658 throbber_size)); | |
| 659 | |
| 660 gfx::Rect progress_bar_rect(local_bounds.x(), | |
| 661 back_button_->bounds().bottom() + 5, | |
| 662 local_bounds.width(), 2); | |
| 663 progress_bar_->SetBoundsRect(progress_bar_rect); | |
| 664 | |
| 665 int debug_view_height = 0; | |
| 666 if (showing_debug_view_) | |
| 667 debug_view_height = debug_view_->GetPreferredSize().height(); | |
| 668 debug_view_->SetBoundsRect( | |
| 669 gfx::Rect(local_bounds.x(), local_bounds.height() - debug_view_height, | |
| 670 local_bounds.width(), debug_view_height)); | |
| 671 | |
| 672 if (content_area_) { | |
| 673 int x = local_bounds.x(); | |
| 674 int y = type_ == Type::POPUP ? 0 : progress_bar_->bounds().bottom(); | |
| 675 gfx::Point offset(x, y); | |
| 676 ConvertPointToWidget(this, &offset); | |
| 677 int width = local_bounds.width(); | |
| 678 int height = local_bounds.height() - y - debug_view_height; | |
| 679 content_area_->SetBounds( | |
| 680 gfx::Rect(offset.x(), offset.y(), width, height)); | |
| 681 for (auto* child : content_area_->children()) | |
| 682 child->SetBounds(gfx::Rect(0, 0, width, height)); | |
| 683 } | |
| 684 } | |
| 685 void ViewHierarchyChanged( | |
| 686 const views::View::ViewHierarchyChangedDetails& details) override { | |
| 687 if (details.is_add && GetWidget() && !content_area_) { | |
| 688 aura::Window* window = GetWidget()->GetNativeWindow(); | |
| 689 content_area_ = new aura::Window(nullptr); | |
| 690 content_area_->Init(ui::LAYER_NOT_DRAWN); | |
| 691 content_area_->Show(); | |
| 692 window->AddChild(content_area_); | |
| 693 tab_strip_->SetContainerWindow(content_area_); | |
| 694 } | |
| 695 } | |
| 696 | |
| 697 // Overridden from views::TextFieldController: | |
| 698 bool HandleKeyEvent(views::Textfield* sender, | |
| 699 const ui::KeyEvent& key_event) override { | |
| 700 if (key_event.type() == ui::ET_KEY_PRESSED && | |
| 701 key_event.key_code() == ui::VKEY_RETURN) | |
| 702 selected_view()->NavigateToURL(GURL(prompt_->text())); | |
| 703 return false; | |
| 704 } | |
| 705 | |
| 706 // TabStrip::Delegate: | |
| 707 void NewTab() override { | |
| 708 tab_strip_->AddTab(browser_->CreateView()); | |
| 709 tab_strip_->selected_tab()->view()->NavigateToURL(GURL("about:blank")); | |
| 710 } | |
| 711 | |
| 712 // TabStripObserver: | |
| 713 void OnTabAdded(Tab* added) override { | |
| 714 added->view()->AddObserver(this); | |
| 715 added->view()->set_delegate(this); | |
| 716 } | |
| 717 void OnTabSelected(Tab* selected) override { | |
| 718 debug_view_->set_view(selected->view()); | |
| 719 prompt_->SetText(base::UTF8ToUTF16(selected->view()->url().spec())); | |
| 720 if (GetWidget()) | |
| 721 GetWidget()->UpdateWindowTitle(); | |
| 722 } | |
| 723 | |
| 724 // navigation::ViewDelegate: | |
| 725 void ViewCreated(navigation::View* source, | |
| 726 std::unique_ptr<navigation::View> view, | |
| 727 bool is_popup, | |
| 728 const gfx::Rect& initial_rect, | |
| 729 bool user_gesture) override { | |
| 730 if (is_popup) | |
| 731 CreateNewWindow(std::move(view), initial_rect, is_popup); | |
| 732 else | |
| 733 tab_strip_->AddTab(std::move(view)); | |
| 734 } | |
| 735 void Close(navigation::View* source) override { | |
| 736 tab_strip_->CloseTabForView(source); | |
| 737 if (tab_strip_->empty()) | |
| 738 GetWidget()->Close(); | |
| 739 } | |
| 740 void OpenURL(navigation::View* source, | |
| 741 navigation::mojom::OpenURLParamsPtr params) override { | |
| 742 switch (params->disposition) { | |
| 743 case navigation::mojom::WindowOpenDisposition::CURRENT_TAB: | |
| 744 selected_view()->NavigateToURL(params->url); | |
| 745 break; | |
| 746 case navigation::mojom::WindowOpenDisposition::NEW_FOREGROUND_TAB: | |
| 747 tab_strip_->AddTab(browser_->CreateView()); | |
| 748 tab_strip_->selected_tab()->view()->NavigateToURL(params->url); | |
| 749 break; | |
| 750 case navigation::mojom::WindowOpenDisposition::NEW_POPUP: | |
| 751 case navigation::mojom::WindowOpenDisposition::NEW_WINDOW: { | |
| 752 std::unique_ptr<navigation::View> view = browser_->CreateView(); | |
| 753 view->NavigateToURL(params->url); | |
| 754 CreateNewWindow( | |
| 755 std::move(view), gfx::Rect(), | |
| 756 params->disposition == | |
| 757 navigation::mojom::WindowOpenDisposition::NEW_POPUP); | |
| 758 break; | |
| 759 } | |
| 760 default: | |
| 761 break; | |
| 762 } | |
| 763 } | |
| 764 | |
| 765 // navigation::ViewObserver: | |
| 766 void LoadingStateChanged(navigation::View* view) override { | |
| 767 if (view->is_loading()) { | |
| 768 reload_button_->SetText(base::ASCIIToUTF16("Stop")); | |
| 769 throbber_->Start(); | |
| 770 } else { | |
| 771 reload_button_->SetText(base::ASCIIToUTF16("Reload")); | |
| 772 throbber_->Stop(); | |
| 773 progress_bar_->SetProgress(0.f); | |
| 774 } | |
| 775 } | |
| 776 void LoadProgressChanged(navigation::View* view, double progress) override { | |
| 777 progress_bar_->SetProgress(progress); | |
| 778 } | |
| 779 void NavigationStateChanged(navigation::View* view) override { | |
| 780 EnableButton(back_button_, view->can_go_back()); | |
| 781 EnableButton(forward_button_, view->can_go_forward()); | |
| 782 prompt_->SetText(base::UTF8ToUTF16(view->url().spec())); | |
| 783 GetWidget()->UpdateWindowTitle(); | |
| 784 } | |
| 785 void HoverTargetURLChanged(navigation::View* view, const GURL& url) override { | |
| 786 if (url.is_valid()) | |
| 787 prompt_->SetText(base::UTF8ToUTF16(url.spec())); | |
| 788 else | |
| 789 prompt_->SetText(base::UTF8ToUTF16(selected_view()->url().spec())); | |
| 790 } | |
| 791 | |
| 792 // NavButton::ModelProvider: | |
| 793 std::unique_ptr<ui::MenuModel> CreateMenuModel( | |
| 794 NavButton::Type type) override { | |
| 795 std::vector<navigation::NavigationListItem> entries; | |
| 796 if (type == NavButton::Type::BACK) { | |
| 797 selected_view()->GetBackMenuItems(&entries); | |
| 798 } else { | |
| 799 selected_view()->GetForwardMenuItems(&entries); | |
| 800 } | |
| 801 return base::MakeUnique<NavMenuModel>(entries, this); | |
| 802 } | |
| 803 | |
| 804 // NavMenuModel::Delegate: | |
| 805 void NavigateToOffset(int offset) override { | |
| 806 selected_view()->NavigateToOffset(offset); | |
| 807 } | |
| 808 | |
| 809 navigation::View* selected_view() { | |
| 810 return const_cast<navigation::View*>( | |
| 811 static_cast<const UI*>(this)->selected_view()); | |
| 812 } | |
| 813 const navigation::View* selected_view() const { | |
| 814 return tab_strip_->selected_tab()->view(); | |
| 815 } | |
| 816 | |
| 817 void CreateNewWindow(std::unique_ptr<navigation::View> view, | |
| 818 const gfx::Rect& initial_bounds, | |
| 819 bool is_popup) { | |
| 820 gfx::Rect bounds = initial_bounds; | |
| 821 if (bounds.IsEmpty()) | |
| 822 bounds = gfx::Rect(10, 10, 400, 300); | |
| 823 views::Widget* window = views::Widget::CreateWindowWithContextAndBounds( | |
| 824 new UI(browser_, is_popup ? UI::Type::POPUP : UI::Type::WINDOW, | |
| 825 std::move(view)), | |
| 826 nullptr, bounds); | |
| 827 window->Show(); | |
| 828 browser_->AddWindow(window); | |
| 829 } | |
| 830 | |
| 831 void ToggleDebugView() { | |
| 832 showing_debug_view_ = !showing_debug_view_; | |
| 833 Layout(); | |
| 834 } | |
| 835 | |
| 836 Browser* browser_; | |
| 837 | |
| 838 Type type_; | |
| 839 | |
| 840 TabStrip* tab_strip_; | |
| 841 views::LabelButton* back_button_; | |
| 842 views::LabelButton* forward_button_; | |
| 843 views::LabelButton* reload_button_; | |
| 844 views::Textfield* prompt_; | |
| 845 views::LabelButton* debug_button_; | |
| 846 Throbber* throbber_; | |
| 847 ProgressBar* progress_bar_; | |
| 848 | |
| 849 aura::Window* content_area_ = nullptr; | |
| 850 | |
| 851 DebugView* debug_view_; | |
| 852 bool showing_debug_view_ = false; | |
| 853 | |
| 854 DISALLOW_COPY_AND_ASSIGN(UI); | |
| 855 }; | |
| 856 | |
| 857 Browser::Browser() { | |
| 858 registry_.AddInterface<mojom::Launchable>( | |
| 859 base::Bind(&Browser::Create, base::Unretained(this))); | |
| 860 } | |
| 861 Browser::~Browser() {} | |
| 862 | |
| 863 void Browser::AddWindow(views::Widget* window) { | |
| 864 windows_.push_back(window); | |
| 865 } | |
| 866 | |
| 867 void Browser::RemoveWindow(views::Widget* window) { | |
| 868 auto it = std::find(windows_.begin(), windows_.end(), window); | |
| 869 DCHECK(it != windows_.end()); | |
| 870 windows_.erase(it); | |
| 871 if (windows_.empty()) | |
| 872 base::MessageLoop::current()->QuitWhenIdle(); | |
| 873 } | |
| 874 | |
| 875 std::unique_ptr<navigation::View> Browser::CreateView() { | |
| 876 navigation::mojom::ViewFactoryPtr factory; | |
| 877 context()->connector()->BindInterface(content::mojom::kBrowserServiceName, | |
| 878 &factory); | |
| 879 return base::MakeUnique<navigation::View>(std::move(factory)); | |
| 880 } | |
| 881 | |
| 882 void Browser::OnStart() { | |
| 883 aura_init_ = base::MakeUnique<views::AuraInit>( | |
| 884 context()->connector(), context()->identity(), "views_mus_resources.pak", | |
| 885 std::string(), nullptr, views::AuraInit::Mode::AURA_MUS); | |
| 886 } | |
| 887 | |
| 888 void Browser::OnBindInterface( | |
| 889 const service_manager::BindSourceInfo& source_info, | |
| 890 const std::string& interface_name, | |
| 891 mojo::ScopedMessagePipeHandle interface_pipe) { | |
| 892 registry_.BindInterface(source_info, interface_name, | |
| 893 std::move(interface_pipe)); | |
| 894 } | |
| 895 | |
| 896 void Browser::Launch(uint32_t what, mojom::LaunchMode how) { | |
| 897 bool reuse = | |
| 898 how == mojom::LaunchMode::REUSE || how == mojom::LaunchMode::DEFAULT; | |
| 899 if (reuse && !windows_.empty()) { | |
| 900 windows_.back()->Activate(); | |
| 901 return; | |
| 902 } | |
| 903 | |
| 904 UI* ui = new UI(this, UI::Type::WINDOW, CreateView()); | |
| 905 views::Widget* window = views::Widget::CreateWindowWithContextAndBounds( | |
| 906 ui, nullptr, gfx::Rect(10, 10, 1024, 600)); | |
| 907 ui->NavigateTo(GURL("http://www.google.com/")); | |
| 908 window->Show(); | |
| 909 AddWindow(window); | |
| 910 } | |
| 911 | |
| 912 void Browser::Create(const service_manager::BindSourceInfo& source_info, | |
| 913 mojom::LaunchableRequest request) { | |
| 914 bindings_.AddBinding(this, std::move(request)); | |
| 915 } | |
| 916 | |
| 917 } // namespace browser | |
| 918 } // namespace mash | |
| OLD | NEW |