Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / media / desktop_media_list_ash.cc
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.
4
5 #include "chrome/browser/media/desktop_media_list_ash.h"
6
7 #include <set>
8
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 "content/public/browser/browser_thread.h"
17 #include "grit/generated_resources.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"
23
24 using content::BrowserThread;
25 using content::DesktopMediaID;
26
27 namespace {
28
29 // Update the list twice per second.
30 const int kDefaultUpdatePeriod = 500;
31
32 }  // namespace
33
34 DesktopMediaListAsh::SourceDescription::SourceDescription(
35     DesktopMediaID id,
36     const base::string16& name)
37     : id(id),
38       name(name) {
39 }
40
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) {
49 }
50
51 DesktopMediaListAsh::~DesktopMediaListAsh() {}
52
53 void DesktopMediaListAsh::SetUpdatePeriod(base::TimeDelta period) {
54   DCHECK(!observer_);
55   update_period_ = period;
56 }
57
58 void DesktopMediaListAsh::SetThumbnailSize(
59     const gfx::Size& thumbnail_size) {
60   thumbnail_size_ = thumbnail_size;
61 }
62
63 void DesktopMediaListAsh::SetViewDialogWindowId(
64     content::DesktopMediaID::Id dialog_id) {
65   view_dialog_id_ = dialog_id;
66 }
67
68 void DesktopMediaListAsh::StartUpdating(DesktopMediaListObserver* observer) {
69   DCHECK(!observer_);
70
71   observer_ = observer;
72   Refresh();
73 }
74
75 int DesktopMediaListAsh::GetSourceCount() const {
76   return sources_.size();
77 }
78
79 const DesktopMediaList::Source& DesktopMediaListAsh::GetSource(
80     int index) const {
81   return sources_[index];
82 }
83
84 void DesktopMediaListAsh::Refresh() {
85   std::vector<SourceDescription> new_sources;
86   EnumerateSources(&new_sources);
87
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);
92   }
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;
99     }
100   }
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);
106     }
107
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);
114       }
115     }
116   }
117   DCHECK_EQ(new_sources.size(), sources_.size());
118
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;
129       }
130       DCHECK(sources_[old_pos].id == new_sources[pos].id);
131
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);
136
137       observer_->OnSourceMoved(old_pos, pos);
138     }
139
140     if (sources_[pos].name != new_sources[pos].name) {
141       sources_[pos].name = new_sources[pos].name;
142       observer_->OnSourceNameChanged(pos);
143     }
144     ++pos;
145   }
146 }
147
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));
153
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);
167
168     CaptureThumbnail(window_source.id, *it);
169   }
170 }
171
172 void DesktopMediaListAsh::EnumerateSources(
173     std::vector<DesktopMediaListAsh::SourceDescription>* sources) {
174   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
175
176   aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
177
178   for (aura::Window::Windows::const_iterator iter = root_windows.begin();
179        iter != root_windows.end(); ++iter) {
180     if (source_types_ & SCREENS) {
181       SourceDescription screen_source(
182           content::DesktopMediaID::RegisterAuraWindow(*iter), (*iter)->title());
183       sources->push_back(screen_source);
184
185       CaptureThumbnail(screen_source.id, *iter);
186     }
187
188     if (source_types_ & WINDOWS) {
189       EnumerateWindowsForRoot(
190           sources, *iter, ash::kShellWindowId_DefaultContainer);
191       EnumerateWindowsForRoot(
192           sources, *iter, ash::kShellWindowId_AlwaysOnTopContainer);
193       EnumerateWindowsForRoot(
194           sources, *iter, ash::kShellWindowId_DockedContainer);
195     }
196   }
197 }
198
199 void DesktopMediaListAsh::CaptureThumbnail(content::DesktopMediaID id,
200                                            aura::Window* window) {
201   gfx::Rect window_rect(window->bounds().width(), window->bounds().height());
202   gfx::Rect scaled_rect = media::ComputeLetterboxRegion(
203       gfx::Rect(thumbnail_size_), window_rect.size());
204
205   ++pending_window_capture_requests_;
206   ui::GrabWindowSnapshotAndScaleAsync(
207       window,
208       window_rect,
209       scaled_rect.size(),
210       BrowserThread::GetBlockingPool(),
211       base::Bind(&DesktopMediaListAsh::OnThumbnailCaptured,
212                  weak_factory_.GetWeakPtr(),
213                  id));
214 }
215
216 void DesktopMediaListAsh::OnThumbnailCaptured(content::DesktopMediaID id,
217                                               const gfx::Image& image) {
218   for (size_t i = 0; i < sources_.size(); ++i) {
219     if (sources_[i].id == id) {
220       sources_[i].thumbnail = image.AsImageSkia();
221       observer_->OnSourceThumbnailChanged(i);
222       break;
223     }
224   }
225
226   --pending_window_capture_requests_;
227   DCHECK_GE(pending_window_capture_requests_, 0);
228
229   if (!pending_window_capture_requests_) {
230     // Once we've finished capturing all windows post a task for the next list
231     // update.
232     BrowserThread::PostDelayedTask(
233         BrowserThread::UI, FROM_HERE,
234         base::Bind(&DesktopMediaListAsh::Refresh,
235                    weak_factory_.GetWeakPtr()),
236         update_period_);
237   }
238 }