Commit f77f856d authored by sschmitz@chromium.org's avatar sschmitz@chromium.org

Crash when taking screenshot onto Chrome OS Google Drive.

And, Don't stack screenshot notifications.

Changed profile handling. The profile is now retrieved everytime a screenshot is taken and
not only once in Ash init. The reason is that the profile can change, which caused this crash.

Changed behavior for a subsequent screenshot notification. It will replace the previous
notification (rather than add a new one).

When saving to Google Drive we have two path: screenshot path (used in notification) and
local path (for internal cash/work; used in writing). Add proper handling of both paths.


BUG=231707, 232876, 233574
R=jamescook@chromium.org
TEST=manual

Review URL: https://chromiumcodereview.appspot.com/14027009

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@195370 0039d316-1c4b-4281-b951-d872f2087c98
parent 7da46c79
...@@ -17,7 +17,6 @@ ...@@ -17,7 +17,6 @@
#include "chrome/browser/chromeos/accessibility/accessibility_util.h" #include "chrome/browser/chromeos/accessibility/accessibility_util.h"
#include "chrome/browser/chromeos/accessibility/magnification_manager.h" #include "chrome/browser/chromeos/accessibility/magnification_manager.h"
#include "chrome/browser/lifetime/application_lifetime.h" #include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/ash/chrome_shell_delegate.h" #include "chrome/browser/ui/ash/chrome_shell_delegate.h"
#include "chrome/browser/ui/ash/event_rewriter.h" #include "chrome/browser/ui/ash/event_rewriter.h"
#include "chrome/browser/ui/ash/screenshot_taker.h" #include "chrome/browser/ui/ash/screenshot_taker.h"
...@@ -69,9 +68,7 @@ void OpenAsh() { ...@@ -69,9 +68,7 @@ void OpenAsh() {
shell->event_rewriter_filter()->SetEventRewriterDelegate( shell->event_rewriter_filter()->SetEventRewriterDelegate(
scoped_ptr<ash::EventRewriterDelegate>(new EventRewriter).Pass()); scoped_ptr<ash::EventRewriterDelegate>(new EventRewriter).Pass());
shell->accelerator_controller()->SetScreenshotDelegate( shell->accelerator_controller()->SetScreenshotDelegate(
scoped_ptr<ash::ScreenshotDelegate>( scoped_ptr<ash::ScreenshotDelegate>(new ScreenshotTaker).Pass());
new ScreenshotTaker(
ProfileManager::GetDefaultProfileOrOffTheRecord())).Pass());
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
shell->accelerator_controller()->SetBrightnessControlDelegate( shell->accelerator_controller()->SetBrightnessControlDelegate(
scoped_ptr<ash::BrightnessControlDelegate>( scoped_ptr<ash::BrightnessControlDelegate>(
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "chrome/browser/notifications/notification.h" #include "chrome/browser/notifications/notification.h"
#include "chrome/browser/notifications/notification_ui_manager.h" #include "chrome/browser/notifications/notification_ui_manager.h"
#include "chrome/browser/profiles/profile.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/webui/screenshot_source.h"
#include "chrome/browser/ui/window_snapshot/window_snapshot.h" #include "chrome/browser/ui/window_snapshot/window_snapshot.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
...@@ -43,6 +44,8 @@ namespace { ...@@ -43,6 +44,8 @@ namespace {
// more than 1000 to prevent the conflict of filenames. // more than 1000 to prevent the conflict of filenames.
const int kScreenshotMinimumIntervalInMS = 1000; const int kScreenshotMinimumIntervalInMS = 1000;
const char kNotificationId[] = "screenshot";
const char kNotificationOriginUrl[] = "chrome://screenshot"; const char kNotificationOriginUrl[] = "chrome://screenshot";
// Delegate for a notification. This class has two roles: to implement callback // Delegate for a notification. This class has two roles: to implement callback
...@@ -50,11 +53,9 @@ const char kNotificationOriginUrl[] = "chrome://screenshot"; ...@@ -50,11 +53,9 @@ const char kNotificationOriginUrl[] = "chrome://screenshot";
// notification. // notification.
class ScreenshotTakerNotificationDelegate : public NotificationDelegate { class ScreenshotTakerNotificationDelegate : public NotificationDelegate {
public: public:
ScreenshotTakerNotificationDelegate(const std::string& id_text, ScreenshotTakerNotificationDelegate(bool success,
bool success,
const base::FilePath& screenshot_path) const base::FilePath& screenshot_path)
: id_text_(id_text), : success_(success),
success_(success),
screenshot_path_(screenshot_path) { screenshot_path_(screenshot_path) {
} }
...@@ -71,7 +72,9 @@ class ScreenshotTakerNotificationDelegate : public NotificationDelegate { ...@@ -71,7 +72,9 @@ class ScreenshotTakerNotificationDelegate : public NotificationDelegate {
// TODO(sschmitz): perhaps add similar action for Windows. // TODO(sschmitz): perhaps add similar action for Windows.
#endif #endif
} }
virtual std::string id() const OVERRIDE { return id_text_; } virtual std::string id() const OVERRIDE {
return std::string(kNotificationId);
}
virtual content::RenderViewHost* GetRenderViewHost() const OVERRIDE { virtual content::RenderViewHost* GetRenderViewHost() const OVERRIDE {
return NULL; return NULL;
} }
...@@ -79,7 +82,6 @@ class ScreenshotTakerNotificationDelegate : public NotificationDelegate { ...@@ -79,7 +82,6 @@ class ScreenshotTakerNotificationDelegate : public NotificationDelegate {
private: private:
virtual ~ScreenshotTakerNotificationDelegate() {} virtual ~ScreenshotTakerNotificationDelegate() {}
const std::string id_text_;
const bool success_; const bool success_;
const base::FilePath screenshot_path_; const base::FilePath screenshot_path_;
...@@ -92,16 +94,17 @@ typedef base::Callback< ...@@ -92,16 +94,17 @@ typedef base::Callback<
void SaveScreenshotInternal(const ShowNotificationCallback& callback, void SaveScreenshotInternal(const ShowNotificationCallback& callback,
const base::FilePath& screenshot_path, const base::FilePath& screenshot_path,
const base::FilePath& local_path,
scoped_refptr<base::RefCountedBytes> png_data) { scoped_refptr<base::RefCountedBytes> png_data) {
DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
DCHECK(!screenshot_path.empty()); DCHECK(!local_path.empty());
ScreenshotTakerObserver::Result result = ScreenshotTakerObserver::Result result =
ScreenshotTakerObserver::SCREENSHOT_SUCCESS; ScreenshotTakerObserver::SCREENSHOT_SUCCESS;
if (static_cast<size_t>(file_util::WriteFile( if (static_cast<size_t>(file_util::WriteFile(
screenshot_path, local_path,
reinterpret_cast<char*>(&(png_data->data()[0])), reinterpret_cast<char*>(&(png_data->data()[0])),
png_data->size())) != png_data->size()) { png_data->size())) != png_data->size()) {
LOG(ERROR) << "Failed to save to " << screenshot_path.value(); LOG(ERROR) << "Failed to save to " << local_path.value();
result = ScreenshotTakerObserver::SCREENSHOT_WRITE_FILE_FAILED; result = ScreenshotTakerObserver::SCREENSHOT_WRITE_FILE_FAILED;
} }
content::BrowserThread::PostTask( content::BrowserThread::PostTask(
...@@ -125,25 +128,29 @@ void SaveScreenshot(const ShowNotificationCallback& callback, ...@@ -125,25 +128,29 @@ void SaveScreenshot(const ShowNotificationCallback& callback,
screenshot_path)); screenshot_path));
return; return;
} }
SaveScreenshotInternal(callback, screenshot_path, png_data); SaveScreenshotInternal(callback, screenshot_path, screenshot_path, png_data);
} }
// TODO(kinaba): crbug.com/140425, remove this ungly #ifdef dispatch. // TODO(kinaba): crbug.com/140425, remove this ungly #ifdef dispatch.
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
void SaveScreenshotToDrive(const ShowNotificationCallback& callback, void SaveScreenshotToDrive(const ShowNotificationCallback& callback,
const base::FilePath& screenshot_path,
scoped_refptr<base::RefCountedBytes> png_data, scoped_refptr<base::RefCountedBytes> png_data,
drive::DriveFileError error, drive::DriveFileError error,
const base::FilePath& local_path) { const base::FilePath& local_path) {
// |screenshot_path| is used in the notification callback.
// |local_path| is a temporary file in a hidden cache directory used for
// internal work generated by drive::util::PrepareWritableFileAndRun.
if (error != drive::DRIVE_FILE_OK) { if (error != drive::DRIVE_FILE_OK) {
LOG(ERROR) << "Failed to write screenshot image to Google Drive: " << error; LOG(ERROR) << "Failed to write screenshot image to Google Drive: " << error;
content::BrowserThread::PostTask( content::BrowserThread::PostTask(
content::BrowserThread::UI, FROM_HERE, content::BrowserThread::UI, FROM_HERE,
base::Bind(callback, base::Bind(callback,
ScreenshotTakerObserver::SCREENSHOT_CREATE_FILE_FAILED, ScreenshotTakerObserver::SCREENSHOT_CREATE_FILE_FAILED,
local_path)); screenshot_path));
return; return;
} }
SaveScreenshotInternal(callback, local_path, png_data); SaveScreenshotInternal(callback, screenshot_path, local_path, png_data);
} }
void EnsureDirectoryExistsCallback( void EnsureDirectoryExistsCallback(
...@@ -159,7 +166,10 @@ void EnsureDirectoryExistsCallback( ...@@ -159,7 +166,10 @@ void EnsureDirectoryExistsCallback(
drive::util::PrepareWritableFileAndRun( drive::util::PrepareWritableFileAndRun(
profile, profile,
screenshot_path, screenshot_path,
base::Bind(&SaveScreenshotToDrive, callback, png_data)); base::Bind(&SaveScreenshotToDrive,
callback,
screenshot_path,
png_data));
} else { } else {
LOG(ERROR) << "Failed to ensure the existence of the specified directory " LOG(ERROR) << "Failed to ensure the existence of the specified directory "
<< "in Google Drive: " << error; << "in Google Drive: " << error;
...@@ -223,10 +233,9 @@ bool GrabWindowSnapshot(aura::Window* window, ...@@ -223,10 +233,9 @@ bool GrabWindowSnapshot(aura::Window* window,
} // namespace } // namespace
ScreenshotTaker::ScreenshotTaker(Profile* profile) ScreenshotTaker::ScreenshotTaker()
: profile_(profile), : ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)),
ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) { profile_for_test_(NULL) {
DCHECK(profile_);
} }
ScreenshotTaker::~ScreenshotTaker() { ScreenshotTaker::~ScreenshotTaker() {
...@@ -265,7 +274,7 @@ void ScreenshotTaker::HandleTakeScreenshotForAllRootWindows() { ...@@ -265,7 +274,7 @@ void ScreenshotTaker::HandleTakeScreenshotForAllRootWindows() {
if (GrabWindowSnapshot(root_window, rect, &png_data->data())) { if (GrabWindowSnapshot(root_window, rect, &png_data->data())) {
PostSaveScreenshotTask( PostSaveScreenshotTask(
base::Bind(&ScreenshotTaker::ShowNotification, factory_.GetWeakPtr()), base::Bind(&ScreenshotTaker::ShowNotification, factory_.GetWeakPtr()),
profile_, GetProfile(),
screenshot_path, screenshot_path,
png_data); png_data);
} else { } else {
...@@ -302,7 +311,7 @@ void ScreenshotTaker::HandleTakePartialScreenshot( ...@@ -302,7 +311,7 @@ void ScreenshotTaker::HandleTakePartialScreenshot(
last_screenshot_timestamp_ = base::Time::Now(); last_screenshot_timestamp_ = base::Time::Now();
PostSaveScreenshotTask( PostSaveScreenshotTask(
base::Bind(&ScreenshotTaker::ShowNotification, factory_.GetWeakPtr()), base::Bind(&ScreenshotTaker::ShowNotification, factory_.GetWeakPtr()),
profile_, GetProfile(),
screenshot_path, screenshot_path,
png_data); png_data);
} else { } else {
...@@ -326,9 +335,11 @@ void ScreenshotTaker::ShowNotification( ...@@ -326,9 +335,11 @@ void ScreenshotTaker::ShowNotification(
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
// TODO(sschmitz): make this work for Windows. // TODO(sschmitz): make this work for Windows.
static int id = 0; const std::string notification_id(kNotificationId);
std::string id_text = base::StringPrintf("screenshot_%3.3d", ++id); // We cancel a previous screenshot notification, if any, to ensure we get
string16 replace_id = UTF8ToUTF16(id_text); // a fresh notification pop-up.
g_browser_process->notification_ui_manager()->CancelById(notification_id);
const string16 replace_id(UTF8ToUTF16(notification_id));
bool success = bool success =
(screenshot_result == ScreenshotTakerObserver::SCREENSHOT_SUCCESS); (screenshot_result == ScreenshotTakerObserver::SCREENSHOT_SUCCESS);
Notification notification( Notification notification(
...@@ -346,10 +357,9 @@ void ScreenshotTaker::ShowNotification( ...@@ -346,10 +357,9 @@ void ScreenshotTaker::ShowNotification(
WebKit::WebTextDirectionDefault, WebKit::WebTextDirectionDefault,
string16(), string16(),
replace_id, replace_id,
new ScreenshotTakerNotificationDelegate(id_text, new ScreenshotTakerNotificationDelegate(success, screenshot_path));
success, g_browser_process->notification_ui_manager()->Add(notification,
screenshot_path)); GetProfile());
g_browser_process->notification_ui_manager()->Add(notification, profile_);
#endif #endif
FOR_EACH_OBSERVER(ScreenshotTakerObserver, observers_, FOR_EACH_OBSERVER(ScreenshotTakerObserver, observers_,
...@@ -368,12 +378,22 @@ bool ScreenshotTaker::HasObserver(ScreenshotTakerObserver* observer) const { ...@@ -368,12 +378,22 @@ bool ScreenshotTaker::HasObserver(ScreenshotTakerObserver* observer) const {
return observers_.HasObserver(observer); return observers_.HasObserver(observer);
} }
Profile* ScreenshotTaker::GetProfile() {
if (profile_for_test_)
return profile_for_test_;
return ProfileManager::GetDefaultProfileOrOffTheRecord();
}
void ScreenshotTaker::SetScreenshotDirectoryForTest( void ScreenshotTaker::SetScreenshotDirectoryForTest(
const base::FilePath& directory) { const base::FilePath& directory) {
screenshot_directory_for_test_ = directory; screenshot_directory_for_test_ = directory;
} }
void ScreenshotTaker::SetScreenshotBasenameForTest( void ScreenshotTaker::SetScreenshotBasenameForTest(
const std::string& basename){ const std::string& basename) {
screenshot_basename_for_test_ = basename; screenshot_basename_for_test_ = basename;
} }
void ScreenshotTaker::SetScreenshotProfileForTest(Profile* profile) {
profile_for_test_ = profile;
}
...@@ -49,7 +49,7 @@ class ScreenshotTakerObserver { ...@@ -49,7 +49,7 @@ class ScreenshotTakerObserver {
class ScreenshotTaker : public ash::ScreenshotDelegate { class ScreenshotTaker : public ash::ScreenshotDelegate {
public: public:
explicit ScreenshotTaker(Profile* profile); ScreenshotTaker();
virtual ~ScreenshotTaker(); virtual ~ScreenshotTaker();
...@@ -70,10 +70,10 @@ class ScreenshotTaker : public ash::ScreenshotDelegate { ...@@ -70,10 +70,10 @@ class ScreenshotTaker : public ash::ScreenshotDelegate {
private: private:
friend class ash::test::ScreenshotTakerTest; friend class ash::test::ScreenshotTakerTest;
Profile* GetProfile();
void SetScreenshotDirectoryForTest(const base::FilePath& directory); void SetScreenshotDirectoryForTest(const base::FilePath& directory);
void SetScreenshotBasenameForTest(const std::string& basename); void SetScreenshotBasenameForTest(const std::string& basename);
void SetScreenshotProfileForTest(Profile* profile);
Profile* profile_;
base::WeakPtrFactory<ScreenshotTaker> factory_; base::WeakPtrFactory<ScreenshotTaker> factory_;
...@@ -81,8 +81,10 @@ class ScreenshotTaker : public ash::ScreenshotDelegate { ...@@ -81,8 +81,10 @@ class ScreenshotTaker : public ash::ScreenshotDelegate {
base::Time last_screenshot_timestamp_; base::Time last_screenshot_timestamp_;
ObserverList<ScreenshotTakerObserver> observers_; ObserverList<ScreenshotTakerObserver> observers_;
base::FilePath screenshot_directory_for_test_; base::FilePath screenshot_directory_for_test_;
std::string screenshot_basename_for_test_; std::string screenshot_basename_for_test_;
Profile* profile_for_test_;
DISALLOW_COPY_AND_ASSIGN(ScreenshotTaker); DISALLOW_COPY_AND_ASSIGN(ScreenshotTaker);
}; };
......
...@@ -63,7 +63,7 @@ class ScreenshotTakerTest : public AshTestBase, ...@@ -63,7 +63,7 @@ class ScreenshotTakerTest : public AshTestBase,
protected: protected:
// ScreenshotTakerTest is a friend of ScreenshotTaker and therefore // ScreenshotTakerTest is a friend of ScreenshotTaker and therefore
// allowed to set the directory and basename. // allowed to set the directory, basename and profile.
void SetScreenshotDirectoryForTest( void SetScreenshotDirectoryForTest(
ScreenshotTaker* screenshot_taker, ScreenshotTaker* screenshot_taker,
const base::FilePath& screenshot_directory) { const base::FilePath& screenshot_directory) {
...@@ -74,6 +74,11 @@ class ScreenshotTakerTest : public AshTestBase, ...@@ -74,6 +74,11 @@ class ScreenshotTakerTest : public AshTestBase,
const std::string& screenshot_basename) { const std::string& screenshot_basename) {
screenshot_taker->SetScreenshotBasenameForTest(screenshot_basename); screenshot_taker->SetScreenshotBasenameForTest(screenshot_basename);
} }
void SetScreenshotProfileForTest(
ScreenshotTaker* screenshot_taker,
Profile* profile) {
screenshot_taker->SetScreenshotProfileForTest(profile);
}
void Wait() { void Wait() {
if (screenshot_complete_) if (screenshot_complete_)
...@@ -97,12 +102,13 @@ class ScreenshotTakerTest : public AshTestBase, ...@@ -97,12 +102,13 @@ class ScreenshotTakerTest : public AshTestBase,
TEST_F(ScreenshotTakerTest, TakeScreenshot) { TEST_F(ScreenshotTakerTest, TakeScreenshot) {
TestingProfile profile; TestingProfile profile;
ScreenshotTaker screenshot_taker(&profile); ScreenshotTaker screenshot_taker;
screenshot_taker.AddObserver(this); screenshot_taker.AddObserver(this);
base::ScopedTempDir directory; base::ScopedTempDir directory;
ASSERT_TRUE(directory.CreateUniqueTempDir()); ASSERT_TRUE(directory.CreateUniqueTempDir());
SetScreenshotDirectoryForTest(&screenshot_taker, directory.path()); SetScreenshotDirectoryForTest(&screenshot_taker, directory.path());
SetScreenshotBasenameForTest(&screenshot_taker, "Screenshot"); SetScreenshotBasenameForTest(&screenshot_taker, "Screenshot");
SetScreenshotProfileForTest(&screenshot_taker, &profile);
EXPECT_TRUE(screenshot_taker.CanTakeScreenshot()); EXPECT_TRUE(screenshot_taker.CanTakeScreenshot());
...@@ -116,7 +122,7 @@ TEST_F(ScreenshotTakerTest, TakeScreenshot) { ...@@ -116,7 +122,7 @@ TEST_F(ScreenshotTakerTest, TakeScreenshot) {
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
// Screenshot notifications on Windows not yet turned on. // Screenshot notifications on Windows not yet turned on.
EXPECT_TRUE(g_browser_process->notification_ui_manager()->DoesIdExist( EXPECT_TRUE(g_browser_process->notification_ui_manager()->DoesIdExist(
std::string("screenshot_001"))); std::string("screenshot")));
g_browser_process->notification_ui_manager()->CancelAll(); g_browser_process->notification_ui_manager()->CancelAll();
#endif #endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment