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