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

Unified Diff: chrome/browser/ui/ash/chrome_screenshot_grabber.cc

Issue 706013004: Move non-browser specific ScreenshotTaker code to ui/snapshot. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Merge and fix gn Created 6 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/ui/ash/chrome_screenshot_grabber.cc
diff --git a/chrome/browser/ui/ash/chrome_screenshot_grabber.cc b/chrome/browser/ui/ash/chrome_screenshot_grabber.cc
new file mode 100644
index 0000000000000000000000000000000000000000..20111d4e96a6b7c956cec61a51dfd74fc2b47052
--- /dev/null
+++ b/chrome/browser/ui/ash/chrome_screenshot_grabber.cc
@@ -0,0 +1,439 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/ash/chrome_screenshot_grabber.h"
+
+#include "ash/shell.h"
+#include "ash/system/system_notifier.h"
+#include "base/base64.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file_util.h"
+#include "base/i18n/time_formatting.h"
+#include "base/prefs/pref_service.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/download/download_prefs.h"
+#include "chrome/browser/notifications/notification_ui_manager.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/common/pref_names.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/user_metrics.h"
+#include "grit/ash_strings.h"
+#include "grit/theme_resources.h"
+#include "ui/base/clipboard/clipboard.h"
+#include "ui/base/clipboard/scoped_clipboard_writer.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/strings/grit/ui_strings.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/drive/file_system_interface.h"
+#include "chrome/browser/chromeos/drive/file_system_util.h"
+#include "chrome/browser/chromeos/file_manager/open_util.h"
+#include "chrome/browser/notifications/desktop_notification_service.h"
+#include "chrome/browser/notifications/desktop_notification_service_factory.h"
+#include "chromeos/login/login_state.h"
+#endif
+
+namespace {
+
+const char kNotificationId[] = "screenshot";
+
+#if defined(OS_CHROMEOS)
+const char kNotificationOriginUrl[] = "chrome://screenshot";
+#endif
+
+const char kImageClipboardFormatPrefix[] = "<img src='data:image/png;base64,";
+const char kImageClipboardFormatSuffix[] = "'>";
+
+void CopyScreenshotToClipboard(scoped_refptr<base::RefCountedString> png_data) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ std::string encoded;
+ base::Base64Encode(png_data->data(), &encoded);
+
+ // Only cares about HTML because ChromeOS doesn't need other formats.
+ // TODO(dcheng): Why don't we take advantage of the ability to write bitmaps
+ // to the clipboard here?
+ {
+ ui::ScopedClipboardWriter scw(ui::CLIPBOARD_TYPE_COPY_PASTE);
+ std::string html(kImageClipboardFormatPrefix);
+ html += encoded;
+ html += kImageClipboardFormatSuffix;
+ scw.WriteHTML(base::UTF8ToUTF16(html), std::string());
+ }
+ content::RecordAction(base::UserMetricsAction("Screenshot_CopyClipboard"));
+}
+
+void ReadFileAndCopyToClipboardLocal(const base::FilePath& screenshot_path) {
+ DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
+
+ scoped_refptr<base::RefCountedString> png_data(new base::RefCountedString());
+ if (!base::ReadFileToString(screenshot_path, &(png_data->data()))) {
+ LOG(ERROR) << "Failed to read the screenshot file: "
+ << screenshot_path.value();
+ return;
+ }
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::UI, FROM_HERE,
+ base::Bind(CopyScreenshotToClipboard, png_data));
+}
+
+#if defined(OS_CHROMEOS)
+void ReadFileAndCopyToClipboardDrive(drive::FileError error,
+ const base::FilePath& file_path,
+ scoped_ptr<drive::ResourceEntry> entry) {
+ if (error != drive::FILE_ERROR_OK) {
+ LOG(ERROR) << "Failed to read the screenshot path on drive: "
+ << drive::FileErrorToString(error);
+ return;
+ }
+ content::BrowserThread::GetBlockingPool()->PostTask(
+ FROM_HERE, base::Bind(&ReadFileAndCopyToClipboardLocal, file_path));
+}
+#endif
+
+// 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 ScreenshotGrabberNotificationDelegate : public NotificationDelegate {
+ public:
+ ScreenshotGrabberNotificationDelegate(bool success,
+ Profile* profile,
+ const base::FilePath& screenshot_path)
+ : success_(success),
+ profile_(profile),
+ screenshot_path_(screenshot_path) {}
+
+ // Overridden from NotificationDelegate:
+ void Click() override {
+ if (!success_)
+ return;
+#if defined(OS_CHROMEOS)
+ file_manager::util::ShowItemInFolder(profile_, screenshot_path_);
+#else
+// TODO(sschmitz): perhaps add similar action for Windows.
+#endif
+ }
+ void ButtonClick(int button_index) override {
+ DCHECK(success_ && button_index == 0);
+
+// To avoid keeping the screenshot image on memory, it will re-read the
+// screenshot file and copy it to the clipboard.
+#if defined(OS_CHROMEOS)
+ if (drive::util::IsUnderDriveMountPoint(screenshot_path_)) {
+ drive::FileSystemInterface* file_system =
+ drive::util::GetFileSystemByProfile(profile_);
+ file_system->GetFile(drive::util::ExtractDrivePath(screenshot_path_),
+ base::Bind(&ReadFileAndCopyToClipboardDrive));
+ return;
+ }
+#endif
+ content::BrowserThread::GetBlockingPool()->PostTask(
+ FROM_HERE,
+ base::Bind(&ReadFileAndCopyToClipboardLocal, screenshot_path_));
+ }
+ bool HasClickedListener() override { return success_; }
+ std::string id() const override { return std::string(kNotificationId); }
+
+ private:
+ ~ScreenshotGrabberNotificationDelegate() override {}
+
+ const bool success_;
+ Profile* profile_;
+ const base::FilePath screenshot_path_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScreenshotGrabberNotificationDelegate);
+};
+
+#if defined(OS_CHROMEOS)
+int GetScreenshotNotificationTitle(
+ ui::ScreenshotGrabberObserver::Result screenshot_result) {
+ switch (screenshot_result) {
+ case ui::ScreenshotGrabberObserver::SCREENSHOTS_DISABLED:
+ return IDS_ASH_SCREENSHOT_NOTIFICATION_TITLE_DISABLED;
+ case ui::ScreenshotGrabberObserver::SCREENSHOT_SUCCESS:
+ return IDS_ASH_SCREENSHOT_NOTIFICATION_TITLE_SUCCESS;
+ default:
+ return IDS_ASH_SCREENSHOT_NOTIFICATION_TITLE_FAIL;
+ }
+}
+
+int GetScreenshotNotificationText(
+ ui::ScreenshotGrabberObserver::Result screenshot_result) {
+ switch (screenshot_result) {
+ case ui::ScreenshotGrabberObserver::SCREENSHOTS_DISABLED:
+ return IDS_ASH_SCREENSHOT_NOTIFICATION_TEXT_DISABLED;
+ case ui::ScreenshotGrabberObserver::SCREENSHOT_SUCCESS:
+ return IDS_ASH_SCREENSHOT_NOTIFICATION_TEXT_SUCCESS;
+ default:
+ return IDS_ASH_SCREENSHOT_NOTIFICATION_TEXT_FAIL;
+ }
+}
+
+void PrepareWritableFileCallback(
+ const ChromeScreenshotGrabber::FileCallback& callback,
+ drive::FileError error,
+ const base::FilePath& local_path) {
+ callback.Run(error == drive::FILE_ERROR_OK
+ ? ui::ScreenshotGrabberDelegate::FILE_SUCCESS
+ : ui::ScreenshotGrabberDelegate::FILE_CREATE_FAILED,
+ local_path);
+}
+
+void EnsureDirectoryExistsCallback(
+ const ChromeScreenshotGrabber::FileCallback& callback,
+ Profile* profile,
+ const base::FilePath& path,
+ drive::FileError error) {
+ // It is okay to fail with FILE_ERROR_EXISTS since anyway the directory
+ // of the target file exists.
+ if (error == drive::FILE_ERROR_OK || error == drive::FILE_ERROR_EXISTS) {
+ drive::util::PrepareWritableFileAndRun(
+ profile, path, base::Bind(&PrepareWritableFileCallback, callback));
+ } else {
+ LOG(ERROR) << "Failed to ensure the existence of the specified directory "
+ << "in Google Drive: " << error;
+ content::BrowserThread::GetBlockingPool()->PostTask(
+ FROM_HERE,
+ base::Bind(callback,
+ ui::ScreenshotGrabberDelegate::FILE_CHECK_DIR_FAILED,
+ base::FilePath()));
+ }
+}
+#endif
+
+bool ScreenshotsDisabled() {
+ return g_browser_process->local_state()->GetBoolean(
+ prefs::kDisableScreenshots);
+}
+
+bool ShouldUse24HourClock() {
+#if defined(OS_CHROMEOS)
+ Profile* profile = ProfileManager::GetActiveUserProfile();
+ if (profile) {
+ return profile->GetPrefs()->GetBoolean(prefs::kUse24HourClock);
+ }
+#endif
+ return base::GetHourClockType() == base::k24HourClock;
+}
+
+bool GetScreenshotDirectory(base::FilePath* directory) {
+ bool is_logged_in = true;
+
+#if defined(OS_CHROMEOS)
+ is_logged_in = chromeos::LoginState::Get()->IsUserLoggedIn();
+#endif
+
+ if (is_logged_in) {
+ DownloadPrefs* download_prefs = DownloadPrefs::FromBrowserContext(
+ ProfileManager::GetActiveUserProfile());
+ *directory = download_prefs->DownloadPath();
+ } else {
+ if (!base::GetTempDir(directory)) {
+ LOG(ERROR) << "Failed to find temporary directory.";
+ return false;
+ }
+ }
+ return true;
+}
+
+std::string GetScreenshotBaseFilename() {
+ base::Time::Exploded now;
+ base::Time::Now().LocalExplode(&now);
+
+ // We don't use base/i18n/time_formatting.h here because it doesn't
+ // support our format. Don't use ICU either to avoid i18n file names
+ // for non-English locales.
+ // TODO(mukai): integrate this logic somewhere time_formatting.h
+ std::string file_name = base::StringPrintf(
+ "Screenshot %d-%02d-%02d at ", now.year, now.month, now.day_of_month);
+
+ if (ShouldUse24HourClock()) {
+ file_name.append(
+ base::StringPrintf("%02d.%02d.%02d", now.hour, now.minute, now.second));
+ } else {
+ int hour = now.hour;
+ if (hour > 12) {
+ hour -= 12;
+ } else if (hour == 0) {
+ hour = 12;
+ }
+ file_name.append(
+ base::StringPrintf("%d.%02d.%02d ", hour, now.minute, now.second));
+ file_name.append((now.hour >= 12) ? "PM" : "AM");
+ }
+
+ return file_name;
+}
+
+} // namespace
+
+ChromeScreenshotGrabber::ChromeScreenshotGrabber()
+ : screenshot_grabber_(new ui::ScreenshotGrabber(
+ this,
+ content::BrowserThread::GetBlockingPool()
+ ->GetTaskRunnerWithShutdownBehavior(
+ base::SequencedWorkerPool::SKIP_ON_SHUTDOWN))),
+ profile_for_test_(NULL) {
+ screenshot_grabber_->AddObserver(this);
+}
+
+ChromeScreenshotGrabber::~ChromeScreenshotGrabber() {
+ screenshot_grabber_->RemoveObserver(this);
+}
+
+void ChromeScreenshotGrabber::HandleTakeScreenshotForAllRootWindows() {
+ if (ScreenshotsDisabled()) {
+ screenshot_grabber_->NotifyScreenshotCompleted(
+ ui::ScreenshotGrabberObserver::SCREENSHOTS_DISABLED, base::FilePath());
+ return;
+ }
+
+ base::FilePath screenshot_directory;
+ if (!GetScreenshotDirectory(&screenshot_directory)) {
+ screenshot_grabber_->NotifyScreenshotCompleted(
+ ui::ScreenshotGrabberObserver::SCREENSHOT_GET_DIR_FAILED,
+ base::FilePath());
+ return;
+ }
+
+ std::string screenshot_basename = GetScreenshotBaseFilename();
+ aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
+ // Reorder root_windows to take the primary root window's snapshot at first.
+ aura::Window* primary_root = ash::Shell::GetPrimaryRootWindow();
+ if (*(root_windows.begin()) != primary_root) {
+ root_windows.erase(
+ std::find(root_windows.begin(), root_windows.end(), primary_root));
+ root_windows.insert(root_windows.begin(), primary_root);
+ }
+ std::vector<base::FilePath> filenames;
+ for (size_t i = 0; i < root_windows.size(); ++i) {
+ aura::Window* root_window = root_windows[i];
+ std::string basename = screenshot_basename;
+ 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");
+ screenshot_grabber_->TakeScreenshot(root_window, rect, screenshot_path);
+ }
+ content::RecordAction(base::UserMetricsAction("Screenshot_TakeFull"));
+}
+
+void ChromeScreenshotGrabber::HandleTakePartialScreenshot(
+ aura::Window* window,
+ const gfx::Rect& rect) {
+ if (ScreenshotsDisabled()) {
+ screenshot_grabber_->NotifyScreenshotCompleted(
+ ui::ScreenshotGrabberObserver::SCREENSHOTS_DISABLED, base::FilePath());
+ return;
+ }
+
+ base::FilePath screenshot_directory;
+ if (!GetScreenshotDirectory(&screenshot_directory)) {
+ screenshot_grabber_->NotifyScreenshotCompleted(
+ ui::ScreenshotGrabberObserver::SCREENSHOT_GET_DIR_FAILED,
+ base::FilePath());
+ return;
+ }
+
+ base::FilePath screenshot_path =
+ screenshot_directory.AppendASCII(GetScreenshotBaseFilename() + ".png");
+ screenshot_grabber_->TakeScreenshot(window, rect, screenshot_path);
+ content::RecordAction(base::UserMetricsAction("Screenshot_TakePartial"));
+}
+
+bool ChromeScreenshotGrabber::CanTakeScreenshot() {
+ return screenshot_grabber_->CanTakeScreenshot();
+}
+
+void ChromeScreenshotGrabber::PrepareFileAndRunOnBlockingPool(
+ const base::FilePath& path,
+ scoped_refptr<base::TaskRunner> blocking_task_runner,
+ const FileCallback& callback) {
+#if defined(OS_CHROMEOS)
+ Profile* profile = ProfileManager::GetActiveUserProfile();
+ if (drive::util::IsUnderDriveMountPoint(path)) {
+ drive::util::EnsureDirectoryExists(
+ profile, path.DirName(),
+ base::Bind(&EnsureDirectoryExistsCallback, callback, profile, path));
+ return;
+ }
+#endif
+ ui::ScreenshotGrabberDelegate::PrepareFileAndRunOnBlockingPool(
+ path, blocking_task_runner, callback);
+}
+
+void ChromeScreenshotGrabber::OnScreenshotCompleted(
+ ui::ScreenshotGrabberObserver::Result result,
+ const base::FilePath& screenshot_path) {
+#if defined(OS_CHROMEOS)
+ // Do not show a notification that a screenshot was taken while no user is
+ // logged in, since it is confusing for the user to get a message about it
+ // after he logs in (crbug.com/235217).
+ if (!chromeos::LoginState::Get()->IsUserLoggedIn())
+ return;
+
+ // TODO(sschmitz): make this work for Windows.
+ DesktopNotificationService* const service =
+ DesktopNotificationServiceFactory::GetForProfile(GetProfile());
+ if (service->IsNotifierEnabled(message_center::NotifierId(
+ message_center::NotifierId::SYSTEM_COMPONENT,
+ ash::system_notifier::kNotifierScreenshot))) {
+ scoped_ptr<Notification> notification(
+ CreateNotification(result, screenshot_path));
+ g_browser_process->notification_ui_manager()->Add(*notification,
+ GetProfile());
+ }
+#endif
+}
+
+#if defined(OS_CHROMEOS)
+Notification* ChromeScreenshotGrabber::CreateNotification(
+ ui::ScreenshotGrabberObserver::Result screenshot_result,
+ const base::FilePath& screenshot_path) {
+ const std::string notification_id(kNotificationId);
+ // We cancel a previous screenshot notification, if any, to ensure we get
+ // a fresh notification pop-up.
+ g_browser_process->notification_ui_manager()->CancelById(
+ notification_id, NotificationUIManager::GetProfileID(GetProfile()));
+ const base::string16 replace_id(base::UTF8ToUTF16(notification_id));
+ bool success =
+ (screenshot_result == ui::ScreenshotGrabberObserver::SCREENSHOT_SUCCESS);
+ message_center::RichNotificationData optional_field;
+ if (success) {
+ const base::string16 label = l10n_util::GetStringUTF16(
+ IDS_MESSAGE_CENTER_NOTIFICATION_BUTTON_COPY_SCREENSHOT_TO_CLIPBOARD);
+ optional_field.buttons.push_back(message_center::ButtonInfo(label));
+ }
+ return new Notification(
+ message_center::NOTIFICATION_TYPE_SIMPLE, GURL(kNotificationOriginUrl),
+ l10n_util::GetStringUTF16(
+ GetScreenshotNotificationTitle(screenshot_result)),
+ l10n_util::GetStringUTF16(
+ GetScreenshotNotificationText(screenshot_result)),
+ ui::ResourceBundle::GetSharedInstance().GetImageNamed(
+ IDR_SCREENSHOT_NOTIFICATION_ICON),
+ blink::WebTextDirectionDefault,
+ message_center::NotifierId(message_center::NotifierId::SYSTEM_COMPONENT,
+ ash::system_notifier::kNotifierScreenshot),
+ l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_NOTIFIER_SCREENSHOT_NAME),
+ replace_id, optional_field, new ScreenshotGrabberNotificationDelegate(
+ success, GetProfile(), screenshot_path));
+}
+#endif
+
+void ChromeScreenshotGrabber::SetProfileForTest(Profile* profile) {
+ profile_for_test_ = profile;
+}
+
+Profile* ChromeScreenshotGrabber::GetProfile() {
+ return profile_for_test_ ? profile_for_test_
+ : ProfileManager::GetActiveUserProfile();
+}
« no previous file with comments | « chrome/browser/ui/ash/chrome_screenshot_grabber.h ('k') | chrome/browser/ui/ash/chrome_screenshot_grabber_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698