1 // Copyright 2018 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 "chrome/browser/ui/views/media_router/web_contents_display_observer_view.h"
6 
7 #include <memory>
8 #include <utility>
9 
10 #include "chrome/browser/ui/browser_list.h"
11 #include "content/public/browser/web_contents.h"
12 #include "ui/display/screen.h"
13 #include "ui/views/widget/widget.h"
14 
15 namespace media_router {
16 
17 // static
Create(content::WebContents * web_contents,base::RepeatingClosure callback)18 std::unique_ptr<WebContentsDisplayObserver> WebContentsDisplayObserver::Create(
19     content::WebContents* web_contents,
20     base::RepeatingClosure callback) {
21   return std::make_unique<WebContentsDisplayObserverView>(web_contents,
22                                                           std::move(callback));
23 }
24 
WebContentsDisplayObserverView(content::WebContents * web_contents,base::RepeatingClosure callback)25 WebContentsDisplayObserverView::WebContentsDisplayObserverView(
26     content::WebContents* web_contents,
27     base::RepeatingClosure callback)
28     : web_contents_(web_contents),
29       widget_(views::Widget::GetWidgetForNativeWindow(
30           web_contents->GetTopLevelNativeWindow())),
31       callback_(std::move(callback)) {
32   // |widget_| may be null in tests.
33   if (widget_) {
34     display_ = GetDisplayNearestWidget();
35     widget_->AddObserver(this);
36   }
37   BrowserList::AddObserver(this);
38 }
39 
~WebContentsDisplayObserverView()40 WebContentsDisplayObserverView::~WebContentsDisplayObserverView() {
41   if (widget_)
42     widget_->RemoveObserver(this);
43   BrowserList::RemoveObserver(this);
44   CHECK(!IsInObserverList());
45 }
46 
OnBrowserSetLastActive(Browser * browser)47 void WebContentsDisplayObserverView::OnBrowserSetLastActive(Browser* browser) {
48   // This gets called when a browser tab detaches from a window or gets merged
49   // into another window. We update the widget to observe, if necessary.
50   // If |web_contents_| or |widget_| is null, then we no longer have WebContents
51   // to observe.
52   if (!web_contents_ || !widget_)
53     return;
54 
55   views::Widget* new_widget = views::Widget::GetWidgetForNativeWindow(
56       web_contents_->GetTopLevelNativeWindow());
57   if (new_widget != widget_) {
58     widget_->RemoveObserver(this);
59     widget_ = new_widget;
60     if (widget_) {
61       widget_->AddObserver(this);
62       CheckForDisplayChange();
63     }
64   }
65 }
66 
OnWidgetClosing(views::Widget * widget)67 void WebContentsDisplayObserverView::OnWidgetClosing(views::Widget* widget) {
68   if (widget_)
69     widget_->RemoveObserver(this);
70   widget_ = nullptr;
71 }
72 
OnWidgetBoundsChanged(views::Widget * widget,const gfx::Rect & new_bounds)73 void WebContentsDisplayObserverView::OnWidgetBoundsChanged(
74     views::Widget* widget,
75     const gfx::Rect& new_bounds) {
76   CheckForDisplayChange();
77 }
78 
GetCurrentDisplay() const79 const display::Display& WebContentsDisplayObserverView::GetCurrentDisplay()
80     const {
81   return display_;
82 }
83 
WebContentsDestroyed()84 void WebContentsDisplayObserverView::WebContentsDestroyed() {
85   web_contents_ = nullptr;
86 }
87 
CheckForDisplayChange()88 void WebContentsDisplayObserverView::CheckForDisplayChange() {
89   display::Display new_display = GetDisplayNearestWidget();
90   if (new_display.id() == display_.id())
91     return;
92 
93   display_ = new_display;
94   callback_.Run();
95 }
96 
GetDisplayNearestWidget() const97 display::Display WebContentsDisplayObserverView::GetDisplayNearestWidget()
98     const {
99   return display::Screen::GetScreen()->GetDisplayNearestWindow(
100       widget_->GetNativeWindow());
101 }
102 
103 }  // namespace media_router
104