diff options
author | flackr <flackr@chromium.org> | 2014-12-03 06:35:37 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-12-03 14:36:03 +0000 |
commit | 149080069ead026c48e07c32430ec98bed17f75c (patch) | |
tree | a959b3db9bd990c1c0225e3a13defde335c6c8f3 /ui/snapshot | |
parent | f8cc584223885893b9d7fd943675c70b0a999091 (diff) | |
download | chromium_src-149080069ead026c48e07c32430ec98bed17f75c.zip chromium_src-149080069ead026c48e07c32430ec98bed17f75c.tar.gz chromium_src-149080069ead026c48e07c32430ec98bed17f75c.tar.bz2 |
Componentize ScreenshotTaker.
The filename determination is still done in chrome/browser/ui/ash as it depends on profile settings (download dir and preferred time format) and the save to drive support is exposed as part of the client api for now.
BUG=428745
TEST=ScreenshotGrabberTest.TakeScreenshot
TEST=Screenshot features continue to work as before on Chromebooks with Ctrl+F5 and Ctrl+Shift+F5
Review URL: https://codereview.chromium.org/706013004
Cr-Commit-Position: refs/heads/master@{#306600}
Diffstat (limited to 'ui/snapshot')
-rw-r--r-- | ui/snapshot/BUILD.gn | 3 | ||||
-rw-r--r-- | ui/snapshot/screenshot_grabber.cc | 196 | ||||
-rw-r--r-- | ui/snapshot/screenshot_grabber.h | 101 | ||||
-rw-r--r-- | ui/snapshot/screenshot_grabber_observer.h | 39 | ||||
-rw-r--r-- | ui/snapshot/snapshot.gyp | 3 |
5 files changed, 342 insertions, 0 deletions
diff --git a/ui/snapshot/BUILD.gn b/ui/snapshot/BUILD.gn index 9daacd0..972218a 100644 --- a/ui/snapshot/BUILD.gn +++ b/ui/snapshot/BUILD.gn @@ -6,6 +6,9 @@ import("//build/config/ui.gni") component("snapshot") { sources = [ + "screenshot_grabber.cc", + "screenshot_grabber.h", + "screenshot_grabber_observer.h", "snapshot.h", "snapshot_android.cc", "snapshot_async.cc", diff --git a/ui/snapshot/screenshot_grabber.cc b/ui/snapshot/screenshot_grabber.cc new file mode 100644 index 0000000..37d8a7e --- /dev/null +++ b/ui/snapshot/screenshot_grabber.cc @@ -0,0 +1,196 @@ +// 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 "ui/snapshot/screenshot_grabber.h" + +#include <climits> +#include <string> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/files/file_util.h" +#include "base/logging.h" +#include "base/task_runner.h" +#include "base/threading/sequenced_worker_pool.h" +#include "base/time/time.h" +#include "ui/gfx/image/image.h" +#include "ui/snapshot/snapshot.h" + +#if defined(USE_AURA) +#include "ui/aura/window.h" +#endif + +namespace ui { + +namespace { +// The minimum interval between two screenshot commands. It has to be +// more than 1000 to prevent the conflict of filenames. +const int kScreenshotMinimumIntervalInMS = 1000; + +using ShowNotificationCallback = + base::Callback<void(ScreenshotGrabberObserver::Result screenshot_result, + const base::FilePath& screenshot_path)>; + +void SaveScreenshot(scoped_refptr<base::TaskRunner> ui_task_runner, + const ShowNotificationCallback& callback, + const base::FilePath& screenshot_path, + scoped_refptr<base::RefCountedBytes> png_data, + ScreenshotGrabberDelegate::FileResult result, + const base::FilePath& local_path) { + DCHECK(!base::MessageLoopForUI::IsCurrent()); + DCHECK(!screenshot_path.empty()); + + // Convert FileResult into ScreenshotGrabberObserver::Result. + ScreenshotGrabberObserver::Result screenshot_result = + ScreenshotGrabberObserver::SCREENSHOT_SUCCESS; + switch (result) { + case ScreenshotGrabberDelegate::FILE_SUCCESS: + // Successfully got a local file to write to, write png data. + DCHECK_GT(static_cast<int>(png_data->size()), 0); + if (static_cast<size_t>(base::WriteFile( + local_path, reinterpret_cast<char*>(&(png_data->data()[0])), + static_cast<int>(png_data->size()))) != png_data->size()) { + LOG(ERROR) << "Failed to save to " << local_path.value(); + screenshot_result = + ScreenshotGrabberObserver::SCREENSHOT_WRITE_FILE_FAILED; + } + break; + case ScreenshotGrabberDelegate::FILE_CHECK_DIR_FAILED: + screenshot_result = + ScreenshotGrabberObserver::SCREENSHOT_CHECK_DIR_FAILED; + break; + case ScreenshotGrabberDelegate::FILE_CREATE_DIR_FAILED: + screenshot_result = + ScreenshotGrabberObserver::SCREENSHOT_CREATE_DIR_FAILED; + break; + case ScreenshotGrabberDelegate::FILE_CREATE_FAILED: + screenshot_result = + ScreenshotGrabberObserver::SCREENSHOT_CREATE_FILE_FAILED; + break; + } + + // Report the result on the UI thread. + ui_task_runner->PostTask( + FROM_HERE, base::Bind(callback, screenshot_result, screenshot_path)); +} + +void EnsureLocalDirectoryExists( + const base::FilePath& path, + ScreenshotGrabberDelegate::FileCallback callback) { + DCHECK(!base::MessageLoopForUI::IsCurrent()); + DCHECK(!path.empty()); + + if (!base::CreateDirectory(path.DirName())) { + LOG(ERROR) << "Failed to ensure the existence of " + << path.DirName().value(); + callback.Run(ScreenshotGrabberDelegate::FILE_CREATE_DIR_FAILED, path); + return; + } + + callback.Run(ScreenshotGrabberDelegate::FILE_SUCCESS, path); +} + +} // namespace + +void ScreenshotGrabberDelegate::PrepareFileAndRunOnBlockingPool( + const base::FilePath& path, + scoped_refptr<base::TaskRunner> blocking_task_runner, + const FileCallback& callback_on_blocking_pool) { + blocking_task_runner->PostTask( + FROM_HERE, + base::Bind(EnsureLocalDirectoryExists, path, callback_on_blocking_pool)); +} + +ScreenshotGrabber::ScreenshotGrabber( + ScreenshotGrabberDelegate* client, + scoped_refptr<base::TaskRunner> blocking_task_runner) + : client_(client), + blocking_task_runner_(blocking_task_runner), + factory_(this) { +} + +ScreenshotGrabber::~ScreenshotGrabber() { +} + +void ScreenshotGrabber::TakeScreenshot(gfx::NativeWindow window, + const gfx::Rect& rect, + const base::FilePath& screenshot_path) { + DCHECK(base::MessageLoopForUI::IsCurrent()); + last_screenshot_timestamp_ = base::TimeTicks::Now(); + + bool is_partial = true; + // Window identifier is used to log a message on failure to capture a full + // screen (i.e. non partial) screenshot. The only time is_partial can be + // false, we will also have an identification string for the window. + std::string window_identifier; +#if defined(USE_AURA) + aura::Window* aura_window = static_cast<aura::Window*>(window); + is_partial = rect.size() != aura_window->bounds().size(); + window_identifier = aura_window->GetBoundsInScreen().ToString(); +#endif + ui::GrabWindowSnapshotAsync( + window, rect, blocking_task_runner_, + base::Bind(&ScreenshotGrabber::GrabWindowSnapshotAsyncCallback, + factory_.GetWeakPtr(), window_identifier, screenshot_path, + is_partial)); +} + +bool ScreenshotGrabber::CanTakeScreenshot() { + return last_screenshot_timestamp_.is_null() || + base::TimeTicks::Now() - last_screenshot_timestamp_ > + base::TimeDelta::FromMilliseconds(kScreenshotMinimumIntervalInMS); +} + +void ScreenshotGrabber::NotifyScreenshotCompleted( + ScreenshotGrabberObserver::Result screenshot_result, + const base::FilePath& screenshot_path) { + DCHECK(base::MessageLoopForUI::IsCurrent()); + FOR_EACH_OBSERVER(ScreenshotGrabberObserver, observers_, + OnScreenshotCompleted(screenshot_result, screenshot_path)); +} + +void ScreenshotGrabber::AddObserver(ScreenshotGrabberObserver* observer) { + observers_.AddObserver(observer); +} + +void ScreenshotGrabber::RemoveObserver(ScreenshotGrabberObserver* observer) { + observers_.RemoveObserver(observer); +} + +bool ScreenshotGrabber::HasObserver( + const ScreenshotGrabberObserver* observer) const { + return observers_.HasObserver(observer); +} + +void ScreenshotGrabber::GrabWindowSnapshotAsyncCallback( + const std::string& window_identifier, + base::FilePath screenshot_path, + bool is_partial, + scoped_refptr<base::RefCountedBytes> png_data) { + DCHECK(base::MessageLoopForUI::IsCurrent()); + if (!png_data.get()) { + if (is_partial) { + LOG(ERROR) << "Failed to grab the window screenshot"; + NotifyScreenshotCompleted( + ScreenshotGrabberObserver::SCREENSHOT_GRABWINDOW_PARTIAL_FAILED, + screenshot_path); + } else { + LOG(ERROR) << "Failed to grab the window screenshot for " + << window_identifier; + NotifyScreenshotCompleted( + ScreenshotGrabberObserver::SCREENSHOT_GRABWINDOW_FULL_FAILED, + screenshot_path); + } + return; + } + + ShowNotificationCallback notification_callback(base::Bind( + &ScreenshotGrabber::NotifyScreenshotCompleted, factory_.GetWeakPtr())); + client_->PrepareFileAndRunOnBlockingPool( + screenshot_path, blocking_task_runner_, + base::Bind(&SaveScreenshot, base::MessageLoop::current()->task_runner(), + notification_callback, screenshot_path, png_data)); +} + +} // namespace ui diff --git a/ui/snapshot/screenshot_grabber.h b/ui/snapshot/screenshot_grabber.h new file mode 100644 index 0000000..117b3ce --- /dev/null +++ b/ui/snapshot/screenshot_grabber.h @@ -0,0 +1,101 @@ +// 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. + +#ifndef UI_SNAPSHOT_SCREENSHOT_GRABBER_H_ +#define UI_SNAPSHOT_SCREENSHOT_GRABBER_H_ + +#include "base/basictypes.h" +#include "base/callback.h" +#include "base/compiler_specific.h" +#include "base/files/file_path.h" +#include "base/memory/ref_counted_memory.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/observer_list.h" +#include "base/time/time.h" +#include "ui/aura/window.h" +#include "ui/gfx/rect.h" +#include "ui/snapshot/screenshot_grabber_observer.h" +#include "ui/snapshot/snapshot_export.h" + +namespace base { +class TaskRunner; +} + +namespace ui { + +// TODO(flackr): Componentize google drive so that we don't need the +// ScreenshotGrabberDelegate. +class SNAPSHOT_EXPORT ScreenshotGrabberDelegate { + public: + enum FileResult { + FILE_SUCCESS, + FILE_CHECK_DIR_FAILED, + FILE_CREATE_DIR_FAILED, + FILE_CREATE_FAILED + }; + + // Callback called with the |result| of trying to create a local writable + // |path| for the possibly remote path. + using FileCallback = + base::Callback<void(FileResult result, const base::FilePath& path)>; + + ScreenshotGrabberDelegate() {} + virtual ~ScreenshotGrabberDelegate() {} + + // Prepares a writable file for |path|. If |path| is a non-local path (i.e. + // Google drive) and it is supported this will create a local cached copy of + // the remote file and call the callback with the local path. + virtual void PrepareFileAndRunOnBlockingPool( + const base::FilePath& path, + scoped_refptr<base::TaskRunner> blocking_task_runner, + const FileCallback& callback_on_blocking_pool); +}; + +class SNAPSHOT_EXPORT ScreenshotGrabber { + public: + ScreenshotGrabber(ScreenshotGrabberDelegate* client, + scoped_refptr<base::TaskRunner> blocking_task_runner); + ~ScreenshotGrabber(); + + // Takes a screenshot of |rect| in |window| in that window's coordinate space + // saving the result to |screenshot_path|. + void TakeScreenshot(gfx::NativeWindow window, + const gfx::Rect& rect, + const base::FilePath& screenshot_path); + bool CanTakeScreenshot(); + + void NotifyScreenshotCompleted( + ScreenshotGrabberObserver::Result screenshot_result, + const base::FilePath& screenshot_path); + + void AddObserver(ScreenshotGrabberObserver* observer); + void RemoveObserver(ScreenshotGrabberObserver* observer); + bool HasObserver(const ScreenshotGrabberObserver* observer) const; + + private: + void GrabWindowSnapshotAsyncCallback( + const std::string& window_identifier, + base::FilePath screenshot_path, + bool is_partial, + scoped_refptr<base::RefCountedBytes> png_data); + + // A weak pointer to the screenshot taker client. + ScreenshotGrabberDelegate* client_; + + // The timestamp when the screenshot task was issued last time. + base::TimeTicks last_screenshot_timestamp_; + + // Task runner for blocking tasks. + scoped_refptr<base::TaskRunner> blocking_task_runner_; + + ObserverList<ScreenshotGrabberObserver> observers_; + base::WeakPtrFactory<ScreenshotGrabber> factory_; + + DISALLOW_COPY_AND_ASSIGN(ScreenshotGrabber); +}; + +} // namespace ui + +#endif // UI_SNAPSHOT_SCREENSHOT_GRABBER_H_ diff --git a/ui/snapshot/screenshot_grabber_observer.h b/ui/snapshot/screenshot_grabber_observer.h new file mode 100644 index 0000000..4d90306 --- /dev/null +++ b/ui/snapshot/screenshot_grabber_observer.h @@ -0,0 +1,39 @@ +// 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. + +#ifndef UI_SNAPSHOT_SCREENSHOT_GRABBER_OBSERVER_H_ +#define UI_SNAPSHOT_SCREENSHOT_GRABBER_OBSERVER_H_ + +#include "base/files/file_path.h" +#include "ui/snapshot/snapshot_export.h" + +namespace ui { + +class SNAPSHOT_EXPORT ScreenshotGrabberObserver { + public: + enum Result { + SCREENSHOT_SUCCESS = 0, + SCREENSHOT_GRABWINDOW_PARTIAL_FAILED, + SCREENSHOT_GRABWINDOW_FULL_FAILED, + SCREENSHOT_CREATE_DIR_FAILED, + SCREENSHOT_GET_DIR_FAILED, + SCREENSHOT_CHECK_DIR_FAILED, + SCREENSHOT_CREATE_FILE_FAILED, + SCREENSHOT_WRITE_FILE_FAILED, + SCREENSHOTS_DISABLED, + SCREENSHOT_RESULT_COUNT + }; + + // Dispatched after attempting to take a screenshot with the |result| and + // |screenshot_path| of the taken screenshot (if successful). + virtual void OnScreenshotCompleted(Result screenshot_result, + const base::FilePath& screenshot_path) = 0; + + protected: + virtual ~ScreenshotGrabberObserver() {} +}; + +} // namespace ui + +#endif // UI_SNAPSHOT_SCREENSHOT_GRABBER_OBSERVER_H_ diff --git a/ui/snapshot/snapshot.gyp b/ui/snapshot/snapshot.gyp index fca6f4e..58c8417 100644 --- a/ui/snapshot/snapshot.gyp +++ b/ui/snapshot/snapshot.gyp @@ -21,6 +21,9 @@ 'SNAPSHOT_IMPLEMENTATION', ], 'sources': [ + 'screenshot_grabber.cc', + 'screenshot_grabber.h', + 'screenshot_grabber_observer.h', 'snapshot.h', 'snapshot_android.cc', 'snapshot_async.cc', |