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