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/tab_desktop_media_list.h"
6
7 #include <utility>
8
9 #include "base/bind.h"
10 #include "base/callback_helpers.h"
11 #include "base/hash/hash.h"
12 #include "base/task/post_task.h"
13 #include "base/task/thread_pool.h"
14 #include "chrome/browser/profiles/profile_manager.h"
15 #include "chrome/browser/ui/browser.h"
16 #include "chrome/browser/ui/browser_list.h"
17 #include "chrome/browser/ui/tabs/tab_strip_model.h"
18 #include "components/favicon/content/content_favicon_driver.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/render_frame_host.h"
21 #include "content/public/browser/render_process_host.h"
22 #include "media/base/video_util.h"
23 #include "third_party/skia/include/core/SkCanvas.h"
24 #include "third_party/skia/include/core/SkImage.h"
25 #include "ui/gfx/favicon_size.h"
26 #include "ui/gfx/image/image.h"
27
28 using content::BrowserThread;
29 using content::DesktopMediaID;
30
31 namespace {
32
CreateEnclosedFaviconImage(gfx::Size size,const gfx::ImageSkia & favicon)33 gfx::ImageSkia CreateEnclosedFaviconImage(gfx::Size size,
34 const gfx::ImageSkia& favicon) {
35 DCHECK_GE(size.width(), gfx::kFaviconSize);
36 DCHECK_GE(size.height(), gfx::kFaviconSize);
37
38 // Create a bitmap.
39 SkBitmap result;
40 result.allocN32Pixels(size.width(), size.height(), false);
41 SkCanvas canvas(result, SkSurfaceProps{});
42 canvas.clear(SK_ColorTRANSPARENT);
43
44 // Draw the favicon image into the center of result image. If the favicon is
45 // too big, scale it down.
46 gfx::Size fill_size = favicon.size();
47 if (result.width() < favicon.width() || result.height() < favicon.height())
48 fill_size = media::ScaleSizeToFitWithinTarget(favicon.size(), size);
49
50 gfx::Rect center_rect(result.width(), result.height());
51 center_rect.ClampToCenteredSize(fill_size);
52 SkRect dest_rect =
53 SkRect::MakeLTRB(center_rect.x(), center_rect.y(), center_rect.right(),
54 center_rect.bottom());
55 canvas.drawBitmapRect(*favicon.bitmap(), dest_rect, nullptr);
56
57 return gfx::ImageSkia::CreateFrom1xBitmap(result);
58 }
59
60 // Update the list once per second.
61 const int kDefaultTabDesktopMediaListUpdatePeriod = 1000;
62
63 } // namespace
64
TabDesktopMediaList()65 TabDesktopMediaList::TabDesktopMediaList()
66 : DesktopMediaListBase(base::TimeDelta::FromMilliseconds(
67 kDefaultTabDesktopMediaListUpdatePeriod)) {
68 type_ = DesktopMediaID::TYPE_WEB_CONTENTS;
69 thumbnail_task_runner_ = base::ThreadPool::CreateSequencedTaskRunner(
70 {base::MayBlock(), base::TaskPriority::USER_VISIBLE});
71 }
72
~TabDesktopMediaList()73 TabDesktopMediaList::~TabDesktopMediaList() {}
74
Refresh(bool update_thumnails)75 void TabDesktopMediaList::Refresh(bool update_thumnails) {
76 DCHECK(can_refresh());
77 DCHECK_CURRENTLY_ON(BrowserThread::UI);
78
79 Profile* profile = ProfileManager::GetLastUsedProfileAllowedByPolicy();
80 if (!profile) {
81 OnRefreshComplete();
82 return;
83 }
84
85 std::vector<Browser*> browsers;
86 for (auto* browser : *BrowserList::GetInstance()) {
87 if (browser->profile()->GetOriginalProfile() ==
88 profile->GetOriginalProfile()) {
89 browsers.push_back(browser);
90 }
91 }
92
93 ImageHashesMap new_favicon_hashes;
94 std::vector<SourceDescription> sources;
95 std::map<base::TimeTicks, SourceDescription> tab_map;
96 std::vector<std::pair<DesktopMediaID, gfx::ImageSkia>> favicon_pairs;
97
98 // Enumerate all tabs with their titles and favicons for a user profile.
99 for (auto* browser : browsers) {
100 const TabStripModel* tab_strip_model = browser->tab_strip_model();
101 DCHECK(tab_strip_model);
102
103 for (int i = 0; i < tab_strip_model->count(); i++) {
104 // Create id for tab.
105 content::WebContents* contents = tab_strip_model->GetWebContentsAt(i);
106 DCHECK(contents);
107 content::RenderFrameHost* main_frame = contents->GetMainFrame();
108 DCHECK(main_frame);
109 DesktopMediaID media_id(
110 DesktopMediaID::TYPE_WEB_CONTENTS, DesktopMediaID::kNullId,
111 content::WebContentsMediaCaptureId(main_frame->GetProcess()->GetID(),
112 main_frame->GetRoutingID()));
113
114 // Get tab's last active time stamp.
115 const base::TimeTicks t = contents->GetLastActiveTime();
116 tab_map.insert(
117 std::make_pair(t, SourceDescription(media_id, contents->GetTitle())));
118
119 // Get favicon for tab.
120 favicon::FaviconDriver* favicon_driver =
121 favicon::ContentFaviconDriver::FromWebContents(contents);
122 if (!favicon_driver)
123 continue;
124
125 gfx::Image favicon = favicon_driver->GetFavicon();
126 if (favicon.IsEmpty())
127 continue;
128
129 // Only new or changed favicon need update.
130 new_favicon_hashes[media_id] = GetImageHash(favicon);
131 if (!favicon_hashes_.count(media_id) ||
132 (favicon_hashes_[media_id] != new_favicon_hashes[media_id])) {
133 gfx::ImageSkia image = favicon.AsImageSkia();
134 image.MakeThreadSafe();
135 favicon_pairs.push_back(std::make_pair(media_id, image));
136 }
137 }
138 }
139 favicon_hashes_ = new_favicon_hashes;
140
141 // Sort tab sources by time. Most recent one first. Then update sources list.
142 for (auto it = tab_map.rbegin(); it != tab_map.rend(); ++it)
143 sources.push_back(it->second);
144
145 UpdateSourcesList(sources);
146
147 for (const auto& it : favicon_pairs) {
148 // Create a thumbail in a different thread and update the thumbnail in
149 // current thread.
150 base::PostTaskAndReplyWithResult(
151 thumbnail_task_runner_.get(), FROM_HERE,
152 base::BindOnce(&CreateEnclosedFaviconImage, thumbnail_size_, it.second),
153 base::BindOnce(&TabDesktopMediaList::UpdateSourceThumbnail,
154 weak_factory_.GetWeakPtr(), it.first));
155 }
156
157 // OnRefreshComplete() needs to be called after all calls for
158 // UpdateSourceThumbnail() have done. Therefore, a DoNothing task is posted to
159 // the same sequenced task runner that CreateEnlargedFaviconImag() is posted.
160 thumbnail_task_runner_.get()->PostTaskAndReply(
161 FROM_HERE, base::DoNothing(),
162 base::BindOnce(&TabDesktopMediaList::OnRefreshComplete,
163 weak_factory_.GetWeakPtr()));
164 }
165