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 |