Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(631)

Side by Side Diff: chrome/browser/media/native_desktop_media_list.cc

Issue 1503563004: Desktop chrome tab capture-chooseDesktopMedia() (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/media/native_desktop_media_list.h" 5 #include "chrome/browser/media/native_desktop_media_list.h"
6 6
7 #include <map> 7 #include <map>
8 #include <set> 8 #include <set>
9 #include <sstream> 9 #include <sstream>
10 10
11 #include "base/hash.h" 11 #include "base/hash.h"
12 #include "base/logging.h" 12 #include "base/logging.h"
13 #include "base/strings/utf_string_conversions.h" 13 #include "base/strings/utf_string_conversions.h"
14 #include "base/threading/sequenced_worker_pool.h" 14 #include "base/threading/sequenced_worker_pool.h"
15 #include "chrome/browser/mac/bluetooth_utility.h"
15 #include "chrome/browser/media/desktop_media_list_observer.h" 16 #include "chrome/browser/media/desktop_media_list_observer.h"
17 #include "chrome/browser/profiles/profile_manager.h"
18 #include "chrome/browser/ui/browser_finder.h"
19 #include "chrome/browser/ui/host_desktop.h"
20 #include "chrome/browser/ui/tabs/tab_strip_model.h"
16 #include "chrome/grit/generated_resources.h" 21 #include "chrome/grit/generated_resources.h"
22 #include "components/favicon/content/content_favicon_driver.h"
17 #include "content/public/browser/browser_thread.h" 23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/browser/render_frame_host.h"
25 #include "content/public/browser/render_process_host.h"
18 #include "media/base/video_util.h" 26 #include "media/base/video_util.h"
19 #include "third_party/libyuv/include/libyuv/scale_argb.h" 27 #include "third_party/libyuv/include/libyuv/scale_argb.h"
20 #include "third_party/skia/include/core/SkBitmap.h" 28 #include "third_party/skia/include/core/SkBitmap.h"
21 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" 29 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
22 #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" 30 #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h"
23 #include "third_party/webrtc/modules/desktop_capture/window_capturer.h" 31 #include "third_party/webrtc/modules/desktop_capture/window_capturer.h"
32 #include "ui/aura/window.h"
24 #include "ui/base/l10n/l10n_util.h" 33 #include "ui/base/l10n/l10n_util.h"
25 #include "ui/gfx/skia_util.h" 34 #include "ui/gfx/skia_util.h"
35 #include "ui/snapshot/snapshot.h"
26 36
27 using content::BrowserThread; 37 using content::BrowserThread;
28 using content::DesktopMediaID; 38 using content::DesktopMediaID;
29 39
30 namespace { 40 namespace {
31 41
32 // Update the list every second. 42 // Update the list every second.
33 const int kDefaultUpdatePeriod = 1000; 43 const int kDefaultUpdatePeriod = 1000;
34 44
35 // Returns a hash of a DesktopFrame content to detect when image for a desktop 45 // Returns a hash of a DesktopFrame content to detect when image for a desktop
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
71 result.unlockPixels(); 81 result.unlockPixels();
72 82
73 return gfx::ImageSkia::CreateFrom1xBitmap(result); 83 return gfx::ImageSkia::CreateFrom1xBitmap(result);
74 } 84 }
75 85
76 } // namespace 86 } // namespace
77 87
78 NativeDesktopMediaList::SourceDescription::SourceDescription( 88 NativeDesktopMediaList::SourceDescription::SourceDescription(
79 DesktopMediaID id, 89 DesktopMediaID id,
80 const base::string16& name) 90 const base::string16& name)
81 : id(id), 91 : id(id), name(name) {}
82 name(name) {
83 }
84 92
85 class NativeDesktopMediaList::Worker 93 class NativeDesktopMediaList::Worker
86 : public webrtc::DesktopCapturer::Callback { 94 : public webrtc::DesktopCapturer::Callback {
87 public: 95 public:
88 Worker(base::WeakPtr<NativeDesktopMediaList> media_list, 96 Worker(base::WeakPtr<NativeDesktopMediaList> media_list,
89 scoped_ptr<webrtc::ScreenCapturer> screen_capturer, 97 scoped_ptr<webrtc::ScreenCapturer> screen_capturer,
90 scoped_ptr<webrtc::WindowCapturer> window_capturer); 98 scoped_ptr<webrtc::WindowCapturer> window_capturer);
91 ~Worker() override; 99 ~Worker() override;
92 100
93 void Refresh(const gfx::Size& thumbnail_size, 101 void Refresh(const gfx::Size& thumbnail_size,
94 content::DesktopMediaID::Id view_dialog_id); 102 content::DesktopMediaID::Id view_dialog_id,
103 std::vector<SourceDescription> tab_sources);
95 104
96 private: 105 private:
97 typedef std::map<DesktopMediaID, uint32> ImageHashesMap; 106 typedef std::map<DesktopMediaID, uint32> ImageHashesMap;
98 107
99 // webrtc::DesktopCapturer::Callback interface. 108 // webrtc::DesktopCapturer::Callback interface.
100 webrtc::SharedMemory* CreateSharedMemory(size_t size) override; 109 webrtc::SharedMemory* CreateSharedMemory(size_t size) override;
101 void OnCaptureCompleted(webrtc::DesktopFrame* frame) override; 110 void OnCaptureCompleted(webrtc::DesktopFrame* frame) override;
102 111
103 base::WeakPtr<NativeDesktopMediaList> media_list_; 112 base::WeakPtr<NativeDesktopMediaList> media_list_;
104 113
(...skipping 17 matching lines...) Expand all
122 if (screen_capturer_) 131 if (screen_capturer_)
123 screen_capturer_->Start(this); 132 screen_capturer_->Start(this);
124 if (window_capturer_) 133 if (window_capturer_)
125 window_capturer_->Start(this); 134 window_capturer_->Start(this);
126 } 135 }
127 136
128 NativeDesktopMediaList::Worker::~Worker() {} 137 NativeDesktopMediaList::Worker::~Worker() {}
129 138
130 void NativeDesktopMediaList::Worker::Refresh( 139 void NativeDesktopMediaList::Worker::Refresh(
131 const gfx::Size& thumbnail_size, 140 const gfx::Size& thumbnail_size,
132 content::DesktopMediaID::Id view_dialog_id) { 141 content::DesktopMediaID::Id view_dialog_id,
142 std::vector<SourceDescription> tab_sources) {
133 std::vector<SourceDescription> sources; 143 std::vector<SourceDescription> sources;
134 144
135 if (screen_capturer_) { 145 if (screen_capturer_) {
136 webrtc::ScreenCapturer::ScreenList screens; 146 webrtc::ScreenCapturer::ScreenList screens;
137 if (screen_capturer_->GetScreenList(&screens)) { 147 if (screen_capturer_->GetScreenList(&screens)) {
138 bool mutiple_screens = screens.size() > 1; 148 bool mutiple_screens = screens.size() > 1;
139 base::string16 title; 149 base::string16 title;
140 for (size_t i = 0; i < screens.size(); ++i) { 150 for (size_t i = 0; i < screens.size(); ++i) {
141 if (mutiple_screens) { 151 if (mutiple_screens) {
142 title = l10n_util::GetStringFUTF16Int( 152 title = l10n_util::GetStringFUTF16Int(
(...skipping 16 matching lines...) Expand all
159 it != windows.end(); ++it) { 169 it != windows.end(); ++it) {
160 // Skip the picker dialog window. 170 // Skip the picker dialog window.
161 if (it->id != view_dialog_id) { 171 if (it->id != view_dialog_id) {
162 sources.push_back(SourceDescription( 172 sources.push_back(SourceDescription(
163 DesktopMediaID(DesktopMediaID::TYPE_WINDOW, it->id), 173 DesktopMediaID(DesktopMediaID::TYPE_WINDOW, it->id),
164 base::UTF8ToUTF16(it->title))); 174 base::UTF8ToUTF16(it->title)));
165 } 175 }
166 } 176 }
167 } 177 }
168 } 178 }
179
180 // Add tab sources to all media sources
181 sources.insert(sources.end(), tab_sources.begin(), tab_sources.end());
182
169 // Update list of windows before updating thumbnails. 183 // Update list of windows before updating thumbnails.
170 BrowserThread::PostTask( 184 BrowserThread::PostTask(
171 BrowserThread::UI, FROM_HERE, 185 BrowserThread::UI, FROM_HERE,
172 base::Bind(&NativeDesktopMediaList::OnSourcesList, 186 base::Bind(&NativeDesktopMediaList::OnSourcesList,
173 media_list_, sources)); 187 media_list_, sources));
174 188
175 ImageHashesMap new_image_hashes; 189 ImageHashesMap new_image_hashes;
176 190
177 // Get a thumbnail for each source. 191 // Get a thumbnail for each source.
178 for (size_t i = 0; i < sources.size(); ++i) { 192 for (size_t i = 0; i < sources.size(); ++i) {
179 SourceDescription& source = sources[i]; 193 SourceDescription& source = sources[i];
180 switch (source.id.type) { 194 switch (source.id.type) {
181 case DesktopMediaID::TYPE_SCREEN: 195 case DesktopMediaID::TYPE_SCREEN:
182 if (!screen_capturer_->SelectScreen(source.id.id)) 196 if (!screen_capturer_->SelectScreen(source.id.id))
183 continue; 197 continue;
184 screen_capturer_->Capture(webrtc::DesktopRegion()); 198 screen_capturer_->Capture(webrtc::DesktopRegion());
185 break; 199 break;
186 200
187 case DesktopMediaID::TYPE_WINDOW: 201 case DesktopMediaID::TYPE_WINDOW:
188 if (!window_capturer_->SelectWindow(source.id.id)) 202 if (!window_capturer_->SelectWindow(source.id.id))
189 continue; 203 continue;
190 window_capturer_->Capture(webrtc::DesktopRegion()); 204 window_capturer_->Capture(webrtc::DesktopRegion());
191 break; 205 break;
206 case DesktopMediaID::TYPE_TAB:
207 // Favicon has already captured for tabs.
qiangchen 2015/12/07 22:45:56 Consider moving the Favicon "capturing" here.
miu 2015/12/08 01:54:38 IIUC, I think that has to be done on the UI thread
GeorgeZ 2015/12/09 19:36:37 This is a no UI thread. Favicon capture has to to
208 break;
192 209
193 default: 210 default:
194 NOTREACHED(); 211 NOTREACHED();
195 } 212 }
196 213
214 // Use captured tab favicon as thumbnail.
215 if (source.id.type == DesktopMediaID::TYPE_TAB) {
216 BrowserThread::PostTask(
217 BrowserThread::UI, FROM_HERE,
218 base::Bind(&NativeDesktopMediaList::OnSourceTabThumbnail, media_list_,
219 source));
220 continue;
221 }
222
197 // Expect that DesktopCapturer to always captures frames synchronously. 223 // Expect that DesktopCapturer to always captures frames synchronously.
198 // |current_frame_| may be NULL if capture failed (e.g. because window has 224 // |current_frame_| may be NULL if capture failed (e.g. because window has
199 // been closed). 225 // been closed).
200 if (current_frame_) { 226 if (current_frame_) {
201 uint32 frame_hash = GetFrameHash(current_frame_.get()); 227 uint32 frame_hash = GetFrameHash(current_frame_.get());
202 new_image_hashes[source.id] = frame_hash; 228 new_image_hashes[source.id] = frame_hash;
203 229
204 // Scale the image only if it has changed. 230 // Scale the image only if it has changed.
205 ImageHashesMap::iterator it = image_hashes_.find(source.id); 231 ImageHashesMap::iterator it = image_hashes_.find(source.id);
206 if (it == image_hashes_.end() || it->second != frame_hash) { 232 if (it == image_hashes_.end() || it->second != frame_hash) {
207 gfx::ImageSkia thumbnail = 233 gfx::ImageSkia thumbnail =
208 ScaleDesktopFrame(current_frame_.Pass(), thumbnail_size); 234 ScaleDesktopFrame(current_frame_.Pass(), thumbnail_size);
209 BrowserThread::PostTask( 235 BrowserThread::PostTask(
210 BrowserThread::UI, FROM_HERE, 236 BrowserThread::UI, FROM_HERE,
211 base::Bind(&NativeDesktopMediaList::OnSourceThumbnail, 237 base::Bind(&NativeDesktopMediaList::OnSourceThumbnail, media_list_,
212 media_list_, i, thumbnail)); 238 i, thumbnail));
213 } 239 }
214 } 240 }
215 } 241 }
216 242
217 image_hashes_.swap(new_image_hashes); 243 image_hashes_.swap(new_image_hashes);
218 244
219 BrowserThread::PostTask( 245 BrowserThread::PostTask(
220 BrowserThread::UI, FROM_HERE, 246 BrowserThread::UI, FROM_HERE,
221 base::Bind(&NativeDesktopMediaList::OnRefreshFinished, media_list_)); 247 base::Bind(&NativeDesktopMediaList::OnRefreshFinished, media_list_));
222 } 248 }
223 249
224 webrtc::SharedMemory* NativeDesktopMediaList::Worker::CreateSharedMemory( 250 webrtc::SharedMemory* NativeDesktopMediaList::Worker::CreateSharedMemory(
225 size_t size) { 251 size_t size) {
226 return NULL; 252 return NULL;
227 } 253 }
228 254
229 void NativeDesktopMediaList::Worker::OnCaptureCompleted( 255 void NativeDesktopMediaList::Worker::OnCaptureCompleted(
230 webrtc::DesktopFrame* frame) { 256 webrtc::DesktopFrame* frame) {
231 current_frame_.reset(frame); 257 current_frame_.reset(frame);
232 } 258 }
233 259
234 NativeDesktopMediaList::NativeDesktopMediaList( 260 NativeDesktopMediaList::NativeDesktopMediaList(
235 scoped_ptr<webrtc::ScreenCapturer> screen_capturer, 261 scoped_ptr<webrtc::ScreenCapturer> screen_capturer,
236 scoped_ptr<webrtc::WindowCapturer> window_capturer) 262 scoped_ptr<webrtc::WindowCapturer> window_capturer,
263 bool tab_capture_enabled)
237 : screen_capturer_(screen_capturer.Pass()), 264 : screen_capturer_(screen_capturer.Pass()),
238 window_capturer_(window_capturer.Pass()), 265 window_capturer_(window_capturer.Pass()),
266 tab_capture_enabled_(tab_capture_enabled),
239 update_period_(base::TimeDelta::FromMilliseconds(kDefaultUpdatePeriod)), 267 update_period_(base::TimeDelta::FromMilliseconds(kDefaultUpdatePeriod)),
240 thumbnail_size_(100, 100), 268 thumbnail_size_(100, 100),
241 view_dialog_id_(content::DesktopMediaID::TYPE_NONE, -1), 269 view_dialog_id_(content::DesktopMediaID::TYPE_NONE, -1),
242 observer_(NULL), 270 observer_(NULL),
243 weak_factory_(this) { 271 weak_factory_(this) {
244 base::SequencedWorkerPool* worker_pool = BrowserThread::GetBlockingPool(); 272 base::SequencedWorkerPool* worker_pool = BrowserThread::GetBlockingPool();
245 capture_task_runner_ = worker_pool->GetSequencedTaskRunner( 273 capture_task_runner_ = worker_pool->GetSequencedTaskRunner(
246 worker_pool->GetSequenceToken()); 274 worker_pool->GetSequenceToken());
247 } 275 }
248 276
(...skipping 11 matching lines...) Expand all
260 thumbnail_size_ = thumbnail_size; 288 thumbnail_size_ = thumbnail_size;
261 } 289 }
262 290
263 void NativeDesktopMediaList::SetViewDialogWindowId( 291 void NativeDesktopMediaList::SetViewDialogWindowId(
264 content::DesktopMediaID dialog_id) { 292 content::DesktopMediaID dialog_id) {
265 view_dialog_id_ = dialog_id; 293 view_dialog_id_ = dialog_id;
266 } 294 }
267 295
268 void NativeDesktopMediaList::StartUpdating(DesktopMediaListObserver* observer) { 296 void NativeDesktopMediaList::StartUpdating(DesktopMediaListObserver* observer) {
269 DCHECK(!observer_); 297 DCHECK(!observer_);
270 DCHECK(screen_capturer_ || window_capturer_); 298 DCHECK(screen_capturer_ || window_capturer_ || tab_capture_enabled_);
271 299
272 observer_ = observer; 300 observer_ = observer;
273 301
274 worker_.reset(new Worker(weak_factory_.GetWeakPtr(), 302 worker_.reset(new Worker(weak_factory_.GetWeakPtr(),
275 screen_capturer_.Pass(), window_capturer_.Pass())); 303 screen_capturer_.Pass(), window_capturer_.Pass()));
276 Refresh(); 304 Refresh();
277 } 305 }
278 306
279 int NativeDesktopMediaList::GetSourceCount() const { 307 int NativeDesktopMediaList::GetSourceCount() const {
280 return sources_.size(); 308 return sources_.size();
281 } 309 }
282 310
283 const DesktopMediaList::Source& NativeDesktopMediaList::GetSource( 311 const DesktopMediaList::Source& NativeDesktopMediaList::GetSource(
284 int index) const { 312 int index) const {
285 return sources_[index]; 313 return sources_[index];
286 } 314 }
287 315
288 void NativeDesktopMediaList::Refresh() { 316 void NativeDesktopMediaList::Refresh() {
317 // Get tab source list
qiangchen 2015/12/07 22:45:56 Consider moving this block to Worker::Refresh.
GeorgeZ 2015/12/09 19:36:37 Many work has to be done in UI thread here. worker
318 std::vector<SourceDescription> tab_sources;
319 if (tab_capture_enabled_) {
320 if (!GetTabSourceDescription(tab_sources)) {
321 tab_sources.clear();
322 LOG(ERROR) << "Failed to get tab sources description";
miu 2015/12/08 01:54:38 Please remove this logging statement. This operat
GeorgeZ 2015/12/09 19:36:37 Done.
323 }
324 }
325
289 capture_task_runner_->PostTask( 326 capture_task_runner_->PostTask(
290 FROM_HERE, base::Bind(&Worker::Refresh, base::Unretained(worker_.get()), 327 FROM_HERE, base::Bind(&Worker::Refresh, base::Unretained(worker_.get()),
291 thumbnail_size_, view_dialog_id_.id)); 328 thumbnail_size_, view_dialog_id_.id, tab_sources));
329 }
330
331 bool NativeDesktopMediaList::GetTabSourceDescription(
qiangchen 2015/12/07 22:45:56 GetTabSourceDescriptions is better.
miu 2015/12/08 01:54:38 This method should not return anything because it
GeorgeZ 2015/12/09 19:36:37 Done.
GeorgeZ 2015/12/09 19:36:37 Done.
332 std::vector<SourceDescription>& tab_sources) {
333 // Only support mac, linux, windows and chrome OS, the last three use aura.
334 #if !defined(OS_MACOSX) && !defined(USE_AURA)
335 return true;
336 #endif
337 // Clear old favicon map.
338 tab_icon_map_.clear();
339
340 // Use to sort tabs based on their last active time stamps.
341 std::map<base::TimeTicks, SourceDescription> tab_map;
342
343 Profile* profile = ProfileManager::GetLastUsedProfileAllowedByPolicy();
344 std::vector<Browser*> browsers = FindAllTabbedBrowsersWithProfile(
345 profile, chrome::HOST_DESKTOP_TYPE_NATIVE);
346
347 for (auto browser : browsers) {
348 TabStripModel* tab_strip_model = browser->tab_strip_model();
349 DCHECK(tab_strip_model);
350
351 for (int i = 0; i < tab_strip_model->count(); i++) {
352 // Get DesktopMediaID.
353 content::WebContents* contents = tab_strip_model->GetWebContentsAt(i);
354 DesktopMediaID media_id;
355 media_id.type = DesktopMediaID::TYPE_TAB;
356 content::RenderFrameHost* const main_frame = contents->GetMainFrame();
357 DCHECK(main_frame);
358 media_id.render_process_id = main_frame->GetProcess()->GetID();
359 media_id.main_render_frame_id = main_frame->GetRoutingID();
360
361 // Get tab title.
362 base::string16 title =
363 base::UTF8ToUTF16("Chrome tab_") + contents->GetTitle();
miu 2015/12/08 01:54:38 ditto: Make this a string resource.
GeorgeZ 2015/12/09 19:36:37 Done.
364
365 // Get tab favicon.
366 favicon::FaviconDriver* favicon_driver =
367 favicon::ContentFaviconDriver::FromWebContents(contents);
368 if (favicon_driver) {
369 gfx::Image tab_icon = favicon_driver->GetFavicon();
370 int window_id =
371 (media_id.main_render_frame_id << 12) + media_id.render_process_id;
372 tab_icon_map_[window_id] =
373 CreateImageEncloseFavicon(thumbnail_size_, tab_icon);
374 }
375
376 // Get tab's last active time stamp.
377 base::TimeTicks t = contents->GetLastActiveTime();
378 tab_map.insert(std::make_pair(t, SourceDescription(media_id, title)));
379 }
380 }
381
382 // Add timely sorted tab sources into vector. Most recent one first.
383 tab_sources.clear();
384 for (auto it = tab_map.rbegin(); it != tab_map.rend(); ++it) {
385 tab_sources.push_back(it->second);
386 }
387
388 return true;
292 } 389 }
293 390
294 void NativeDesktopMediaList::OnSourcesList( 391 void NativeDesktopMediaList::OnSourcesList(
295 const std::vector<SourceDescription>& new_sources) { 392 const std::vector<SourceDescription>& new_sources) {
296 typedef std::set<content::DesktopMediaID> SourceSet; 393 typedef std::set<content::DesktopMediaID> SourceSet;
297 SourceSet new_source_set; 394 SourceSet new_source_set;
298 for (size_t i = 0; i < new_sources.size(); ++i) { 395 for (size_t i = 0; i < new_sources.size(); ++i) {
299 new_source_set.insert(new_sources[i].id); 396 new_source_set.insert(new_sources[i].id);
300 } 397 }
301 // Iterate through the old sources to find the removed sources. 398 // Iterate through the old sources to find the removed sources.
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
354 } 451 }
355 452
356 void NativeDesktopMediaList::OnSourceThumbnail( 453 void NativeDesktopMediaList::OnSourceThumbnail(
357 int index, 454 int index,
358 const gfx::ImageSkia& image) { 455 const gfx::ImageSkia& image) {
359 DCHECK_LT(index, static_cast<int>(sources_.size())); 456 DCHECK_LT(index, static_cast<int>(sources_.size()));
360 sources_[index].thumbnail = image; 457 sources_[index].thumbnail = image;
361 observer_->OnSourceThumbnailChanged(index); 458 observer_->OnSourceThumbnailChanged(index);
362 } 459 }
363 460
461 void NativeDesktopMediaList::OnSourceTabThumbnail(SourceDescription source) {
qiangchen 2015/12/07 22:45:56 Is it possible to remove this function, and merge
GeorgeZ 2015/12/09 19:36:37 Favicon is capture and stored in a UI thead. While
462 DCHECK(source.id.type == DesktopMediaID::TYPE_TAB);
463
464 for (size_t i = 0; i < sources_.size(); ++i) {
465 if (sources_[i].id == source.id) {
466 int window_id =
467 (source.id.main_render_frame_id << 12) + source.id.render_process_id;
468 // Use favicon as thumbnail for tab.
469 if (tab_icon_map_.count(window_id)) {
470 // Only do update when there is a change to avoid flickering.
471 if (sources_[i].thumbnail.size() != tab_icon_map_[window_id].size() ||
472 sources_[i].name != source.name) {
473 sources_[i].thumbnail = tab_icon_map_[window_id];
474 observer_->OnSourceThumbnailChanged(i);
475 }
476 }
477 break;
478 }
479 }
480 }
481
364 void NativeDesktopMediaList::OnRefreshFinished() { 482 void NativeDesktopMediaList::OnRefreshFinished() {
365 BrowserThread::PostDelayedTask( 483 BrowserThread::PostDelayedTask(
366 BrowserThread::UI, FROM_HERE, 484 BrowserThread::UI, FROM_HERE,
367 base::Bind(&NativeDesktopMediaList::Refresh, 485 base::Bind(&NativeDesktopMediaList::Refresh,
368 weak_factory_.GetWeakPtr()), 486 weak_factory_.GetWeakPtr()),
369 update_period_); 487 update_period_);
370 } 488 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698