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 "chrome/browser/media/webrtc/desktop_media_list_base.h"
6 
7 #include <set>
8 #include <utility>
9 
10 #include "base/bind.h"
11 #include "chrome/browser/media/webrtc/desktop_media_list.h"
12 #include "content/public/browser/browser_task_traits.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "ui/gfx/image/image.h"
15 
16 using content::DesktopMediaID;
17 
DesktopMediaListBase(base::TimeDelta update_period)18 DesktopMediaListBase::DesktopMediaListBase(base::TimeDelta update_period)
19     : update_period_(update_period) {}
20 
~DesktopMediaListBase()21 DesktopMediaListBase::~DesktopMediaListBase() {}
22 
SetUpdatePeriod(base::TimeDelta period)23 void DesktopMediaListBase::SetUpdatePeriod(base::TimeDelta period) {
24   DCHECK(!observer_);
25   update_period_ = period;
26 }
27 
SetThumbnailSize(const gfx::Size & thumbnail_size)28 void DesktopMediaListBase::SetThumbnailSize(const gfx::Size& thumbnail_size) {
29   thumbnail_size_ = thumbnail_size;
30 }
31 
SetViewDialogWindowId(DesktopMediaID dialog_id)32 void DesktopMediaListBase::SetViewDialogWindowId(DesktopMediaID dialog_id) {
33   view_dialog_id_ = dialog_id;
34 }
35 
StartUpdating(DesktopMediaListObserver * observer)36 void DesktopMediaListBase::StartUpdating(DesktopMediaListObserver* observer) {
37   DCHECK(!observer_);
38   observer_ = observer;
39 
40   // Process sources previously discovered by a call to Update().
41   if (observer_) {
42     for (size_t i = 0; i < sources_.size(); i++) {
43       observer_->OnSourceAdded(this, i);
44     }
45   }
46 
47   DCHECK(!refresh_callback_);
48   refresh_callback_ = base::BindOnce(&DesktopMediaListBase::ScheduleNextRefresh,
49                                      weak_factory_.GetWeakPtr());
50   Refresh(true);
51 }
52 
Update(UpdateCallback callback)53 void DesktopMediaListBase::Update(UpdateCallback callback) {
54   DCHECK(sources_.empty());
55   DCHECK(!refresh_callback_);
56   refresh_callback_ = std::move(callback);
57   Refresh(false);
58 }
59 
GetSourceCount() const60 int DesktopMediaListBase::GetSourceCount() const {
61   return sources_.size();
62 }
63 
GetSource(int index) const64 const DesktopMediaList::Source& DesktopMediaListBase::GetSource(
65     int index) const {
66   DCHECK_GE(index, 0);
67   DCHECK_LT(index, static_cast<int>(sources_.size()));
68   return sources_[index];
69 }
70 
GetMediaListType() const71 DesktopMediaID::Type DesktopMediaListBase::GetMediaListType() const {
72   return type_;
73 }
74 
SourceDescription(DesktopMediaID id,const base::string16 & name)75 DesktopMediaListBase::SourceDescription::SourceDescription(
76     DesktopMediaID id,
77     const base::string16& name)
78     : id(id), name(name) {}
79 
UpdateSourcesList(const std::vector<SourceDescription> & new_sources)80 void DesktopMediaListBase::UpdateSourcesList(
81     const std::vector<SourceDescription>& new_sources) {
82   typedef std::set<DesktopMediaID> SourceSet;
83   SourceSet new_source_set;
84   for (size_t i = 0; i < new_sources.size(); ++i) {
85     new_source_set.insert(new_sources[i].id);
86   }
87   // Iterate through the old sources to find the removed sources.
88   for (size_t i = 0; i < sources_.size(); ++i) {
89     if (new_source_set.find(sources_[i].id) == new_source_set.end()) {
90       sources_.erase(sources_.begin() + i);
91       if (observer_)
92         observer_->OnSourceRemoved(this, i);
93       --i;
94     }
95   }
96   // Iterate through the new sources to find the added sources.
97   if (new_sources.size() > sources_.size()) {
98     SourceSet old_source_set;
99     for (size_t i = 0; i < sources_.size(); ++i) {
100       old_source_set.insert(sources_[i].id);
101     }
102 
103     for (size_t i = 0; i < new_sources.size(); ++i) {
104       if (old_source_set.find(new_sources[i].id) == old_source_set.end()) {
105         sources_.insert(sources_.begin() + i, Source());
106         sources_[i].id = new_sources[i].id;
107         sources_[i].name = new_sources[i].name;
108         if (observer_)
109           observer_->OnSourceAdded(this, i);
110       }
111     }
112   }
113   DCHECK_EQ(new_sources.size(), sources_.size());
114 
115   // Find the moved/changed sources.
116   size_t pos = 0;
117   while (pos < sources_.size()) {
118     if (!(sources_[pos].id == new_sources[pos].id)) {
119       // Find the source that should be moved to |pos|, starting from |pos + 1|
120       // of |sources_|, because entries before |pos| should have been sorted.
121       size_t old_pos = pos + 1;
122       for (; old_pos < sources_.size(); ++old_pos) {
123         if (sources_[old_pos].id == new_sources[pos].id)
124           break;
125       }
126       DCHECK(sources_[old_pos].id == new_sources[pos].id);
127 
128       // Move the source from |old_pos| to |pos|.
129       Source temp = sources_[old_pos];
130       sources_.erase(sources_.begin() + old_pos);
131       sources_.insert(sources_.begin() + pos, temp);
132 
133       if (observer_)
134         observer_->OnSourceMoved(this, old_pos, pos);
135     }
136 
137     if (sources_[pos].name != new_sources[pos].name) {
138       sources_[pos].name = new_sources[pos].name;
139       if (observer_)
140         observer_->OnSourceNameChanged(this, pos);
141     }
142     ++pos;
143   }
144 }
145 
UpdateSourceThumbnail(DesktopMediaID id,const gfx::ImageSkia & image)146 void DesktopMediaListBase::UpdateSourceThumbnail(DesktopMediaID id,
147                                                  const gfx::ImageSkia& image) {
148   // Unlike other methods that check can_refresh(), this one won't cause
149   // OnRefreshComplete() to be called, but the caller is expected to schedule a
150   // call to OnRefreshComplete() after this method has been called as many times
151   // as needed, so the check is still valid.
152   DCHECK(can_refresh());
153 
154   for (size_t i = 0; i < sources_.size(); ++i) {
155     if (sources_[i].id == id) {
156       sources_[i].thumbnail = image;
157       if (observer_)
158         observer_->OnSourceThumbnailChanged(this, i);
159       break;
160     }
161   }
162 }
163 
164 // static
GetImageHash(const gfx::Image & image)165 uint32_t DesktopMediaListBase::GetImageHash(const gfx::Image& image) {
166   SkBitmap bitmap = image.AsBitmap();
167   return base::FastHash(base::make_span(
168       static_cast<uint8_t*>(bitmap.getPixels()), bitmap.computeByteSize()));
169 }
170 
OnRefreshComplete()171 void DesktopMediaListBase::OnRefreshComplete() {
172   DCHECK(refresh_callback_);
173   std::move(refresh_callback_).Run();
174 }
175 
ScheduleNextRefresh()176 void DesktopMediaListBase::ScheduleNextRefresh() {
177   DCHECK(!refresh_callback_);
178   refresh_callback_ = base::BindOnce(&DesktopMediaListBase::ScheduleNextRefresh,
179                                      weak_factory_.GetWeakPtr());
180   content::GetUIThreadTaskRunner({})->PostDelayedTask(
181       FROM_HERE,
182       base::BindOnce(&DesktopMediaListBase::Refresh, weak_factory_.GetWeakPtr(),
183                      true),
184       update_period_);
185 }
186