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