1 // Copyright (c) 2012 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/ui/ash/screenshot_taker.h" 6 7 #include <climits> 8 #include <string> 9 10 #include "ash/shell.h" 11 #include "ash/shell_delegate.h" 12 #include "ash/system/system_notifier.h" 13 #include "base/base64.h" 14 #include "base/bind.h" 15 #include "base/file_util.h" 16 #include "base/i18n/time_formatting.h" 17 #include "base/logging.h" 18 #include "base/memory/ref_counted_memory.h" 19 #include "base/prefs/pref_service.h" 20 #include "base/strings/stringprintf.h" 21 #include "base/strings/utf_string_conversions.h" 22 #include "base/threading/sequenced_worker_pool.h" 23 #include "base/time/time.h" 24 #include "chrome/browser/browser_process.h" 25 #include "chrome/browser/download/download_prefs.h" 26 #include "chrome/browser/notifications/notification_ui_manager.h" 27 #include "chrome/browser/profiles/profile.h" 28 #include "chrome/browser/profiles/profile_manager.h" 29 #include "chrome/common/pref_names.h" 30 #include "content/public/browser/browser_thread.h" 31 #include "content/public/browser/user_metrics.h" 32 #include "grit/ash_strings.h" 33 #include "grit/theme_resources.h" 34 #include "grit/ui_strings.h" 35 #include "ui/aura/window.h" 36 #include "ui/aura/window_event_dispatcher.h" 37 #include "ui/base/clipboard/clipboard.h" 38 #include "ui/base/clipboard/scoped_clipboard_writer.h" 39 #include "ui/base/l10n/l10n_util.h" 40 #include "ui/base/resource/resource_bundle.h" 41 #include "ui/gfx/image/image.h" 42 #include "ui/snapshot/snapshot.h" 43 44 #if defined(OS_CHROMEOS) 45 #include "chrome/browser/chromeos/drive/file_system_interface.h" 46 #include "chrome/browser/chromeos/drive/file_system_util.h" 47 #include "chrome/browser/chromeos/file_manager/open_util.h" 48 #include "chrome/browser/chromeos/login/users/user_manager.h" 49 #include "chrome/browser/notifications/desktop_notification_service.h" 50 #include "chrome/browser/notifications/desktop_notification_service_factory.h" 51 #include "chromeos/login/login_state.h" 52 #endif 53 54 namespace { 55 // The minimum interval between two screenshot commands. It has to be 56 // more than 1000 to prevent the conflict of filenames. 57 const int kScreenshotMinimumIntervalInMS = 1000; 58 59 const char kNotificationId[] = "screenshot"; 60 61 #if defined(OS_CHROMEOS) 62 const char kNotificationOriginUrl[] = "chrome://screenshot"; 63 #endif 64 65 const char kImageClipboardFormatPrefix[] = "<img src='data:image/png;base64,"; 66 const char kImageClipboardFormatSuffix[] = "'>"; 67 68 void CopyScreenshotToClipboard(scoped_refptr<base::RefCountedString> png_data) { 69 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 70 71 std::string encoded; 72 base::Base64Encode(png_data->data(), &encoded); 73 74 // Only cares about HTML because ChromeOS doesn't need other formats. 75 // TODO(dcheng): Why don't we take advantage of the ability to write bitmaps 76 // to the clipboard here? 77 { 78 ui::ScopedClipboardWriter scw(ui::Clipboard::GetForCurrentThread(), 79 ui::CLIPBOARD_TYPE_COPY_PASTE); 80 std::string html(kImageClipboardFormatPrefix); 81 html += encoded; 82 html += kImageClipboardFormatSuffix; 83 scw.WriteHTML(base::UTF8ToUTF16(html), std::string()); 84 } 85 content::RecordAction(base::UserMetricsAction("Screenshot_CopyClipboard")); 86 } 87 88 void ReadFileAndCopyToClipboardLocal(const base::FilePath& screenshot_path) { 89 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); 90 91 scoped_refptr<base::RefCountedString> png_data(new base::RefCountedString()); 92 if (!base::ReadFileToString(screenshot_path, &(png_data->data()))) { 93 LOG(ERROR) << "Failed to read the screenshot file: " 94 << screenshot_path.value(); 95 return; 96 } 97 98 content::BrowserThread::PostTask( 99 content::BrowserThread::UI, FROM_HERE, 100 base::Bind(CopyScreenshotToClipboard, png_data)); 101 } 102 103 #if defined(OS_CHROMEOS) 104 void ReadFileAndCopyToClipboardDrive(drive::FileError error, 105 const base::FilePath& file_path, 106 scoped_ptr<drive::ResourceEntry> entry) { 107 if (error != drive::FILE_ERROR_OK) { 108 LOG(ERROR) << "Failed to read the screenshot path on drive: " 109 << drive::FileErrorToString(error); 110 return; 111 } 112 content::BrowserThread::GetBlockingPool()->PostTask( 113 FROM_HERE, 114 base::Bind(&ReadFileAndCopyToClipboardLocal, file_path)); 115 } 116 #endif 117 118 // Delegate for a notification. This class has two roles: to implement callback 119 // methods for notification, and to provide an identity of the associated 120 // notification. 121 class ScreenshotTakerNotificationDelegate : public NotificationDelegate { 122 public: 123 ScreenshotTakerNotificationDelegate(bool success, 124 Profile* profile, 125 const base::FilePath& screenshot_path) 126 : success_(success), 127 profile_(profile), 128 screenshot_path_(screenshot_path) { 129 } 130 131 // Overridden from NotificationDelegate: 132 virtual void Display() OVERRIDE {} 133 virtual void Error() OVERRIDE {} 134 virtual void Close(bool by_user) OVERRIDE {} 135 virtual void Click() OVERRIDE { 136 if (!success_) 137 return; 138 #if defined(OS_CHROMEOS) 139 file_manager::util::ShowItemInFolder(profile_, screenshot_path_); 140 #else 141 // TODO(sschmitz): perhaps add similar action for Windows. 142 #endif 143 } 144 virtual void ButtonClick(int button_index) OVERRIDE { 145 DCHECK(success_ && button_index == 0); 146 147 // To avoid keeping the screenshot image on memory, it will re-read the 148 // screenshot file and copy it to the clipboard. 149 #if defined(OS_CHROMEOS) 150 if (drive::util::IsUnderDriveMountPoint(screenshot_path_)) { 151 drive::FileSystemInterface* file_system = 152 drive::util::GetFileSystemByProfile(profile_); 153 file_system->GetFile( 154 drive::util::ExtractDrivePath(screenshot_path_), 155 base::Bind(&ReadFileAndCopyToClipboardDrive)); 156 return; 157 } 158 #endif 159 content::BrowserThread::GetBlockingPool()->PostTask( 160 FROM_HERE, base::Bind( 161 &ReadFileAndCopyToClipboardLocal, screenshot_path_)); 162 } 163 virtual bool HasClickedListener() OVERRIDE { return success_; } 164 virtual std::string id() const OVERRIDE { 165 return std::string(kNotificationId); 166 } 167 virtual content::WebContents* GetWebContents() const OVERRIDE { 168 return NULL; 169 } 170 171 private: 172 virtual ~ScreenshotTakerNotificationDelegate() {} 173 174 const bool success_; 175 Profile* profile_; 176 const base::FilePath screenshot_path_; 177 178 DISALLOW_COPY_AND_ASSIGN(ScreenshotTakerNotificationDelegate); 179 }; 180 181 typedef base::Callback< 182 void(ScreenshotTakerObserver::Result screenshot_result, 183 const base::FilePath& screenshot_path)> ShowNotificationCallback; 184 185 void SaveScreenshotInternal(const ShowNotificationCallback& callback, 186 const base::FilePath& screenshot_path, 187 const base::FilePath& local_path, 188 scoped_refptr<base::RefCountedBytes> png_data) { 189 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); 190 DCHECK(!local_path.empty()); 191 ScreenshotTakerObserver::Result result = 192 ScreenshotTakerObserver::SCREENSHOT_SUCCESS; 193 if (static_cast<size_t>(base::WriteFile( 194 local_path, 195 reinterpret_cast<char*>(&(png_data->data()[0])), 196 png_data->size())) != png_data->size()) { 197 LOG(ERROR) << "Failed to save to " << local_path.value(); 198 result = ScreenshotTakerObserver::SCREENSHOT_WRITE_FILE_FAILED; 199 } 200 content::BrowserThread::PostTask( 201 content::BrowserThread::UI, FROM_HERE, 202 base::Bind(callback, result, screenshot_path)); 203 } 204 205 void SaveScreenshot(const ShowNotificationCallback& callback, 206 const base::FilePath& screenshot_path, 207 scoped_refptr<base::RefCountedBytes> png_data) { 208 DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); 209 DCHECK(!screenshot_path.empty()); 210 211 if (!base::CreateDirectory(screenshot_path.DirName())) { 212 LOG(ERROR) << "Failed to ensure the existence of " 213 << screenshot_path.DirName().value(); 214 content::BrowserThread::PostTask( 215 content::BrowserThread::UI, FROM_HERE, 216 base::Bind(callback, 217 ScreenshotTakerObserver::SCREENSHOT_CREATE_DIR_FAILED, 218 screenshot_path)); 219 return; 220 } 221 SaveScreenshotInternal(callback, screenshot_path, screenshot_path, png_data); 222 } 223 224 // TODO(kinaba): crbug.com/140425, remove this ungly #ifdef dispatch. 225 #if defined(OS_CHROMEOS) 226 void SaveScreenshotToDrive(const ShowNotificationCallback& callback, 227 const base::FilePath& screenshot_path, 228 scoped_refptr<base::RefCountedBytes> png_data, 229 drive::FileError error, 230 const base::FilePath& local_path) { 231 // |screenshot_path| is used in the notification callback. 232 // |local_path| is a temporary file in a hidden cache directory used for 233 // internal work generated by drive::util::PrepareWritableFileAndRun. 234 if (error != drive::FILE_ERROR_OK) { 235 LOG(ERROR) << "Failed to write screenshot image to Google Drive: " << error; 236 content::BrowserThread::PostTask( 237 content::BrowserThread::UI, FROM_HERE, 238 base::Bind(callback, 239 ScreenshotTakerObserver::SCREENSHOT_CREATE_FILE_FAILED, 240 screenshot_path)); 241 return; 242 } 243 SaveScreenshotInternal(callback, screenshot_path, local_path, png_data); 244 } 245 246 void EnsureDirectoryExistsCallback( 247 const ShowNotificationCallback& callback, 248 Profile* profile, 249 const base::FilePath& screenshot_path, 250 scoped_refptr<base::RefCountedBytes> png_data, 251 drive::FileError error) { 252 // It is okay to fail with FILE_ERROR_EXISTS since anyway the directory 253 // of the target file exists. 254 if (error == drive::FILE_ERROR_OK || 255 error == drive::FILE_ERROR_EXISTS) { 256 drive::util::PrepareWritableFileAndRun( 257 profile, 258 screenshot_path, 259 base::Bind(&SaveScreenshotToDrive, 260 callback, 261 screenshot_path, 262 png_data)); 263 } else { 264 LOG(ERROR) << "Failed to ensure the existence of the specified directory " 265 << "in Google Drive: " << error; 266 callback.Run(ScreenshotTakerObserver::SCREENSHOT_CHECK_DIR_FAILED, 267 screenshot_path); 268 } 269 } 270 271 void PostSaveScreenshotTask(const ShowNotificationCallback& callback, 272 Profile* profile, 273 const base::FilePath& screenshot_path, 274 scoped_refptr<base::RefCountedBytes> png_data) { 275 if (drive::util::IsUnderDriveMountPoint(screenshot_path)) { 276 drive::util::EnsureDirectoryExists( 277 profile, 278 screenshot_path.DirName(), 279 base::Bind(&EnsureDirectoryExistsCallback, 280 callback, 281 profile, 282 screenshot_path, 283 png_data)); 284 } else { 285 content::BrowserThread::GetBlockingPool()->PostTask( 286 FROM_HERE, base::Bind(&SaveScreenshot, 287 callback, 288 screenshot_path, 289 png_data)); 290 } 291 } 292 #else 293 void PostSaveScreenshotTask(const ShowNotificationCallback& callback, 294 Profile* profile, 295 const base::FilePath& screenshot_path, 296 scoped_refptr<base::RefCountedBytes> png_data) { 297 content::BrowserThread::GetBlockingPool()->PostTask( 298 FROM_HERE, base::Bind(&SaveScreenshot, 299 callback, 300 screenshot_path, 301 png_data)); 302 } 303 #endif 304 305 bool ShouldUse24HourClock() { 306 #if defined(OS_CHROMEOS) 307 Profile* profile = ProfileManager::GetActiveUserProfile(); 308 if (profile) { 309 return profile->GetPrefs()->GetBoolean(prefs::kUse24HourClock); 310 } 311 #endif 312 return base::GetHourClockType() == base::k24HourClock; 313 } 314 315 std::string GetScreenshotBaseFilename() { 316 base::Time::Exploded now; 317 base::Time::Now().LocalExplode(&now); 318 319 // We don't use base/i18n/time_formatting.h here because it doesn't 320 // support our format. Don't use ICU either to avoid i18n file names 321 // for non-English locales. 322 // TODO(mukai): integrate this logic somewhere time_formatting.h 323 std::string file_name = base::StringPrintf( 324 "Screenshot %d-%02d-%02d at ", now.year, now.month, now.day_of_month); 325 326 if (ShouldUse24HourClock()) { 327 file_name.append(base::StringPrintf( 328 "%02d.%02d.%02d", now.hour, now.minute, now.second)); 329 } else { 330 int hour = now.hour; 331 if (hour > 12) { 332 hour -= 12; 333 } else if (hour == 0) { 334 hour = 12; 335 } 336 file_name.append(base::StringPrintf( 337 "%d.%02d.%02d ", hour, now.minute, now.second)); 338 file_name.append((now.hour >= 12) ? "PM" : "AM"); 339 } 340 341 return file_name; 342 } 343 344 bool GetScreenshotDirectory(base::FilePath* directory) { 345 bool is_logged_in = true; 346 347 #if defined(OS_CHROMEOS) 348 is_logged_in = chromeos::LoginState::Get()->IsUserLoggedIn(); 349 #endif 350 351 if (is_logged_in) { 352 DownloadPrefs* download_prefs = DownloadPrefs::FromBrowserContext( 353 ProfileManager::GetActiveUserProfile()); 354 *directory = download_prefs->DownloadPath(); 355 } else { 356 if (!base::GetTempDir(directory)) { 357 LOG(ERROR) << "Failed to find temporary directory."; 358 return false; 359 } 360 } 361 return true; 362 } 363 364 #if defined(OS_CHROMEOS) 365 const int GetScreenshotNotificationTitle( 366 ScreenshotTakerObserver::Result screenshot_result) { 367 switch (screenshot_result) { 368 case ScreenshotTakerObserver::SCREENSHOTS_DISABLED: 369 return IDS_ASH_SCREENSHOT_NOTIFICATION_TITLE_DISABLED; 370 case ScreenshotTakerObserver::SCREENSHOT_SUCCESS: 371 return IDS_ASH_SCREENSHOT_NOTIFICATION_TITLE_SUCCESS; 372 default: 373 return IDS_ASH_SCREENSHOT_NOTIFICATION_TITLE_FAIL; 374 } 375 } 376 377 const int GetScreenshotNotificationText( 378 ScreenshotTakerObserver::Result screenshot_result) { 379 switch (screenshot_result) { 380 case ScreenshotTakerObserver::SCREENSHOTS_DISABLED: 381 return IDS_ASH_SCREENSHOT_NOTIFICATION_TEXT_DISABLED; 382 case ScreenshotTakerObserver::SCREENSHOT_SUCCESS: 383 return IDS_ASH_SCREENSHOT_NOTIFICATION_TEXT_SUCCESS; 384 default: 385 return IDS_ASH_SCREENSHOT_NOTIFICATION_TEXT_FAIL; 386 } 387 } 388 #endif 389 390 } // namespace 391 392 ScreenshotTaker::ScreenshotTaker() 393 : factory_(this), 394 profile_for_test_(NULL) { 395 } 396 397 ScreenshotTaker::~ScreenshotTaker() { 398 } 399 400 void ScreenshotTaker::HandleTakeScreenshotForAllRootWindows() { 401 if (g_browser_process->local_state()-> 402 GetBoolean(prefs::kDisableScreenshots)) { 403 ShowNotification(ScreenshotTakerObserver::SCREENSHOTS_DISABLED, 404 base::FilePath()); 405 return; 406 } 407 base::FilePath screenshot_directory; 408 if (!screenshot_directory_for_test_.empty()) { 409 screenshot_directory = screenshot_directory_for_test_; 410 } else if (!GetScreenshotDirectory(&screenshot_directory)) { 411 ShowNotification(ScreenshotTakerObserver::SCREENSHOT_GET_DIR_FAILED, 412 base::FilePath()); 413 return; 414 } 415 std::string screenshot_basename = !screenshot_basename_for_test_.empty() ? 416 screenshot_basename_for_test_ : GetScreenshotBaseFilename(); 417 418 aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows(); 419 // Reorder root_windows to take the primary root window's snapshot at first. 420 aura::Window* primary_root = ash::Shell::GetPrimaryRootWindow(); 421 if (*(root_windows.begin()) != primary_root) { 422 root_windows.erase(std::find( 423 root_windows.begin(), root_windows.end(), primary_root)); 424 root_windows.insert(root_windows.begin(), primary_root); 425 } 426 for (size_t i = 0; i < root_windows.size(); ++i) { 427 aura::Window* root_window = root_windows[i]; 428 std::string basename = screenshot_basename; 429 gfx::Rect rect = root_window->bounds(); 430 if (root_windows.size() > 1) 431 basename += base::StringPrintf(" - Display %d", static_cast<int>(i + 1)); 432 base::FilePath screenshot_path = 433 screenshot_directory.AppendASCII(basename + ".png"); 434 GrabFullWindowSnapshotAsync( 435 root_window, rect, GetProfile(), screenshot_path, i); 436 } 437 content::RecordAction(base::UserMetricsAction("Screenshot_TakeFull")); 438 } 439 440 void ScreenshotTaker::HandleTakePartialScreenshot( 441 aura::Window* window, const gfx::Rect& rect) { 442 if (g_browser_process->local_state()-> 443 GetBoolean(prefs::kDisableScreenshots)) { 444 ShowNotification(ScreenshotTakerObserver::SCREENSHOTS_DISABLED, 445 base::FilePath()); 446 return; 447 } 448 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 449 450 base::FilePath screenshot_directory; 451 if (!screenshot_directory_for_test_.empty()) { 452 screenshot_directory = screenshot_directory_for_test_; 453 } else if (!GetScreenshotDirectory(&screenshot_directory)) { 454 ShowNotification(ScreenshotTakerObserver::SCREENSHOT_GET_DIR_FAILED, 455 base::FilePath()); 456 return; 457 } 458 459 std::string screenshot_basename = !screenshot_basename_for_test_.empty() ? 460 screenshot_basename_for_test_ : GetScreenshotBaseFilename(); 461 base::FilePath screenshot_path = 462 screenshot_directory.AppendASCII(screenshot_basename + ".png"); 463 GrabPartialWindowSnapshotAsync(window, rect, GetProfile(), screenshot_path); 464 content::RecordAction(base::UserMetricsAction("Screenshot_TakePartial")); 465 } 466 467 bool ScreenshotTaker::CanTakeScreenshot() { 468 return last_screenshot_timestamp_.is_null() || 469 base::Time::Now() - last_screenshot_timestamp_ > 470 base::TimeDelta::FromMilliseconds( 471 kScreenshotMinimumIntervalInMS); 472 } 473 474 #if defined(OS_CHROMEOS) 475 Notification* ScreenshotTaker::CreateNotification( 476 ScreenshotTakerObserver::Result screenshot_result, 477 const base::FilePath& screenshot_path) { 478 const std::string notification_id(kNotificationId); 479 // We cancel a previous screenshot notification, if any, to ensure we get 480 // a fresh notification pop-up. 481 g_browser_process->notification_ui_manager()->CancelById(notification_id); 482 const base::string16 replace_id(base::UTF8ToUTF16(notification_id)); 483 bool success = 484 (screenshot_result == ScreenshotTakerObserver::SCREENSHOT_SUCCESS); 485 message_center::RichNotificationData optional_field; 486 if (success) { 487 const base::string16 label = l10n_util::GetStringUTF16( 488 IDS_MESSAGE_CENTER_NOTIFICATION_BUTTON_COPY_SCREENSHOT_TO_CLIPBOARD); 489 optional_field.buttons.push_back(message_center::ButtonInfo(label)); 490 } 491 return new Notification( 492 message_center::NOTIFICATION_TYPE_SIMPLE, 493 GURL(kNotificationOriginUrl), 494 l10n_util::GetStringUTF16( 495 GetScreenshotNotificationTitle(screenshot_result)), 496 l10n_util::GetStringUTF16( 497 GetScreenshotNotificationText(screenshot_result)), 498 ui::ResourceBundle::GetSharedInstance().GetImageNamed( 499 IDR_SCREENSHOT_NOTIFICATION_ICON), 500 blink::WebTextDirectionDefault, 501 message_center::NotifierId( 502 message_center::NotifierId::SYSTEM_COMPONENT, 503 ash::system_notifier::kNotifierScreenshot), 504 l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_NOTIFIER_SCREENSHOT_NAME), 505 replace_id, 506 optional_field, 507 new ScreenshotTakerNotificationDelegate( 508 success, GetProfile(), screenshot_path)); 509 } 510 #endif 511 512 void ScreenshotTaker::ShowNotification( 513 ScreenshotTakerObserver::Result screenshot_result, 514 const base::FilePath& screenshot_path) { 515 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 516 #if defined(OS_CHROMEOS) 517 // Do not show a notification that a screenshot was taken while no user is 518 // logged in, since it is confusing for the user to get a message about it 519 // after he logs in (crbug.com/235217). 520 if (!chromeos::LoginState::Get()->IsUserLoggedIn()) 521 return; 522 523 // TODO(sschmitz): make this work for Windows. 524 DesktopNotificationService* const service = 525 DesktopNotificationServiceFactory::GetForProfile(GetProfile()); 526 if (service->IsNotifierEnabled(message_center::NotifierId( 527 message_center::NotifierId::SYSTEM_COMPONENT, 528 ash::system_notifier::kNotifierScreenshot))) { 529 scoped_ptr<Notification> notification( 530 CreateNotification(screenshot_result, screenshot_path)); 531 g_browser_process->notification_ui_manager()->Add(*notification, 532 GetProfile()); 533 } 534 #endif 535 FOR_EACH_OBSERVER(ScreenshotTakerObserver, observers_, 536 OnScreenshotCompleted(screenshot_result, screenshot_path)); 537 } 538 539 void ScreenshotTaker::AddObserver(ScreenshotTakerObserver* observer) { 540 observers_.AddObserver(observer); 541 } 542 543 void ScreenshotTaker::RemoveObserver(ScreenshotTakerObserver* observer) { 544 observers_.RemoveObserver(observer); 545 } 546 547 bool ScreenshotTaker::HasObserver(ScreenshotTakerObserver* observer) const { 548 return observers_.HasObserver(observer); 549 } 550 551 void ScreenshotTaker::GrabWindowSnapshotAsyncCallback( 552 base::FilePath screenshot_path, 553 bool is_partial, 554 int window_idx, 555 scoped_refptr<base::RefCountedBytes> png_data) { 556 if (!png_data) { 557 if (is_partial) { 558 LOG(ERROR) << "Failed to grab the window screenshot"; 559 ShowNotification( 560 ScreenshotTakerObserver::SCREENSHOT_GRABWINDOW_PARTIAL_FAILED, 561 screenshot_path); 562 } else { 563 LOG(ERROR) << "Failed to grab the window screenshot for " << window_idx; 564 ShowNotification( 565 ScreenshotTakerObserver::SCREENSHOT_GRABWINDOW_FULL_FAILED, 566 screenshot_path); 567 } 568 return; 569 } 570 571 PostSaveScreenshotTask( 572 base::Bind(&ScreenshotTaker::ShowNotification, factory_.GetWeakPtr()), 573 GetProfile(), 574 screenshot_path, 575 png_data); 576 } 577 578 void ScreenshotTaker::GrabPartialWindowSnapshotAsync( 579 aura::Window* window, 580 const gfx::Rect& snapshot_bounds, 581 Profile* profile, 582 base::FilePath screenshot_path) { 583 last_screenshot_timestamp_ = base::Time::Now(); 584 585 bool is_partial = true; 586 int window_idx = -1; // unused 587 ui::GrabWindowSnapshotAsync( 588 window, 589 snapshot_bounds, 590 content::BrowserThread::GetBlockingPool(), 591 base::Bind(&ScreenshotTaker::GrabWindowSnapshotAsyncCallback, 592 factory_.GetWeakPtr(), 593 screenshot_path, 594 is_partial, 595 window_idx)); 596 } 597 598 void ScreenshotTaker::GrabFullWindowSnapshotAsync( 599 aura::Window* window, 600 const gfx::Rect& snapshot_bounds, 601 Profile* profile, 602 base::FilePath screenshot_path, 603 int window_idx) { 604 last_screenshot_timestamp_ = base::Time::Now(); 605 606 bool is_partial = false; 607 ui::GrabWindowSnapshotAsync( 608 window, 609 snapshot_bounds, 610 content::BrowserThread::GetBlockingPool(), 611 base::Bind(&ScreenshotTaker::GrabWindowSnapshotAsyncCallback, 612 factory_.GetWeakPtr(), 613 screenshot_path, 614 is_partial, 615 window_idx)); 616 } 617 618 Profile* ScreenshotTaker::GetProfile() { 619 if (profile_for_test_) 620 return profile_for_test_; 621 return ProfileManager::GetActiveUserProfile(); 622 } 623 624 void ScreenshotTaker::SetScreenshotDirectoryForTest( 625 const base::FilePath& directory) { 626 screenshot_directory_for_test_ = directory; 627 } 628 629 void ScreenshotTaker::SetScreenshotBasenameForTest( 630 const std::string& basename) { 631 screenshot_basename_for_test_ = basename; 632 } 633 634 void ScreenshotTaker::SetScreenshotProfileForTest(Profile* profile) { 635 profile_for_test_ = profile; 636 } 637