summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsadrul@chromium.org <sadrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-04-27 02:06:32 +0000
committersadrul@chromium.org <sadrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-04-27 02:06:32 +0000
commite6961e79b0e8a6d1c111503f558cb5fd0566fdbe (patch)
treeee8fb4f27aba9befc6b05c76c56346bef396bb87
parent0918ceec6f1365c15fb4bd94f3a36fa4a9ade41b (diff)
downloadchromium_src-e6961e79b0e8a6d1c111503f558cb5fd0566fdbe.zip
chromium_src-e6961e79b0e8a6d1c111503f558cb5fd0566fdbe.tar.gz
chromium_src-e6961e79b0e8a6d1c111503f558cb5fd0566fdbe.tar.bz2
gesture nav: Encode screenshots in a worker thread.
Screenshots can be large, and png-encoding them in the UI thread is just a very bad idea (because it can take a long time). BUG=235739 R=piman@chromium.org Review URL: https://codereview.chromium.org/14335003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@196919 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--content/browser/web_contents/navigation_controller_impl_unittest.cc23
-rw-r--r--content/browser/web_contents/navigation_entry_impl.cc4
-rw-r--r--content/browser/web_contents/navigation_entry_impl.h2
-rw-r--r--content/browser/web_contents/web_contents_screenshot_manager.cc78
-rw-r--r--content/browser/web_contents/web_contents_screenshot_manager.h18
-rw-r--r--content/browser/web_contents/web_contents_view_aura_browsertest.cc27
6 files changed, 133 insertions, 19 deletions
diff --git a/content/browser/web_contents/navigation_controller_impl_unittest.cc b/content/browser/web_contents/navigation_controller_impl_unittest.cc
index 320c8aa..a8433e7 100644
--- a/content/browser/web_contents/navigation_controller_impl_unittest.cc
+++ b/content/browser/web_contents/navigation_controller_impl_unittest.cc
@@ -34,6 +34,7 @@
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/test/mock_render_process_host.h"
#include "content/public/test/test_notification_tracker.h"
+#include "content/public/test/test_utils.h"
#include "content/test/test_web_contents.h"
#include "net/base/net_util.h"
#include "skia/ext/platform_canvas.h"
@@ -73,7 +74,8 @@ bool DoImagesMatch(const gfx::Image& a, const gfx::Image& b) {
class MockScreenshotManager : public content::WebContentsScreenshotManager {
public:
explicit MockScreenshotManager(content::NavigationControllerImpl* owner)
- : content::WebContentsScreenshotManager(owner) {
+ : content::WebContentsScreenshotManager(owner),
+ encoding_screenshot_in_progress_(false) {
}
virtual ~MockScreenshotManager() {
@@ -84,13 +86,22 @@ class MockScreenshotManager : public content::WebContentsScreenshotManager {
bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1, 1);
bitmap.allocPixels();
bitmap.eraseRGB(0, 0, 0);
+ encoding_screenshot_in_progress_ = true;
OnScreenshotTaken(entry->GetUniqueID(), true, bitmap);
+ WaitUntilScreenshotIsReady();
}
int GetScreenshotCount() {
return content::WebContentsScreenshotManager::GetScreenshotCount();
}
+ void WaitUntilScreenshotIsReady() {
+ if (!encoding_screenshot_in_progress_)
+ return;
+ message_loop_runner_ = new content::MessageLoopRunner;
+ message_loop_runner_->Run();
+ }
+
private:
// Overridden from content::WebContentsScreenshotManager:
virtual void TakeScreenshotImpl(
@@ -98,6 +109,16 @@ class MockScreenshotManager : public content::WebContentsScreenshotManager {
content::NavigationEntryImpl* entry) OVERRIDE {
}
+ virtual void OnScreenshotSet(content::NavigationEntryImpl* entry) OVERRIDE {
+ encoding_screenshot_in_progress_ = false;
+ WebContentsScreenshotManager::OnScreenshotSet(entry);
+ if (message_loop_runner_)
+ message_loop_runner_->Quit();
+ }
+
+ scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
+ bool encoding_screenshot_in_progress_;
+
DISALLOW_COPY_AND_ASSIGN(MockScreenshotManager);
};
diff --git a/content/browser/web_contents/navigation_entry_impl.cc b/content/browser/web_contents/navigation_entry_impl.cc
index dafc2e0..b3b6f78 100644
--- a/content/browser/web_contents/navigation_entry_impl.cc
+++ b/content/browser/web_contents/navigation_entry_impl.cc
@@ -310,8 +310,8 @@ void NavigationEntryImpl::ClearExtraData(const std::string& key) {
}
void NavigationEntryImpl::SetScreenshotPNGData(
- const std::vector<unsigned char>& png_data) {
- screenshot_ = png_data.empty() ? NULL : new base::RefCountedBytes(png_data);
+ scoped_refptr<base::RefCountedBytes> png_data) {
+ screenshot_ = png_data;
if (screenshot_)
UMA_HISTOGRAM_MEMORY_KB("Overscroll.ScreenshotSize", screenshot_->size());
}
diff --git a/content/browser/web_contents/navigation_entry_impl.h b/content/browser/web_contents/navigation_entry_impl.h
index 62234bf..c5d8c49 100644
--- a/content/browser/web_contents/navigation_entry_impl.h
+++ b/content/browser/web_contents/navigation_entry_impl.h
@@ -185,7 +185,7 @@ class CONTENT_EXPORT NavigationEntryImpl
should_replace_entry_ = should_replace_entry;
}
- void SetScreenshotPNGData(const std::vector<unsigned char>& png_data);
+ void SetScreenshotPNGData(scoped_refptr<base::RefCountedBytes> png_data);
const scoped_refptr<base::RefCountedBytes> screenshot() const {
return screenshot_;
}
diff --git a/content/browser/web_contents/web_contents_screenshot_manager.cc b/content/browser/web_contents/web_contents_screenshot_manager.cc
index 9b8750f..49cb0c6 100644
--- a/content/browser/web_contents/web_contents_screenshot_manager.cc
+++ b/content/browser/web_contents/web_contents_screenshot_manager.cc
@@ -5,6 +5,7 @@
#include "content/browser/web_contents/web_contents_screenshot_manager.h"
#include "base/command_line.h"
+#include "base/threading/worker_pool.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/web_contents/navigation_controller_impl.h"
#include "content/browser/web_contents/navigation_entry_impl.h"
@@ -23,10 +24,45 @@ const int kMinScreenshotIntervalMS = 1000;
namespace content {
+// Encodes an SkBitmap to PNG data in a worker thread.
+class ScreenshotData : public base::RefCountedThreadSafe<ScreenshotData> {
+ public:
+ ScreenshotData() {
+ }
+
+ void EncodeScreenshot(const SkBitmap& bitmap, base::Closure callback) {
+ if (!base::WorkerPool::PostTaskAndReply(FROM_HERE,
+ base::Bind(&ScreenshotData::EncodeOnWorker,
+ this,
+ bitmap),
+ callback,
+ true)) {
+ callback.Run();
+ }
+ }
+
+ scoped_refptr<base::RefCountedBytes> data() const { return data_; }
+
+ private:
+ friend class base::RefCountedThreadSafe<ScreenshotData>;
+ virtual ~ScreenshotData() {
+ }
+
+ void EncodeOnWorker(const SkBitmap& bitmap) {
+ std::vector<unsigned char> data;
+ if (gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, true, &data))
+ data_ = new base::RefCountedBytes(data);
+ }
+
+ scoped_refptr<base::RefCountedBytes> data_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScreenshotData);
+};
+
WebContentsScreenshotManager::WebContentsScreenshotManager(
NavigationControllerImpl* owner)
: owner_(owner),
- ALLOW_THIS_IN_INITIALIZER_LIST(take_screenshot_factory_(this)),
+ screenshot_factory_(this),
min_screenshot_interval_ms_(kMinScreenshotIntervalMS) {
}
@@ -85,7 +121,7 @@ void WebContentsScreenshotManager::TakeScreenshotImpl(
host->CopyFromBackingStore(gfx::Rect(),
host->GetView()->GetViewBounds().size(),
base::Bind(&WebContentsScreenshotManager::OnScreenshotTaken,
- take_screenshot_factory_.GetWeakPtr(),
+ screenshot_factory_.GetWeakPtr(),
entry->GetUniqueID()));
}
@@ -117,13 +153,13 @@ void WebContentsScreenshotManager::OnScreenshotTaken(int unique_id,
return;
}
- std::vector<unsigned char> data;
- if (gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, true, &data)) {
- entry->SetScreenshotPNGData(data);
- PurgeScreenshotsIfNecessary();
- } else {
- ClearScreenshot(entry);
- }
+ scoped_refptr<ScreenshotData> screenshot = new ScreenshotData();
+ screenshot->EncodeScreenshot(
+ bitmap,
+ base::Bind(&WebContentsScreenshotManager::OnScreenshotEncodeComplete,
+ screenshot_factory_.GetWeakPtr(),
+ unique_id,
+ screenshot));
}
int WebContentsScreenshotManager::GetScreenshotCount() const {
@@ -138,11 +174,33 @@ int WebContentsScreenshotManager::GetScreenshotCount() const {
return screenshot_count;
}
+void WebContentsScreenshotManager::OnScreenshotEncodeComplete(
+ int unique_id,
+ scoped_refptr<ScreenshotData> screenshot) {
+ NavigationEntryImpl* entry = NULL;
+ int entry_count = owner_->GetEntryCount();
+ for (int i = 0; i < entry_count; ++i) {
+ NavigationEntry* iter = owner_->GetEntryAtIndex(i);
+ if (iter->GetUniqueID() == unique_id) {
+ entry = NavigationEntryImpl::FromNavigationEntry(iter);
+ break;
+ }
+ }
+ if (!entry)
+ return;
+ entry->SetScreenshotPNGData(screenshot->data());
+ OnScreenshotSet(entry);
+}
+
+void WebContentsScreenshotManager::OnScreenshotSet(NavigationEntryImpl* entry) {
+ PurgeScreenshotsIfNecessary();
+}
+
bool WebContentsScreenshotManager::ClearScreenshot(NavigationEntryImpl* entry) {
if (!entry->screenshot())
return false;
- entry->SetScreenshotPNGData(std::vector<unsigned char>());
+ entry->SetScreenshotPNGData(NULL);
return true;
}
diff --git a/content/browser/web_contents/web_contents_screenshot_manager.h b/content/browser/web_contents/web_contents_screenshot_manager.h
index 54e58ed..2162cf4 100644
--- a/content/browser/web_contents/web_contents_screenshot_manager.h
+++ b/content/browser/web_contents/web_contents_screenshot_manager.h
@@ -17,6 +17,7 @@ namespace content {
class NavigationControllerImpl;
class NavigationEntryImpl;
class RenderViewHost;
+class ScreenshotData;
// WebContentsScreenshotManager takes care of taking image-captures for the
// current navigation entry of a NavigationControllerImpl, and managing these
@@ -37,6 +38,10 @@ class CONTENT_EXPORT WebContentsScreenshotManager {
virtual void TakeScreenshotImpl(RenderViewHost* host,
NavigationEntryImpl* entry);
+ // Called after a screenshot has been set on an NavigationEntryImpl.
+ // Overridden in tests to get notified of when a screenshot is set.
+ virtual void OnScreenshotSet(NavigationEntryImpl* entry);
+
NavigationControllerImpl* owner() { return owner_; }
void SetMinScreenshotIntervalMS(int interval_ms);
@@ -51,6 +56,11 @@ class CONTENT_EXPORT WebContentsScreenshotManager {
int GetScreenshotCount() const;
private:
+ // This is called when the screenshot data has beene encoded to PNG in a
+ // worker thread.
+ void OnScreenshotEncodeComplete(int unique_id,
+ scoped_refptr<ScreenshotData> data);
+
// Removes the screenshot for the entry, returning true if the entry had a
// screenshot.
bool ClearScreenshot(NavigationEntryImpl* entry);
@@ -63,10 +73,10 @@ class CONTENT_EXPORT WebContentsScreenshotManager {
// The navigation controller that owns this screenshot-manager.
NavigationControllerImpl* owner_;
- // Taking a screenshot can be async. So use a weakptr for the callback to make
- // sure that the screenshot completion callback does not trigger on a
- // destroyed WebContentsScreenshotManager.
- base::WeakPtrFactory<WebContentsScreenshotManager> take_screenshot_factory_;
+ // Taking a screenshot and encoding them can be async. So use a weakptr for
+ // the callback to make sure that the screenshot/encoding completion callback
+ // does not trigger on a destroyed WebContentsScreenshotManager.
+ base::WeakPtrFactory<WebContentsScreenshotManager> screenshot_factory_;
base::Time last_screenshot_time_;
int min_screenshot_interval_ms_;
diff --git a/content/browser/web_contents/web_contents_view_aura_browsertest.cc b/content/browser/web_contents/web_contents_view_aura_browsertest.cc
index a18b994..a6cdfe3 100644
--- a/content/browser/web_contents/web_contents_view_aura_browsertest.cc
+++ b/content/browser/web_contents/web_contents_view_aura_browsertest.cc
@@ -33,7 +33,8 @@ class ScreenshotTracker : public WebContentsScreenshotManager {
public:
explicit ScreenshotTracker(NavigationControllerImpl* controller)
: WebContentsScreenshotManager(controller),
- screenshot_taken_for_(NULL) {
+ screenshot_taken_for_(NULL),
+ waiting_for_screenshots_(0) {
}
virtual ~ScreenshotTracker() {
@@ -49,15 +50,32 @@ class ScreenshotTracker : public WebContentsScreenshotManager {
SetMinScreenshotIntervalMS(interval_ms);
}
+ void WaitUntilScreenshotIsReady() {
+ if (!waiting_for_screenshots_)
+ return;
+ message_loop_runner_ = new content::MessageLoopRunner;
+ message_loop_runner_->Run();
+ }
+
private:
// Overridden from WebContentsScreenshotManager:
virtual void TakeScreenshotImpl(RenderViewHost* host,
NavigationEntryImpl* entry) OVERRIDE {
+ ++waiting_for_screenshots_;
screenshot_taken_for_ = host;
WebContentsScreenshotManager::TakeScreenshotImpl(host, entry);
}
+ virtual void OnScreenshotSet(NavigationEntryImpl* entry) OVERRIDE {
+ --waiting_for_screenshots_;
+ WebContentsScreenshotManager::OnScreenshotSet(entry);
+ if (waiting_for_screenshots_ == 0 && message_loop_runner_)
+ message_loop_runner_->Quit();
+ }
+
RenderViewHost* screenshot_taken_for_;
+ scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
+ int waiting_for_screenshots_;
DISALLOW_COPY_AND_ASSIGN(ScreenshotTracker);
};
@@ -321,6 +339,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest,
EXPECT_EQ(1, GetCurrentIndex());
ExecuteSyncJSFunction(view_host, "navigate_next()");
EXPECT_EQ(2, GetCurrentIndex());
+ screenshot_manager()->WaitUntilScreenshotIsReady();
// The current entry won't have any screenshots. But the entries in the
// history should now have screenshots.
@@ -339,6 +358,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest,
// Navigate again. Index 2 should now have a screenshot.
ExecuteSyncJSFunction(view_host, "navigate_next()");
EXPECT_EQ(3, GetCurrentIndex());
+ screenshot_manager()->WaitUntilScreenshotIsReady();
entry = NavigationEntryImpl::FromNavigationEntry(
web_contents->GetController().GetEntryAtIndex(2));
@@ -364,6 +384,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest,
string16 actual_title = title_watcher.WaitAndGetTitle();
EXPECT_EQ(expected_title, actual_title);
EXPECT_EQ(2, GetCurrentIndex());
+ screenshot_manager()->WaitUntilScreenshotIsReady();
entry = NavigationEntryImpl::FromNavigationEntry(
web_contents->GetController().GetEntryAtIndex(3));
EXPECT_TRUE(entry->screenshot().get());
@@ -374,6 +395,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest,
EXPECT_EQ(3, GetCurrentIndex());
ExecuteSyncJSFunction(view_host, "navigate_next()");
EXPECT_EQ(4, GetCurrentIndex());
+ screenshot_manager()->WaitUntilScreenshotIsReady();
entry = NavigationEntryImpl::FromNavigationEntry(
web_contents->GetController().GetEntryAtIndex(4));
EXPECT_FALSE(entry->screenshot().get());
@@ -386,6 +408,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest,
string16 actual_title = title_watcher.WaitAndGetTitle();
EXPECT_EQ(expected_title, actual_title);
EXPECT_EQ(3, GetCurrentIndex());
+ screenshot_manager()->WaitUntilScreenshotIsReady();
entry = NavigationEntryImpl::FromNavigationEntry(
web_contents->GetController().GetEntryAtIndex(4));
EXPECT_TRUE(entry->screenshot().get());
@@ -431,6 +454,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest,
RenderViewHost* old_host = web_contents->GetRenderViewHost();
web_contents->GetController().LoadURLWithParams(params);
WaitForLoadStop(web_contents);
+ screenshot_manager()->WaitUntilScreenshotIsReady();
EXPECT_NE(old_host, web_contents->GetRenderViewHost())
<< navigations[i].url.spec();
@@ -455,6 +479,7 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest,
params.transition_type = PageTransitionFromInt(navigations[0].transition);
web_contents->GetController().LoadURLWithParams(params);
WaitForLoadStop(web_contents);
+ screenshot_manager()->WaitUntilScreenshotIsReady();
EXPECT_EQ(NULL, screenshot_manager()->screenshot_taken_for());
}