| Index: chrome/browser/ui/ash/screenshot_taker.cc
|
| diff --git a/chrome/browser/ui/ash/screenshot_taker.cc b/chrome/browser/ui/ash/screenshot_taker.cc
|
| index e5742f9dd8a57d100c2a75de28dde8fcefa44d0c..92487b4248c78a430aba1b1e19ab888d8fa093a8 100644
|
| --- a/chrome/browser/ui/ash/screenshot_taker.cc
|
| +++ b/chrome/browser/ui/ash/screenshot_taker.cc
|
| @@ -8,56 +8,109 @@
|
| #include <string>
|
|
|
| #include "ash/shell.h"
|
| -#include "ash/shell_delegate.h"
|
| -#include "ash/shell_window_ids.h"
|
| #include "base/bind.h"
|
| #include "base/file_util.h"
|
| -#include "base/files/file_path.h"
|
| #include "base/logging.h"
|
| #include "base/memory/ref_counted_memory.h"
|
| #include "base/stringprintf.h"
|
| #include "base/threading/sequenced_worker_pool.h"
|
| #include "base/time.h"
|
| +#include "base/utf_string_conversions.h"
|
| +#include "chrome/browser/browser_process.h"
|
| #include "chrome/browser/download/download_prefs.h"
|
| +#include "chrome/browser/notifications/notification.h"
|
| +#include "chrome/browser/notifications/notification_ui_manager.h"
|
| #include "chrome/browser/profiles/profile.h"
|
| -#include "chrome/browser/profiles/profile_manager.h"
|
| #include "chrome/browser/ui/webui/screenshot_source.h"
|
| #include "chrome/browser/ui/window_snapshot/window_snapshot.h"
|
| #include "content/public/browser/browser_thread.h"
|
| +#include "grit/ash_strings.h"
|
| +#include "grit/theme_resources.h"
|
| #include "ui/aura/root_window.h"
|
| #include "ui/aura/window.h"
|
| +#include "ui/base/l10n/l10n_util.h"
|
| +#include "ui/base/resource/resource_bundle.h"
|
| +#include "ui/gfx/image/image.h"
|
|
|
| #if defined(OS_CHROMEOS)
|
| #include "chrome/browser/chromeos/drive/drive_file_system_util.h"
|
| +#include "chrome/browser/chromeos/extensions/file_manager_util.h"
|
| #include "chrome/browser/chromeos/login/user_manager.h"
|
| #endif
|
|
|
| namespace {
|
| -// How opaque should the layer that we flash onscreen to provide visual
|
| -// feedback after the screenshot is taken be?
|
| -const float kVisualFeedbackLayerOpacity = 0.25f;
|
| -
|
| -// How long should the visual feedback layer be displayed?
|
| -const int64 kVisualFeedbackLayerDisplayTimeMs = 100;
|
| -
|
| // The minimum interval between two screenshot commands. It has to be
|
| // more than 1000 to prevent the conflict of filenames.
|
| const int kScreenshotMinimumIntervalInMS = 1000;
|
|
|
| +const char kNotificationOriginUrl[] = "chrome://screenshot";
|
|
|
| -void SaveScreenshotInternal(const base::FilePath& screenshot_path,
|
| +// Delegate for a notification. This class has two roles: to implement callback
|
| +// methods for notification, and to provide an identity of the associated
|
| +// notification.
|
| +class ScreenshotTakerNotificationDelegate : public NotificationDelegate {
|
| + public:
|
| + ScreenshotTakerNotificationDelegate(const std::string& id_text,
|
| + bool success,
|
| + const base::FilePath& screenshot_path)
|
| + : id_text_(id_text),
|
| + success_(success),
|
| + screenshot_path_(screenshot_path) {
|
| + }
|
| +
|
| + // Overridden from NotificationDelegate:
|
| + virtual void Display() OVERRIDE {}
|
| + virtual void Error() OVERRIDE {}
|
| + virtual void Close(bool by_user) OVERRIDE {}
|
| + virtual void Click() OVERRIDE {
|
| + if (!success_)
|
| + return;
|
| +#if defined(OS_CHROMEOS)
|
| + file_manager_util::ShowFileInFolder(screenshot_path_);
|
| +#else
|
| + // TODO(sschmitz): perhaps add similar action for Windows.
|
| +#endif
|
| + }
|
| + virtual std::string id() const OVERRIDE { return id_text_; }
|
| + virtual content::RenderViewHost* GetRenderViewHost() const OVERRIDE {
|
| + return NULL;
|
| + }
|
| +
|
| + private:
|
| + virtual ~ScreenshotTakerNotificationDelegate() {}
|
| +
|
| + const std::string id_text_;
|
| + const bool success_;
|
| + const base::FilePath screenshot_path_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ScreenshotTakerNotificationDelegate);
|
| +};
|
| +
|
| +typedef base::Callback<
|
| + void(ScreenshotTakerObserver::Result screenshot_result,
|
| + const base::FilePath& screenshot_path)> ShowNotificationCallback;
|
| +
|
| +void SaveScreenshotInternal(const ShowNotificationCallback& callback,
|
| + const base::FilePath& screenshot_path,
|
| scoped_refptr<base::RefCountedBytes> png_data) {
|
| DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
|
| DCHECK(!screenshot_path.empty());
|
| + ScreenshotTakerObserver::Result result =
|
| + ScreenshotTakerObserver::SCREENSHOT_SUCCESS;
|
| if (static_cast<size_t>(file_util::WriteFile(
|
| screenshot_path,
|
| reinterpret_cast<char*>(&(png_data->data()[0])),
|
| png_data->size())) != png_data->size()) {
|
| LOG(ERROR) << "Failed to save to " << screenshot_path.value();
|
| + result = ScreenshotTakerObserver::SCREENSHOT_WRITE_FILE_FAILED;
|
| }
|
| + content::BrowserThread::PostTask(
|
| + content::BrowserThread::UI, FROM_HERE,
|
| + base::Bind(callback, result, screenshot_path));
|
| }
|
|
|
| -void SaveScreenshot(const base::FilePath& screenshot_path,
|
| +void SaveScreenshot(const ShowNotificationCallback& callback,
|
| + const base::FilePath& screenshot_path,
|
| scoped_refptr<base::RefCountedBytes> png_data) {
|
| DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
|
| DCHECK(!screenshot_path.empty());
|
| @@ -65,24 +118,36 @@ void SaveScreenshot(const base::FilePath& screenshot_path,
|
| if (!file_util::CreateDirectory(screenshot_path.DirName())) {
|
| LOG(ERROR) << "Failed to ensure the existence of "
|
| << screenshot_path.DirName().value();
|
| + content::BrowserThread::PostTask(
|
| + content::BrowserThread::UI, FROM_HERE,
|
| + base::Bind(callback,
|
| + ScreenshotTakerObserver::SCREENSHOT_CREATE_DIR_FAILED,
|
| + screenshot_path));
|
| return;
|
| }
|
| - SaveScreenshotInternal(screenshot_path, png_data);
|
| + SaveScreenshotInternal(callback, screenshot_path, png_data);
|
| }
|
|
|
| // TODO(kinaba): crbug.com/140425, remove this ungly #ifdef dispatch.
|
| #if defined(OS_CHROMEOS)
|
| -void SaveScreenshotToDrive(scoped_refptr<base::RefCountedBytes> png_data,
|
| +void SaveScreenshotToDrive(const ShowNotificationCallback& callback,
|
| + scoped_refptr<base::RefCountedBytes> png_data,
|
| drive::DriveFileError error,
|
| const base::FilePath& local_path) {
|
| if (error != drive::DRIVE_FILE_OK) {
|
| LOG(ERROR) << "Failed to write screenshot image to Google Drive: " << error;
|
| + content::BrowserThread::PostTask(
|
| + content::BrowserThread::UI, FROM_HERE,
|
| + base::Bind(callback,
|
| + ScreenshotTakerObserver::SCREENSHOT_CREATE_FILE_FAILED,
|
| + local_path));
|
| return;
|
| }
|
| - SaveScreenshotInternal(local_path, png_data);
|
| + SaveScreenshotInternal(callback, local_path, png_data);
|
| }
|
|
|
| void EnsureDirectoryExistsCallback(
|
| + const ShowNotificationCallback& callback,
|
| Profile* profile,
|
| const base::FilePath& screenshot_path,
|
| scoped_refptr<base::RefCountedBytes> png_data,
|
| @@ -94,36 +159,46 @@ void EnsureDirectoryExistsCallback(
|
| drive::util::PrepareWritableFileAndRun(
|
| profile,
|
| screenshot_path,
|
| - base::Bind(&SaveScreenshotToDrive, png_data));
|
| + base::Bind(&SaveScreenshotToDrive, callback, png_data));
|
| } else {
|
| LOG(ERROR) << "Failed to ensure the existence of the specified directory "
|
| << "in Google Drive: " << error;
|
| + callback.Run(ScreenshotTakerObserver::SCREENSHOT_CHECK_DIR_FAILED,
|
| + screenshot_path);
|
| }
|
| }
|
|
|
| -void PostSaveScreenshotTask(const base::FilePath& screenshot_path,
|
| +void PostSaveScreenshotTask(const ShowNotificationCallback& callback,
|
| + Profile* profile,
|
| + const base::FilePath& screenshot_path,
|
| scoped_refptr<base::RefCountedBytes> png_data) {
|
| if (drive::util::IsUnderDriveMountPoint(screenshot_path)) {
|
| - Profile* profile = ProfileManager::GetDefaultProfileOrOffTheRecord();
|
| - if (profile) {
|
| - drive::util::EnsureDirectoryExists(
|
| - profile,
|
| - screenshot_path.DirName(),
|
| - base::Bind(&EnsureDirectoryExistsCallback,
|
| - profile,
|
| - screenshot_path,
|
| - png_data));
|
| - }
|
| + drive::util::EnsureDirectoryExists(
|
| + profile,
|
| + screenshot_path.DirName(),
|
| + base::Bind(&EnsureDirectoryExistsCallback,
|
| + callback,
|
| + profile,
|
| + screenshot_path,
|
| + png_data));
|
| } else {
|
| content::BrowserThread::GetBlockingPool()->PostTask(
|
| - FROM_HERE, base::Bind(&SaveScreenshot, screenshot_path, png_data));
|
| + FROM_HERE, base::Bind(&SaveScreenshot,
|
| + callback,
|
| + screenshot_path,
|
| + png_data));
|
| }
|
| }
|
| #else
|
| -void PostSaveScreenshotTask(const base::FilePath& screenshot_path,
|
| +void PostSaveScreenshotTask(const ShowNotificationCallback& callback,
|
| + Profile* profile,
|
| + const base::FilePath& screenshot_path,
|
| scoped_refptr<base::RefCountedBytes> png_data) {
|
| content::BrowserThread::GetBlockingPool()->PostTask(
|
| - FROM_HERE, base::Bind(&SaveScreenshot, screenshot_path, png_data));
|
| + FROM_HERE, base::Bind(&SaveScreenshot,
|
| + callback,
|
| + screenshot_path,
|
| + png_data));
|
| }
|
| #endif
|
|
|
| @@ -148,7 +223,10 @@ bool GrabWindowSnapshot(aura::Window* window,
|
|
|
| } // namespace
|
|
|
| -ScreenshotTaker::ScreenshotTaker() {
|
| +ScreenshotTaker::ScreenshotTaker(Profile* profile)
|
| + : profile_(profile),
|
| + ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {
|
| + DCHECK(profile_);
|
| }
|
|
|
| ScreenshotTaker::~ScreenshotTaker() {
|
| @@ -156,11 +234,17 @@ ScreenshotTaker::~ScreenshotTaker() {
|
|
|
| void ScreenshotTaker::HandleTakeScreenshotForAllRootWindows() {
|
| base::FilePath screenshot_directory;
|
| - if (!ScreenshotSource::GetScreenshotDirectory(&screenshot_directory))
|
| + if (!screenshot_directory_for_test_.empty()) {
|
| + screenshot_directory = screenshot_directory_for_test_;
|
| + } else if (!ScreenshotSource::GetScreenshotDirectory(&screenshot_directory)) {
|
| + ShowNotification(ScreenshotTakerObserver::SCREENSHOT_GET_DIR_FAILED,
|
| + base::FilePath());
|
| return;
|
| -
|
| - std::string screenshot_basename =
|
| + }
|
| + std::string screenshot_basename = !screenshot_basename_for_test_.empty() ?
|
| + screenshot_basename_for_test_ :
|
| ScreenshotSource::GetScreenshotBaseFilename();
|
| +
|
| ash::Shell::RootWindowList root_windows = ash::Shell::GetAllRootWindows();
|
| // Reorder root_windows to take the primary root window's snapshot at first.
|
| aura::RootWindow* primary_root = ash::Shell::GetPrimaryRootWindow();
|
| @@ -176,12 +260,19 @@ void ScreenshotTaker::HandleTakeScreenshotForAllRootWindows() {
|
| gfx::Rect rect = root_window->bounds();
|
| if (root_windows.size() > 1)
|
| basename += base::StringPrintf(" - Display %d", static_cast<int>(i + 1));
|
| + base::FilePath screenshot_path =
|
| + screenshot_directory.AppendASCII(basename + ".png");
|
| if (GrabWindowSnapshot(root_window, rect, &png_data->data())) {
|
| - DisplayVisualFeedback(rect);
|
| PostSaveScreenshotTask(
|
| - screenshot_directory.AppendASCII(basename + ".png"), png_data);
|
| + base::Bind(&ScreenshotTaker::ShowNotification, factory_.GetWeakPtr()),
|
| + profile_,
|
| + screenshot_path,
|
| + png_data);
|
| } else {
|
| LOG(ERROR) << "Failed to grab the window screenshot for " << i;
|
| + ShowNotification(
|
| + ScreenshotTakerObserver::SCREENSHOT_GRABWINDOW_FULL_FAILED,
|
| + screenshot_path);
|
| }
|
| }
|
| last_screenshot_timestamp_ = base::Time::Now();
|
| @@ -192,20 +283,33 @@ void ScreenshotTaker::HandleTakePartialScreenshot(
|
| DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
|
|
|
| base::FilePath screenshot_directory;
|
| - if (!ScreenshotSource::GetScreenshotDirectory(&screenshot_directory))
|
| + if (!screenshot_directory_for_test_.empty()) {
|
| + screenshot_directory = screenshot_directory_for_test_;
|
| + } else if (!ScreenshotSource::GetScreenshotDirectory(&screenshot_directory)) {
|
| + ShowNotification(ScreenshotTakerObserver::SCREENSHOT_GET_DIR_FAILED,
|
| + base::FilePath());
|
| return;
|
| + }
|
|
|
| scoped_refptr<base::RefCountedBytes> png_data(new base::RefCountedBytes);
|
|
|
| + std::string screenshot_basename = !screenshot_basename_for_test_.empty() ?
|
| + screenshot_basename_for_test_ :
|
| + ScreenshotSource::GetScreenshotBaseFilename();
|
| + base::FilePath screenshot_path =
|
| + screenshot_directory.AppendASCII(screenshot_basename + ".png");
|
| if (GrabWindowSnapshot(window, rect, &png_data->data())) {
|
| last_screenshot_timestamp_ = base::Time::Now();
|
| - DisplayVisualFeedback(rect);
|
| PostSaveScreenshotTask(
|
| - screenshot_directory.AppendASCII(
|
| - ScreenshotSource::GetScreenshotBaseFilename() + ".png"),
|
| - png_data);
|
| + base::Bind(&ScreenshotTaker::ShowNotification, factory_.GetWeakPtr()),
|
| + profile_,
|
| + screenshot_path,
|
| + png_data);
|
| } else {
|
| LOG(ERROR) << "Failed to grab the window screenshot";
|
| + ShowNotification(
|
| + ScreenshotTakerObserver::SCREENSHOT_GRABWINDOW_PARTIAL_FAILED,
|
| + screenshot_path);
|
| }
|
| }
|
|
|
| @@ -216,25 +320,60 @@ bool ScreenshotTaker::CanTakeScreenshot() {
|
| kScreenshotMinimumIntervalInMS);
|
| }
|
|
|
| -void ScreenshotTaker::CloseVisualFeedbackLayer() {
|
| - visual_feedback_layer_.reset();
|
| +void ScreenshotTaker::ShowNotification(
|
| + ScreenshotTakerObserver::Result screenshot_result,
|
| + const base::FilePath& screenshot_path) {
|
| + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
|
| +#if defined(OS_CHROMEOS)
|
| + // TODO(sschmitz): make this work for Windows.
|
| + static int id = 0;
|
| + std::string id_text = base::StringPrintf("screenshot_%3.3d", ++id);
|
| + string16 replace_id = UTF8ToUTF16(id_text);
|
| + bool success =
|
| + (screenshot_result == ScreenshotTakerObserver::SCREENSHOT_SUCCESS);
|
| + Notification notification(
|
| + GURL(kNotificationOriginUrl),
|
| + ui::ResourceBundle::GetSharedInstance().GetImageNamed(
|
| + IDR_SCREENSHOT_NOTIFICATION_ICON),
|
| + l10n_util::GetStringUTF16(
|
| + success ?
|
| + IDS_ASH_SCREENSHOT_NOTIFICATION_TITLE_SUCCESS :
|
| + IDS_ASH_SCREENSHOT_NOTIFICATION_TITLE_FAIL),
|
| + l10n_util::GetStringUTF16(
|
| + success ?
|
| + IDS_ASH_SCREENSHOT_NOTIFICATION_TEXT_SUCCESS :
|
| + IDS_ASH_SCREENSHOT_NOTIFICATION_TEXT_FAIL),
|
| + WebKit::WebTextDirectionDefault,
|
| + string16(),
|
| + replace_id,
|
| + new ScreenshotTakerNotificationDelegate(id_text,
|
| + success,
|
| + screenshot_path));
|
| + g_browser_process->notification_ui_manager()->Add(notification, profile_);
|
| +#endif
|
| +
|
| + FOR_EACH_OBSERVER(ScreenshotTakerObserver, observers_,
|
| + OnScreenshotCompleted(screenshot_result, screenshot_path));
|
| +}
|
| +
|
| +void ScreenshotTaker::AddObserver(ScreenshotTakerObserver* observer) {
|
| + observers_.AddObserver(observer);
|
| }
|
|
|
| -void ScreenshotTaker::DisplayVisualFeedback(const gfx::Rect& rect) {
|
| - visual_feedback_layer_.reset(new ui::Layer(ui::LAYER_SOLID_COLOR));
|
| - visual_feedback_layer_->SetColor(SK_ColorWHITE);
|
| - visual_feedback_layer_->SetOpacity(kVisualFeedbackLayerOpacity);
|
| - visual_feedback_layer_->SetBounds(rect);
|
| +void ScreenshotTaker::RemoveObserver(ScreenshotTakerObserver* observer) {
|
| + observers_.RemoveObserver(observer);
|
| +}
|
| +
|
| +bool ScreenshotTaker::HasObserver(ScreenshotTakerObserver* observer) const {
|
| + return observers_.HasObserver(observer);
|
| +}
|
|
|
| - ui::Layer* parent = ash::Shell::GetContainer(
|
| - ash::Shell::GetActiveRootWindow(),
|
| - ash::internal::kShellWindowId_OverlayContainer)->layer();
|
| - parent->Add(visual_feedback_layer_.get());
|
| - visual_feedback_layer_->SetVisible(true);
|
| +void ScreenshotTaker::SetScreenshotDirectoryForTest(
|
| + const base::FilePath& directory) {
|
| + screenshot_directory_for_test_ = directory;
|
| +}
|
|
|
| - MessageLoopForUI::current()->PostDelayedTask(
|
| - FROM_HERE,
|
| - base::Bind(&ScreenshotTaker::CloseVisualFeedbackLayer,
|
| - base::Unretained(this)),
|
| - base::TimeDelta::FromMilliseconds(kVisualFeedbackLayerDisplayTimeMs));
|
| +void ScreenshotTaker::SetScreenshotBasenameForTest(
|
| + const std::string& basename){
|
| + screenshot_basename_for_test_ = basename;
|
| }
|
|
|