replace OVERRIDE and FINAL with override and final in ash/
[chromium-blink-merge.git] / chrome / browser / media / desktop_media_list_ash.cc
blobd24ec21b2b1c493942bb5472bfe57dacb2610b98
1 // Copyright 2013 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.
5 #include "chrome/browser/media/desktop_media_list_ash.h"
7 #include <set>
9 #include "ash/shell.h"
10 #include "ash/shell_window_ids.h"
11 #include "base/hash.h"
12 #include "base/logging.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/threading/sequenced_worker_pool.h"
15 #include "chrome/browser/media/desktop_media_list_observer.h"
16 #include "chrome/grit/generated_resources.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "media/base/video_util.h"
19 #include "ui/base/l10n/l10n_util.h"
20 #include "ui/compositor/dip_util.h"
21 #include "ui/gfx/image/image.h"
22 #include "ui/snapshot/snapshot.h"
24 using content::BrowserThread;
25 using content::DesktopMediaID;
27 namespace {
29 // Update the list twice per second.
30 const int kDefaultUpdatePeriod = 500;
32 } // namespace
34 DesktopMediaListAsh::SourceDescription::SourceDescription(
35 DesktopMediaID id,
36 const base::string16& name)
37 : id(id),
38 name(name) {
41 DesktopMediaListAsh::DesktopMediaListAsh(int source_types)
42 : source_types_(source_types),
43 update_period_(base::TimeDelta::FromMilliseconds(kDefaultUpdatePeriod)),
44 thumbnail_size_(100, 100),
45 view_dialog_id_(-1),
46 observer_(NULL),
47 pending_window_capture_requests_(0),
48 weak_factory_(this) {
51 DesktopMediaListAsh::~DesktopMediaListAsh() {}
53 void DesktopMediaListAsh::SetUpdatePeriod(base::TimeDelta period) {
54 DCHECK(!observer_);
55 update_period_ = period;
58 void DesktopMediaListAsh::SetThumbnailSize(
59 const gfx::Size& thumbnail_size) {
60 thumbnail_size_ = thumbnail_size;
63 void DesktopMediaListAsh::SetViewDialogWindowId(
64 content::DesktopMediaID::Id dialog_id) {
65 view_dialog_id_ = dialog_id;
68 void DesktopMediaListAsh::StartUpdating(DesktopMediaListObserver* observer) {
69 DCHECK(!observer_);
71 observer_ = observer;
72 Refresh();
75 int DesktopMediaListAsh::GetSourceCount() const {
76 return sources_.size();
79 const DesktopMediaList::Source& DesktopMediaListAsh::GetSource(
80 int index) const {
81 return sources_[index];
84 void DesktopMediaListAsh::Refresh() {
85 std::vector<SourceDescription> new_sources;
86 EnumerateSources(&new_sources);
88 typedef std::set<content::DesktopMediaID> SourceSet;
89 SourceSet new_source_set;
90 for (size_t i = 0; i < new_sources.size(); ++i) {
91 new_source_set.insert(new_sources[i].id);
93 // Iterate through the old sources to find the removed sources.
94 for (size_t i = 0; i < sources_.size(); ++i) {
95 if (new_source_set.find(sources_[i].id) == new_source_set.end()) {
96 sources_.erase(sources_.begin() + i);
97 observer_->OnSourceRemoved(i);
98 --i;
101 // Iterate through the new sources to find the added sources.
102 if (new_sources.size() > sources_.size()) {
103 SourceSet old_source_set;
104 for (size_t i = 0; i < sources_.size(); ++i) {
105 old_source_set.insert(sources_[i].id);
108 for (size_t i = 0; i < new_sources.size(); ++i) {
109 if (old_source_set.find(new_sources[i].id) == old_source_set.end()) {
110 sources_.insert(sources_.begin() + i, Source());
111 sources_[i].id = new_sources[i].id;
112 sources_[i].name = new_sources[i].name;
113 observer_->OnSourceAdded(i);
117 DCHECK_EQ(new_sources.size(), sources_.size());
119 // Find the moved/changed sources.
120 size_t pos = 0;
121 while (pos < sources_.size()) {
122 if (!(sources_[pos].id == new_sources[pos].id)) {
123 // Find the source that should be moved to |pos|, starting from |pos + 1|
124 // of |sources_|, because entries before |pos| should have been sorted.
125 size_t old_pos = pos + 1;
126 for (; old_pos < sources_.size(); ++old_pos) {
127 if (sources_[old_pos].id == new_sources[pos].id)
128 break;
130 DCHECK(sources_[old_pos].id == new_sources[pos].id);
132 // Move the source from |old_pos| to |pos|.
133 Source temp = sources_[old_pos];
134 sources_.erase(sources_.begin() + old_pos);
135 sources_.insert(sources_.begin() + pos, temp);
137 observer_->OnSourceMoved(old_pos, pos);
140 if (sources_[pos].name != new_sources[pos].name) {
141 sources_[pos].name = new_sources[pos].name;
142 observer_->OnSourceNameChanged(pos);
144 ++pos;
148 void DesktopMediaListAsh::EnumerateWindowsForRoot(
149 std::vector<DesktopMediaListAsh::SourceDescription>* sources,
150 aura::Window* root_window,
151 int container_id) {
152 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
154 aura::Window* container = ash::Shell::GetContainer(root_window, container_id);
155 if (!container)
156 return;
157 for (aura::Window::Windows::const_iterator it = container->children().begin();
158 it != container->children().end(); ++it) {
159 if (!(*it)->IsVisible() || !(*it)->CanFocus())
160 continue;
161 content::DesktopMediaID id =
162 content::DesktopMediaID::RegisterAuraWindow(*it);
163 if (id.id == view_dialog_id_)
164 continue;
165 SourceDescription window_source(id, (*it)->title());
166 sources->push_back(window_source);
168 CaptureThumbnail(window_source.id, *it);
172 void DesktopMediaListAsh::EnumerateSources(
173 std::vector<DesktopMediaListAsh::SourceDescription>* sources) {
174 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
176 aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
178 for (size_t i = 0; i < root_windows.size(); ++i) {
179 if (source_types_ & SCREENS) {
180 SourceDescription screen_source(
181 content::DesktopMediaID::RegisterAuraWindow(root_windows[i]),
182 root_windows[i]->title());
184 if (root_windows[i] == ash::Shell::GetPrimaryRootWindow())
185 sources->insert(sources->begin(), screen_source);
186 else
187 sources->push_back(screen_source);
189 if (screen_source.name.empty()) {
190 if (root_windows.size() > 1) {
191 screen_source.name = l10n_util::GetStringFUTF16Int(
192 IDS_DESKTOP_MEDIA_PICKER_MULTIPLE_SCREEN_NAME,
193 static_cast<int>(i + 1));
194 } else {
195 screen_source.name = l10n_util::GetStringUTF16(
196 IDS_DESKTOP_MEDIA_PICKER_SINGLE_SCREEN_NAME);
200 CaptureThumbnail(screen_source.id, root_windows[i]);
203 if (source_types_ & WINDOWS) {
204 EnumerateWindowsForRoot(
205 sources, root_windows[i], ash::kShellWindowId_DefaultContainer);
206 EnumerateWindowsForRoot(
207 sources, root_windows[i], ash::kShellWindowId_AlwaysOnTopContainer);
208 EnumerateWindowsForRoot(
209 sources, root_windows[i], ash::kShellWindowId_DockedContainer);
214 void DesktopMediaListAsh::CaptureThumbnail(content::DesktopMediaID id,
215 aura::Window* window) {
216 gfx::Rect window_rect(window->bounds().width(), window->bounds().height());
217 gfx::Rect scaled_rect = media::ComputeLetterboxRegion(
218 gfx::Rect(thumbnail_size_), window_rect.size());
220 ++pending_window_capture_requests_;
221 ui::GrabWindowSnapshotAndScaleAsync(
222 window,
223 window_rect,
224 scaled_rect.size(),
225 BrowserThread::GetBlockingPool(),
226 base::Bind(&DesktopMediaListAsh::OnThumbnailCaptured,
227 weak_factory_.GetWeakPtr(),
228 id));
231 void DesktopMediaListAsh::OnThumbnailCaptured(content::DesktopMediaID id,
232 const gfx::Image& image) {
233 for (size_t i = 0; i < sources_.size(); ++i) {
234 if (sources_[i].id == id) {
235 sources_[i].thumbnail = image.AsImageSkia();
236 observer_->OnSourceThumbnailChanged(i);
237 break;
241 --pending_window_capture_requests_;
242 DCHECK_GE(pending_window_capture_requests_, 0);
244 if (!pending_window_capture_requests_) {
245 // Once we've finished capturing all windows post a task for the next list
246 // update.
247 BrowserThread::PostDelayedTask(
248 BrowserThread::UI, FROM_HERE,
249 base::Bind(&DesktopMediaListAsh::Refresh,
250 weak_factory_.GetWeakPtr()),
251 update_period_);