// Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "mash/browser/browser.h" #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/memory/weak_ptr.h" #include "base/message_loop/message_loop.h" #include "base/strings/string16.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/threading/thread_task_runner_handle.h" #include "base/timer/timer.h" #include "content/public/common/service_names.mojom.h" #include "mash/browser/debug_view.h" #include "mash/public/interfaces/launchable.mojom.h" #include "services/navigation/public/cpp/view.h" #include "services/navigation/public/cpp/view_delegate.h" #include "services/navigation/public/cpp/view_observer.h" #include "services/navigation/public/interfaces/view.mojom.h" #include "services/service_manager/public/c/main.h" #include "services/service_manager/public/cpp/connector.h" #include "services/service_manager/public/cpp/interface_registry.h" #include "services/service_manager/public/cpp/service.h" #include "services/service_manager/public/cpp/service_context.h" #include "services/service_manager/public/cpp/service_runner.h" #include "services/tracing/public/cpp/provider.h" #include "ui/aura/window.h" #include "ui/base/models/menu_model.h" #include "ui/gfx/canvas.h" #include "ui/gfx/paint_throbber.h" #include "ui/gfx/text_constants.h" #include "ui/native_theme/native_theme.h" #include "ui/views/background.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/menu/menu_model_adapter.h" #include "ui/views/controls/menu/menu_runner.h" #include "ui/views/controls/textfield/textfield.h" #include "ui/views/controls/textfield/textfield_controller.h" #include "ui/views/layout/box_layout.h" #include "ui/views/mus/aura_init.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" #include "url/gurl.h" namespace mash { namespace browser { void EnableButton(views::CustomButton* button, bool enabled) { button->SetState(enabled ? views::Button::STATE_NORMAL : views::Button::STATE_DISABLED); } class Tab; class TabStripObserver { public: virtual void OnTabAdded(Tab* added) {} virtual void OnTabRemoved(Tab* removed) {} virtual void OnTabSelected(Tab* selected) {} }; class Tab : public views::LabelButton, public navigation::ViewObserver, public TabStripObserver { public: class Background : public views::Background { public: explicit Background(Tab* tab) : tab_(tab) {} ~Background() override {} private: // views::Background: void Paint(gfx::Canvas* canvas, views::View* view) const override { DCHECK_EQ(view, tab_); SkColor bg = tab_->selected() ? SK_ColorGRAY : SK_ColorLTGRAY; gfx::Rect lb = view->GetLocalBounds(); canvas->FillRect(lb, bg); if (!tab_->selected()) { lb.set_y(lb.bottom() - 1); lb.set_height(1); canvas->FillRect(lb, SK_ColorGRAY); } } Tab* tab_; DISALLOW_COPY_AND_ASSIGN(Background); }; Tab(std::unique_ptr view, views::ButtonListener* listener) : views::LabelButton(listener, base::ASCIIToUTF16("Blank")), view_(std::move(view)) { view_->AddObserver(this); set_background(new Background(this)); } ~Tab() override { view_->RemoveObserver(this); } bool selected() const { return selected_; } aura::Window* window() { return window_; } void SetWindow(aura::Window* window) { window_ = window; if (selected_) window_->Show(); else window_->Hide(); view_->EmbedInWindow(window_); } navigation::View* view() { return view_.get(); } private: // views::View: gfx::Size GetPreferredSize() const override { gfx::Size ps = views::LabelButton::GetPreferredSize(); ps.set_width(180); return ps; } // navigation::ViewObserver: void NavigationStateChanged(navigation::View* view) override { if (!view->title().empty()) SetText(view->title()); } // TabStripObserver: void OnTabSelected(Tab* selected) override { selected_ = selected == this; SetTextColor(views::Button::STATE_NORMAL, selected_ ? SK_ColorWHITE : SK_ColorBLACK); SetTextColor(views::Button::STATE_HOVERED, selected_ ? SK_ColorWHITE : SK_ColorBLACK); SetTextColor(views::Button::STATE_PRESSED, selected_ ? SK_ColorWHITE : SK_ColorBLACK); if (window_) { if (selected_) window_->Show(); else window_->Hide(); } } aura::Window* window_ = nullptr; std::unique_ptr view_; bool selected_ = false; DISALLOW_COPY_AND_ASSIGN(Tab); }; class TabStrip : public views::View, public views::ButtonListener { public: class Delegate { public: virtual void NewTab() = 0; }; explicit TabStrip(Delegate* delegate) : delegate_(delegate), tab_container_(new views::View), new_tab_button_( new views::LabelButton(this, base::ASCIIToUTF16("+"))) { views::BoxLayout* layout = new views::BoxLayout(views::BoxLayout::kHorizontal, 5, 0, 0); layout->set_main_axis_alignment( views::BoxLayout::MAIN_AXIS_ALIGNMENT_START); layout->set_cross_axis_alignment( views::BoxLayout::CROSS_AXIS_ALIGNMENT_STRETCH); layout->SetDefaultFlex(0); SetLayoutManager(layout); views::BoxLayout* tab_container_layout = new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0); tab_container_layout->set_main_axis_alignment( views::BoxLayout::MAIN_AXIS_ALIGNMENT_START); tab_container_layout->set_cross_axis_alignment( views::BoxLayout::CROSS_AXIS_ALIGNMENT_STRETCH); tab_container_layout->SetDefaultFlex(0); tab_container_->SetLayoutManager(tab_container_layout); AddChildView(tab_container_); layout->SetFlexForView(tab_container_, 1); AddChildView(new_tab_button_); } ~TabStrip() override { for (auto* tab : tabs_) RemoveObserver(tab); } void SetContainerWindow(aura::Window* container) { DCHECK(!container_); container_ = container; for (auto* tab : tabs_) { aura::Window* window = new aura::Window(nullptr); window->Init(ui::LAYER_NOT_DRAWN); container_->AddChild(window); tab->SetWindow(window); } } void AddTab(std::unique_ptr view) { selected_index_ = static_cast(tabs_.size()); Tab* tab = new Tab(std::move(view), this); // We won't have a WindowTree until we're added to a view hierarchy. if (container_) { aura::Window* window = new aura::Window(nullptr); window->Init(ui::LAYER_NOT_DRAWN); container_->AddChild(window); tab->SetWindow(window); } AddObserver(tab); tabs_.push_back(tab); tab_container_->AddChildView(tab); for (auto& observer : observers_) observer.OnTabAdded(tab); SelectTab(tab); } void CloseTabForView(navigation::View* view) { for (auto it = tabs_.begin(); it != tabs_.end(); ++it) { Tab* tab = *it; if (tab->view() == view) { CloseTab(tab); break; } } } void CloseTab(Tab* tab) { auto it = std::find(tabs_.begin(), tabs_.end(), tab); int tab_index = static_cast(it - tabs_.begin()); if (tab_index < selected_index_) --selected_index_; DCHECK(it != tabs_.end()); tabs_.erase(it); RemoveObserver(tab); tab_container_->RemoveChildView(tab); if (tab->selected()) { int next_selected_index = selected_index_; if (selected_index_ == static_cast(tabs_.size())) --next_selected_index; if (next_selected_index >= 0) SelectTab(tabs_[next_selected_index]); } Layout(); for (auto& observer : observers_) observer.OnTabRemoved(tab); delete tab; } bool empty() const { return tabs_.empty(); } void SelectTab(Tab* tab) { auto it = std::find(tabs_.begin(), tabs_.end(), tab); DCHECK(it != tabs_.end()); selected_index_ = it - tabs_.begin(); for (auto& observer : observers_) observer.OnTabSelected(tab); } Tab* selected_tab() { return selected_index_ != -1 ? tabs_[selected_index_] : nullptr; } void AddObserver(TabStripObserver* observer) { observers_.AddObserver(observer); } void RemoveObserver(TabStripObserver* observer) { observers_.RemoveObserver(observer); } private: // views::View: void OnPaint(gfx::Canvas* canvas) override { gfx::Rect lb = GetLocalBounds(); lb.set_y(lb.bottom() - 1); lb.set_height(1); canvas->FillRect(lb, SK_ColorGRAY); } // views::ButtonListener: void ButtonPressed(views::Button* sender, const ui::Event& event) override { auto it = std::find(tabs_.begin(), tabs_.end(), sender); if (it != tabs_.end()) { if (event.IsControlDown()) CloseTab(*it); else SelectTab(*it); } else if (sender == new_tab_button_ && delegate_) delegate_->NewTab(); } Delegate* delegate_; views::View* tab_container_; views::LabelButton* new_tab_button_; std::vector tabs_; int selected_index_ = -1; base::ObserverList observers_; aura::Window* container_ = nullptr; DISALLOW_COPY_AND_ASSIGN(TabStrip); }; class NavMenuModel : public ui::MenuModel { public: class Delegate { public: virtual void NavigateToOffset(int offset) = 0; }; NavMenuModel(const std::vector& entries, Delegate* delegate) : navigation_delegate_(delegate), entries_(entries) {} ~NavMenuModel() override {} private: bool HasIcons() const override { return false; } int GetItemCount() const override { return static_cast(entries_.size()); } ui::MenuModel::ItemType GetTypeAt(int index) const override { return ui::MenuModel::TYPE_COMMAND; } ui::MenuSeparatorType GetSeparatorTypeAt(int index) const override { return ui::NORMAL_SEPARATOR; } int GetCommandIdAt(int index) const override { return index; } base::string16 GetLabelAt(int index) const override { return entries_[index].title; } base::string16 GetSublabelAt(int index) const override { return base::string16(); } base::string16 GetMinorTextAt(int index) const override { return base::string16(); } bool IsItemDynamicAt(int index) const override { return false; } bool GetAcceleratorAt(int index, ui::Accelerator* accelerator) const override { return false; } bool IsItemCheckedAt(int index) const override { return false; } int GetGroupIdAt(int index) const override { return -1; } bool GetIconAt(int index, gfx::Image* icon) override { return false; } ui::ButtonMenuItemModel* GetButtonMenuItemAt(int index) const override { return nullptr; } bool IsEnabledAt(int index) const override { return true; } bool IsVisibleAt(int index) const override { return true; } ui::MenuModel* GetSubmenuModelAt(int index) const override { return nullptr; } void HighlightChangedTo(int index) override {} void ActivatedAt(int index) override { ActivatedAt(index, 0); } void ActivatedAt(int index, int event_flags) override { navigation_delegate_->NavigateToOffset(entries_[index].offset); } void SetMenuModelDelegate(ui::MenuModelDelegate* delegate) override { delegate_ = delegate; } ui::MenuModelDelegate* GetMenuModelDelegate() const override { return delegate_; } ui::MenuModelDelegate* delegate_ = nullptr; Delegate* navigation_delegate_; std::vector entries_; DISALLOW_COPY_AND_ASSIGN(NavMenuModel); }; class NavButton : public views::LabelButton { public: enum class Type { BACK, FORWARD }; class ModelProvider { public: virtual std::unique_ptr CreateMenuModel(Type type) = 0; }; NavButton(Type type, ModelProvider* model_provider, views::ButtonListener* listener, const base::string16& label) : views::LabelButton(listener, label), type_(type), model_provider_(model_provider), show_menu_factory_(this) {} ~NavButton() override {} private: // views::LabelButton overrides: bool OnMousePressed(const ui::MouseEvent& event) override { if (IsTriggerableEvent(event) && enabled() && HitTestPoint(event.location())) { y_pos_on_lbuttondown_ = event.y(); base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::Bind(&NavButton::ShowMenu, show_menu_factory_.GetWeakPtr(), ui::GetMenuSourceTypeForEvent(event)), base::TimeDelta::FromMilliseconds(500)); } return LabelButton::OnMousePressed(event); } bool OnMouseDragged(const ui::MouseEvent& event) override { bool result = LabelButton::OnMouseDragged(event); if (show_menu_factory_.HasWeakPtrs()) { if (event.y() > y_pos_on_lbuttondown_ + GetHorizontalDragThreshold()) { show_menu_factory_.InvalidateWeakPtrs(); ShowMenu(ui::GetMenuSourceTypeForEvent(event)); } } return result; } void OnMouseReleased(const ui::MouseEvent& event) override { if (IsTriggerableEvent(event)) show_menu_factory_.InvalidateWeakPtrs(); LabelButton::OnMouseReleased(event); } void ShowMenu(ui::MenuSourceType source_type) { gfx::Rect local = GetLocalBounds(); gfx::Point menu_position(local.origin()); menu_position.Offset(0, local.height() - 1); View::ConvertPointToScreen(this, &menu_position); model_ = model_provider_->CreateMenuModel(type_); menu_model_adapter_.reset(new views::MenuModelAdapter( model_.get(), base::Bind(&NavButton::OnMenuClosed, base::Unretained(this)))); menu_model_adapter_->set_triggerable_event_flags(triggerable_event_flags()); menu_runner_.reset(new views::MenuRunner( menu_model_adapter_->CreateMenu(), views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::ASYNC)); ignore_result(menu_runner_->RunMenuAt( GetWidget(), nullptr, gfx::Rect(menu_position, gfx::Size(0, 0)), views::MENU_ANCHOR_TOPLEFT, source_type)); } void OnMenuClosed() { SetMouseHandler(nullptr); model_.reset(); menu_runner_.reset(); menu_model_adapter_.reset(); } Type type_; ModelProvider* model_provider_; int y_pos_on_lbuttondown_ = 0; std::unique_ptr model_; std::unique_ptr menu_model_adapter_; std::unique_ptr menu_runner_; base::WeakPtrFactory show_menu_factory_; DISALLOW_COPY_AND_ASSIGN(NavButton); }; class ProgressBar : public views::View { public: ProgressBar() {} ~ProgressBar() override {} void SetProgress(double progress) { progress_ = progress; SchedulePaint(); } private: void OnPaint(gfx::Canvas* canvas) override { gfx::Rect stroke_rect = GetLocalBounds(); stroke_rect.set_y(stroke_rect.bottom() - 1); stroke_rect.set_height(1); canvas->FillRect(stroke_rect, SK_ColorGRAY); if (progress_ != 0.f) { gfx::Rect progress_rect = GetLocalBounds(); progress_rect.set_width(progress_rect.width() * progress_); canvas->FillRect(progress_rect, SK_ColorRED); } } double progress_ = 0.f; DISALLOW_COPY_AND_ASSIGN(ProgressBar); }; class Throbber : public views::View { public: Throbber() : timer_(false, true), weak_factory_(this) {} ~Throbber() override {} void Start() { throbbing_ = true; start_time_ = base::TimeTicks::Now(); SchedulePaint(); timer_.Start( FROM_HERE, base::TimeDelta::FromMilliseconds(30), base::Bind(&Throbber::SchedulePaint, weak_factory_.GetWeakPtr())); } void Stop() { throbbing_ = false; if (timer_.IsRunning()) timer_.Stop(); SchedulePaint(); } private: void OnPaint(gfx::Canvas* canvas) override { if (!throbbing_) return; gfx::PaintThrobberSpinning( canvas, GetLocalBounds(), GetNativeTheme()->GetSystemColor( ui::NativeTheme::kColorId_ThrobberSpinningColor), base::TimeTicks::Now() - start_time_); } bool throbbing_ = false; base::TimeTicks start_time_; base::Timer timer_; base::WeakPtrFactory weak_factory_; DISALLOW_COPY_AND_ASSIGN(Throbber); }; class UI : public views::WidgetDelegateView, public views::ButtonListener, public views::TextfieldController, public TabStrip::Delegate, public TabStripObserver, public navigation::ViewDelegate, public navigation::ViewObserver, public NavButton::ModelProvider, public NavMenuModel::Delegate { public: enum class Type { WINDOW, POPUP }; UI(Browser* browser, Type type, std::unique_ptr view) : browser_(browser), type_(type), tab_strip_(new TabStrip(this)), back_button_(new NavButton(NavButton::Type::BACK, this, this, base::ASCIIToUTF16("Back"))), forward_button_(new NavButton(NavButton::Type::FORWARD, this, this, base::ASCIIToUTF16("Forward"))), reload_button_( new views::LabelButton(this, base::ASCIIToUTF16("Reload"))), prompt_(new views::Textfield), debug_button_(new views::LabelButton(this, base::ASCIIToUTF16("DV"))), throbber_(new Throbber), progress_bar_(new ProgressBar), debug_view_(new DebugView) { set_background(views::Background::CreateStandardPanelBackground()); prompt_->set_controller(this); back_button_->set_request_focus_on_press(false); forward_button_->set_request_focus_on_press(false); reload_button_->set_request_focus_on_press(false); AddChildView(tab_strip_); AddChildView(back_button_); AddChildView(forward_button_); AddChildView(reload_button_); AddChildView(prompt_); AddChildView(debug_button_); AddChildView(throbber_); AddChildView(progress_bar_); AddChildView(debug_view_); tab_strip_->AddObserver(this); tab_strip_->AddTab(std::move(view)); } ~UI() override { browser_->RemoveWindow(GetWidget()); } void NavigateTo(const GURL& url) { selected_view()->NavigateToURL(url); } private: // Overridden from views::WidgetDelegate: base::string16 GetWindowTitle() const override { // TODO(beng): use resources. if (selected_view()->title().empty()) return base::ASCIIToUTF16("Browser"); base::string16 format = base::ASCIIToUTF16("%s - Browser"); base::ReplaceFirstSubstringAfterOffset(&format, 0, base::ASCIIToUTF16("%s"), selected_view()->title()); return format; } bool CanResize() const override { return true; } bool CanMaximize() const override { return true; } bool CanMinimize() const override { return true; } // views::ButtonListener: void ButtonPressed(views::Button* sender, const ui::Event& event) override { if (sender == back_button_) { selected_view()->GoBack(); } else if (sender == forward_button_) { selected_view()->GoForward(); } else if (sender == reload_button_) { if (selected_view()->is_loading()) selected_view()->Stop(); else selected_view()->Reload(false); } else if (sender == debug_button_) { ToggleDebugView(); } } // Overridden from views::View: void Layout() override { gfx::Rect local_bounds = GetLocalBounds(); gfx::Size ps = tab_strip_->GetPreferredSize(); tab_strip_->SetBoundsRect( gfx::Rect(0, 5, local_bounds.width(), ps.height())); gfx::Rect bounds = local_bounds; bounds.set_y(tab_strip_->bounds().bottom()); bounds.Inset(5, 5); ps = back_button_->GetPreferredSize(); back_button_->SetBoundsRect( gfx::Rect(bounds.x(), bounds.y(), ps.width(), ps.height())); ps = forward_button_->GetPreferredSize(); forward_button_->SetBoundsRect(gfx::Rect(back_button_->bounds().right() + 5, bounds.y(), ps.width(), ps.height())); ps = reload_button_->GetPreferredSize(); reload_button_->SetBoundsRect( gfx::Rect(forward_button_->bounds().right() + 5, bounds.y(), ps.width(), ps.height())); ps = prompt_->GetPreferredSize(); int throbber_size = ps.height(); gfx::Size debug_ps = debug_button_->GetPreferredSize(); int prompt_y = bounds.y() + (reload_button_->bounds().height() - ps.height()) / 2; int width = bounds.width() - reload_button_->bounds().right() - throbber_size - 15 - debug_ps.width(); prompt_->SetBoundsRect(gfx::Rect(reload_button_->bounds().right() + 5, prompt_y, width, ps.height())); debug_button_->SetBoundsRect( gfx::Rect(prompt_->bounds().right() + 5, prompt_->bounds().y(), debug_ps.width(), debug_ps.height())); throbber_->SetBoundsRect(gfx::Rect(debug_button_->bounds().right() + 5, prompt_->bounds().y(), throbber_size, throbber_size)); gfx::Rect progress_bar_rect(local_bounds.x(), back_button_->bounds().bottom() + 5, local_bounds.width(), 2); progress_bar_->SetBoundsRect(progress_bar_rect); int debug_view_height = 0; if (showing_debug_view_) debug_view_height = debug_view_->GetPreferredSize().height(); debug_view_->SetBoundsRect( gfx::Rect(local_bounds.x(), local_bounds.height() - debug_view_height, local_bounds.width(), debug_view_height)); if (content_area_) { int x = local_bounds.x(); int y = type_ == Type::POPUP ? 0 : progress_bar_->bounds().bottom(); gfx::Point offset(x, y); ConvertPointToWidget(this, &offset); int width = local_bounds.width(); int height = local_bounds.height() - y - debug_view_height; content_area_->SetBounds( gfx::Rect(offset.x(), offset.y(), width, height)); for (auto* child : content_area_->children()) child->SetBounds(gfx::Rect(0, 0, width, height)); } } void ViewHierarchyChanged( const views::View::ViewHierarchyChangedDetails& details) override { if (details.is_add && GetWidget() && !content_area_) { aura::Window* window = GetWidget()->GetNativeWindow(); content_area_ = new aura::Window(nullptr); content_area_->Init(ui::LAYER_NOT_DRAWN); content_area_->Show(); window->AddChild(content_area_); tab_strip_->SetContainerWindow(content_area_); } } // Overridden from views::TextFieldController: bool HandleKeyEvent(views::Textfield* sender, const ui::KeyEvent& key_event) override { if (key_event.type() == ui::ET_KEY_PRESSED && key_event.key_code() == ui::VKEY_RETURN) selected_view()->NavigateToURL(GURL(prompt_->text())); return false; } // TabStrip::Delegate: void NewTab() override { tab_strip_->AddTab(browser_->CreateView()); tab_strip_->selected_tab()->view()->NavigateToURL(GURL("about:blank")); } // TabStripObserver: void OnTabAdded(Tab* added) override { added->view()->AddObserver(this); added->view()->set_delegate(this); } void OnTabSelected(Tab* selected) override { debug_view_->set_view(selected->view()); prompt_->SetText(base::UTF8ToUTF16(selected->view()->url().spec())); if (GetWidget()) GetWidget()->UpdateWindowTitle(); } // navigation::ViewDelegate: void ViewCreated(navigation::View* source, std::unique_ptr view, bool is_popup, const gfx::Rect& initial_rect, bool user_gesture) override { if (is_popup) CreateNewWindow(std::move(view), initial_rect, is_popup); else tab_strip_->AddTab(std::move(view)); } void Close(navigation::View* source) override { tab_strip_->CloseTabForView(source); if (tab_strip_->empty()) GetWidget()->Close(); } void OpenURL(navigation::View* source, navigation::mojom::OpenURLParamsPtr params) override { switch (params->disposition) { case navigation::mojom::WindowOpenDisposition::CURRENT_TAB: selected_view()->NavigateToURL(params->url); break; case navigation::mojom::WindowOpenDisposition::NEW_FOREGROUND_TAB: tab_strip_->AddTab(browser_->CreateView()); tab_strip_->selected_tab()->view()->NavigateToURL(params->url); break; case navigation::mojom::WindowOpenDisposition::NEW_POPUP: case navigation::mojom::WindowOpenDisposition::NEW_WINDOW: { std::unique_ptr view = browser_->CreateView(); view->NavigateToURL(params->url); CreateNewWindow( std::move(view), gfx::Rect(), params->disposition == navigation::mojom::WindowOpenDisposition::NEW_POPUP); break; } default: break; } } // navigation::ViewObserver: void LoadingStateChanged(navigation::View* view) override { if (view->is_loading()) { reload_button_->SetText(base::ASCIIToUTF16("Stop")); throbber_->Start(); } else { reload_button_->SetText(base::ASCIIToUTF16("Reload")); throbber_->Stop(); progress_bar_->SetProgress(0.f); } } void LoadProgressChanged(navigation::View* view, double progress) override { progress_bar_->SetProgress(progress); } void NavigationStateChanged(navigation::View* view) override { EnableButton(back_button_, view->can_go_back()); EnableButton(forward_button_, view->can_go_forward()); prompt_->SetText(base::UTF8ToUTF16(view->url().spec())); GetWidget()->UpdateWindowTitle(); } void HoverTargetURLChanged(navigation::View* view, const GURL& url) override { if (url.is_valid()) prompt_->SetText(base::UTF8ToUTF16(url.spec())); else prompt_->SetText(base::UTF8ToUTF16(selected_view()->url().spec())); } // NavButton::ModelProvider: std::unique_ptr CreateMenuModel( NavButton::Type type) override { std::vector entries; if (type == NavButton::Type::BACK) { selected_view()->GetBackMenuItems(&entries); } else { selected_view()->GetForwardMenuItems(&entries); } return base::MakeUnique(entries, this); } // NavMenuModel::Delegate: void NavigateToOffset(int offset) override { selected_view()->NavigateToOffset(offset); } navigation::View* selected_view() { return const_cast( static_cast(this)->selected_view()); } const navigation::View* selected_view() const { return tab_strip_->selected_tab()->view(); } void CreateNewWindow(std::unique_ptr view, const gfx::Rect& initial_bounds, bool is_popup) { gfx::Rect bounds = initial_bounds; if (bounds.IsEmpty()) bounds = gfx::Rect(10, 10, 400, 300); views::Widget* window = views::Widget::CreateWindowWithContextAndBounds( new UI(browser_, is_popup ? UI::Type::POPUP : UI::Type::WINDOW, std::move(view)), nullptr, bounds); window->Show(); browser_->AddWindow(window); } void ToggleDebugView() { showing_debug_view_ = !showing_debug_view_; Layout(); } Browser* browser_; Type type_; TabStrip* tab_strip_; views::LabelButton* back_button_; views::LabelButton* forward_button_; views::LabelButton* reload_button_; views::Textfield* prompt_; views::LabelButton* debug_button_; Throbber* throbber_; ProgressBar* progress_bar_; aura::Window* content_area_ = nullptr; DebugView* debug_view_; bool showing_debug_view_ = false; DISALLOW_COPY_AND_ASSIGN(UI); }; Browser::Browser() { registry_.AddInterface(this); } Browser::~Browser() {} void Browser::AddWindow(views::Widget* window) { windows_.push_back(window); } void Browser::RemoveWindow(views::Widget* window) { auto it = std::find(windows_.begin(), windows_.end(), window); DCHECK(it != windows_.end()); windows_.erase(it); if (windows_.empty()) base::MessageLoop::current()->QuitWhenIdle(); } std::unique_ptr Browser::CreateView() { navigation::mojom::ViewFactoryPtr factory; context()->connector()->BindInterface(content::mojom::kBrowserServiceName, &factory); return base::MakeUnique(std::move(factory)); } void Browser::OnStart() { tracing_.Initialize(context()->connector(), context()->identity().name()); aura_init_ = base::MakeUnique( context()->connector(), context()->identity(), "views_mus_resources.pak", std::string(), nullptr, views::AuraInit::Mode::AURA_MUS); } void Browser::OnBindInterface(const service_manager::ServiceInfo& source_info, const std::string& interface_name, mojo::ScopedMessagePipeHandle interface_pipe) { registry_.BindInterface(source_info.identity, interface_name, std::move(interface_pipe)); } void Browser::Launch(uint32_t what, mojom::LaunchMode how) { bool reuse = how == mojom::LaunchMode::REUSE || how == mojom::LaunchMode::DEFAULT; if (reuse && !windows_.empty()) { windows_.back()->Activate(); return; } UI* ui = new UI(this, UI::Type::WINDOW, CreateView()); views::Widget* window = views::Widget::CreateWindowWithContextAndBounds( ui, nullptr, gfx::Rect(10, 10, 1024, 600)); ui->NavigateTo(GURL("http://www.google.com/")); window->Show(); AddWindow(window); } void Browser::Create(const service_manager::Identity& remote_identity, mojom::LaunchableRequest request) { bindings_.AddBinding(this, std::move(request)); } } // namespace browser } // namespace mash