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/webrtc/desktop_media_list_ash.h"
6
7 #include <utility>
8
9 #include "ash/public/cpp/shell_window_ids.h"
10 #include "ash/shell.h"
11 #include "ash/wm/desks/desks_util.h"
12 #include "base/bind.h"
13 #include "chrome/grit/generated_resources.h"
14 #include "media/base/video_util.h"
15 #include "ui/base/l10n/l10n_util.h"
16 #include "ui/gfx/image/image.h"
17 #include "ui/snapshot/snapshot.h"
18
19 using content::DesktopMediaID;
20
21 namespace {
22
23 // Update the list twice per second.
24 const int kDefaultDesktopMediaListUpdatePeriod = 500;
25
26 } // namespace
27
DesktopMediaListAsh(content::DesktopMediaID::Type type)28 DesktopMediaListAsh::DesktopMediaListAsh(content::DesktopMediaID::Type type)
29 : DesktopMediaListBase(base::TimeDelta::FromMilliseconds(
30 kDefaultDesktopMediaListUpdatePeriod)) {
31 DCHECK(type == content::DesktopMediaID::TYPE_SCREEN ||
32 type == content::DesktopMediaID::TYPE_WINDOW);
33 type_ = type;
34 }
35
~DesktopMediaListAsh()36 DesktopMediaListAsh::~DesktopMediaListAsh() {
37 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
38 }
39
Refresh(bool update_thumnails)40 void DesktopMediaListAsh::Refresh(bool update_thumnails) {
41 DCHECK(can_refresh());
42 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
43 DCHECK_EQ(pending_window_capture_requests_, 0);
44
45 std::vector<SourceDescription> new_sources;
46 EnumerateSources(&new_sources, update_thumnails);
47 UpdateSourcesList(new_sources);
48 OnRefreshMaybeComplete();
49 }
50
EnumerateWindowsForRoot(std::vector<DesktopMediaListAsh::SourceDescription> * sources,bool update_thumnails,aura::Window * root_window,int container_id)51 void DesktopMediaListAsh::EnumerateWindowsForRoot(
52 std::vector<DesktopMediaListAsh::SourceDescription>* sources,
53 bool update_thumnails,
54 aura::Window* root_window,
55 int container_id) {
56 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
57
58 aura::Window* container = ash::Shell::GetContainer(root_window, container_id);
59 if (!container)
60 return;
61 // The |container| has all the top-level windows in reverse order, e.g. the
62 // most top-level window is at the end. So iterate children reversely to make
63 // sure |sources| is in the expected order.
64 for (aura::Window::Windows::const_reverse_iterator it =
65 container->children().rbegin();
66 it != container->children().rend(); ++it) {
67 if (!(*it)->IsVisible() || !(*it)->CanFocus())
68 continue;
69 content::DesktopMediaID id = content::DesktopMediaID::RegisterNativeWindow(
70 content::DesktopMediaID::TYPE_WINDOW, *it);
71 if (id.window_id == view_dialog_id_.window_id)
72 continue;
73 SourceDescription window_source(id, (*it)->GetTitle());
74 sources->push_back(window_source);
75
76 if (update_thumnails)
77 CaptureThumbnail(window_source.id, *it);
78 }
79 }
80
EnumerateSources(std::vector<DesktopMediaListAsh::SourceDescription> * sources,bool update_thumnails)81 void DesktopMediaListAsh::EnumerateSources(
82 std::vector<DesktopMediaListAsh::SourceDescription>* sources,
83 bool update_thumnails) {
84 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
85
86 aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
87
88 for (size_t i = 0; i < root_windows.size(); ++i) {
89 if (type_ == content::DesktopMediaID::TYPE_SCREEN) {
90 SourceDescription screen_source(
91 content::DesktopMediaID::RegisterNativeWindow(
92 content::DesktopMediaID::TYPE_SCREEN, root_windows[i]),
93 root_windows[i]->GetTitle());
94
95 if (root_windows[i] == ash::Shell::GetPrimaryRootWindow())
96 sources->insert(sources->begin(), screen_source);
97 else
98 sources->push_back(screen_source);
99
100 if (screen_source.name.empty()) {
101 if (root_windows.size() > 1) {
102 // 'Screen' in 'Screen 1, Screen 2, etc ' might be inflected in some
103 // languages depending on the number although rather unlikely. To be
104 // safe, use the plural format.
105 // TODO(jshin): Revert to GetStringFUTF16Int (with native digits)
106 // if none of UI languages inflects 'Screen' in this context.
107 screen_source.name = l10n_util::GetPluralStringFUTF16(
108 IDS_DESKTOP_MEDIA_PICKER_MULTIPLE_SCREEN_NAME,
109 static_cast<int>(i + 1));
110 } else {
111 screen_source.name = l10n_util::GetStringUTF16(
112 IDS_DESKTOP_MEDIA_PICKER_SINGLE_SCREEN_NAME);
113 }
114 }
115
116 if (update_thumnails)
117 CaptureThumbnail(screen_source.id, root_windows[i]);
118 } else {
119 // The list of desks containers depends on whether the Virtual Desks
120 // feature is enabled or not.
121 for (int desk_id : ash::desks_util::GetDesksContainersIds())
122 EnumerateWindowsForRoot(sources, update_thumnails, root_windows[i],
123 desk_id);
124
125 EnumerateWindowsForRoot(sources, update_thumnails, root_windows[i],
126 ash::kShellWindowId_AlwaysOnTopContainer);
127 EnumerateWindowsForRoot(sources, update_thumnails, root_windows[i],
128 ash::kShellWindowId_PipContainer);
129 }
130 }
131 }
132
CaptureThumbnail(content::DesktopMediaID id,aura::Window * window)133 void DesktopMediaListAsh::CaptureThumbnail(content::DesktopMediaID id,
134 aura::Window* window) {
135 gfx::Rect window_rect(window->bounds().width(), window->bounds().height());
136 gfx::Rect scaled_rect = media::ComputeLetterboxRegion(
137 gfx::Rect(thumbnail_size_), window_rect.size());
138
139 ++pending_window_capture_requests_;
140 ui::GrabWindowSnapshotAndScaleAsync(
141 window, window_rect, scaled_rect.size(),
142 base::BindOnce(&DesktopMediaListAsh::OnThumbnailCaptured,
143 weak_factory_.GetWeakPtr(), id));
144 }
145
OnThumbnailCaptured(content::DesktopMediaID id,gfx::Image image)146 void DesktopMediaListAsh::OnThumbnailCaptured(content::DesktopMediaID id,
147 gfx::Image image) {
148 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
149
150 UpdateSourceThumbnail(id, image.AsImageSkia());
151
152 --pending_window_capture_requests_;
153 DCHECK_GE(pending_window_capture_requests_, 0);
154
155 OnRefreshMaybeComplete();
156 }
157
OnRefreshMaybeComplete()158 void DesktopMediaListAsh::OnRefreshMaybeComplete() {
159 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
160
161 if (pending_window_capture_requests_ == 0) {
162 // Once we've finished capturing all windows, notify the caller, which will
163 // post a task for the next list update if necessary.
164 OnRefreshComplete();
165 }
166 }
167