summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorskerner@chromium.org <skerner@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-01-11 22:40:34 +0000
committerskerner@chromium.org <skerner@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-01-11 22:40:34 +0000
commite5518c760e5a8b4269eeab6849d670b0138c9462 (patch)
treeaf4a4bded911ffe719fbd0739780992399a3d0a8
parent279e814726e6a3657c3b9e6ce458e13fd895df96 (diff)
downloadchromium_src-e5518c760e5a8b4269eeab6849d670b0138c9462.zip
chromium_src-e5518c760e5a8b4269eeab6849d670b0138c9462.tar.gz
chromium_src-e5518c760e5a8b4269eeab6849d670b0138c9462.tar.bz2
If a backing store is not available, chrome.tabs.captureVisibleTab() asks the renderer for a snapshot of the page.
BUG=19274 TEST=Manual testing on Mac,Linux,Win Review URL: http://codereview.chromium.org/502034 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@35951 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/extension_tabs_module.cc77
-rw-r--r--chrome/browser/extensions/extension_tabs_module.h14
-rw-r--r--chrome/browser/renderer_host/render_view_host.cc12
-rw-r--r--chrome/browser/renderer_host/render_view_host.h4
-rw-r--r--chrome/common/notification_type.h3
-rw-r--r--chrome/common/render_messages_internal.h8
-rw-r--r--chrome/renderer/render_view.cc94
-rw-r--r--chrome/renderer/render_view.h9
8 files changed, 184 insertions, 37 deletions
diff --git a/chrome/browser/extensions/extension_tabs_module.cc b/chrome/browser/extensions/extension_tabs_module.cc
index 64ee348..1e36294 100644
--- a/chrome/browser/extensions/extension_tabs_module.cc
+++ b/chrome/browser/extensions/extension_tabs_module.cc
@@ -729,25 +729,52 @@ bool CaptureVisibleTabFunction::RunImpl() {
return false;
}
- SkBitmap screen_capture;
TabContents* tab_contents = browser->GetSelectedTabContents();
if (!tab_contents) {
error_ = keys::kInternalVisibleTabCaptureError;
return false;
}
RenderViewHost* render_view_host = tab_contents->render_view_host();
+
+ // If a backing store is cached for the tab we want to capture,
+ // then use it to generate the image.
BackingStore* backing_store = render_view_host->GetBackingStore(false);
- if (!backing_store) {
- error_ = keys::kInternalVisibleTabCaptureError;
- return false;
+ if (backing_store) {
+ CaptureSnapshotFromBackingStore(backing_store);
+ return true;
}
+
+ // TODO: If a paint request is pending, wait for it to finish rather than
+ // asking the renderer to capture a snapshot. It is possible for a paint
+ // request to be queued for a long time by the renderer. For example, if
+ // a tab is hidden, the repaint is not done until the tab is un-hidden.
+ // To avoid waiting for a long time, there could be a timeout after which
+ // we ask for a snapshot, or the renderer could send a NACK to paint
+ // requests it defers.
+
+ // Explicitly ask for a snapshot of the tab.
+ render_view_host->CaptureSnapshot();
+ registrar_.Add(this,
+ NotificationType::TAB_SNAPSHOT_TAKEN,
+ NotificationService::AllSources());
+ AddRef(); // Balanced in CaptureVisibleTabFunction::Observe().
+
+ return true;
+}
+
+// Build the image of a tab's contents out of a backing store.
+void CaptureVisibleTabFunction::CaptureSnapshotFromBackingStore(
+ BackingStore* backing_store) {
+ SkBitmap screen_capture;
+
#if defined(OS_WIN)
skia::PlatformCanvas temp_canvas;
if (!temp_canvas.initialize(backing_store->size().width(),
backing_store->size().height(), true)) {
error_ = ExtensionErrorUtils::FormatErrorMessage(
keys::kInternalVisibleTabCaptureError, "");
- return false;
+ SendResponse(false);
+ return;
}
HDC temp_dc = temp_canvas.beginPlatformPaint();
BitBlt(temp_dc,
@@ -762,7 +789,8 @@ bool CaptureVisibleTabFunction::RunImpl() {
backing_store->size().height(), true)) {
error_ = ExtensionErrorUtils::FormatErrorMessage(
keys::kInternalVisibleTabCaptureError, "");
- return false;
+ SendResponse(false);
+ return;
}
CGContextRef temp_context = temp_canvas.beginPlatformPaint();
CGContextSaveGState(temp_context);
@@ -781,8 +809,38 @@ bool CaptureVisibleTabFunction::RunImpl() {
#else
// TODO(port)
error_ = keys::kNotImplementedError;
- return false;
+ SendResponse(false);
+ return;
#endif
+
+ SendResultFromBitmap(screen_capture);
+}
+
+// If a backing store was not available in CaptureVisibleTabFunction::RunImpl,
+// than the renderer was asked for a snapshot. Listen for a notification
+// that the snapshot is available.
+void CaptureVisibleTabFunction::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK(type == NotificationType::TAB_SNAPSHOT_TAKEN);
+
+ const SkBitmap *screen_capture = Details<const SkBitmap>(details).ptr();
+ const bool error = screen_capture->empty();
+
+ if (error) {
+ error_ = keys::kInternalVisibleTabCaptureError;
+ SendResponse(false);
+ } else {
+ SendResultFromBitmap(*screen_capture);
+ }
+
+ Release(); // Balanced in CaptureVisibleTabFunction::RunImpl().
+}
+
+// Turn a bitmap of the screen into an image, set that image as the result,
+// and call SendResponse().
+void CaptureVisibleTabFunction::SendResultFromBitmap(
+ const SkBitmap& screen_capture) {
scoped_refptr<RefCountedBytes> jpeg_data(new RefCountedBytes);
SkAutoLockPixels screen_capture_lock(screen_capture);
bool encoded = gfx::JPEGCodec::Encode(
@@ -794,7 +852,8 @@ bool CaptureVisibleTabFunction::RunImpl() {
if (!encoded) {
error_ = ExtensionErrorUtils::FormatErrorMessage(
keys::kInternalVisibleTabCaptureError, "");
- return false;
+ SendResponse(false);
+ return;
}
std::string base64_result;
@@ -807,7 +866,7 @@ bool CaptureVisibleTabFunction::RunImpl() {
base::Base64Encode(stream_as_string, &base64_result);
base64_result.insert(0, "data:image/jpg;base64,");
result_.reset(new StringValue(base64_result));
- return true;
+ SendResponse(true);
}
bool DetectTabLanguageFunction::RunImpl() {
diff --git a/chrome/browser/extensions/extension_tabs_module.h b/chrome/browser/extensions/extension_tabs_module.h
index 1fa7f21..a4d6f5d 100644
--- a/chrome/browser/extensions/extension_tabs_module.h
+++ b/chrome/browser/extensions/extension_tabs_module.h
@@ -11,9 +11,11 @@
#include "chrome/common/notification_service.h"
#include "chrome/common/notification_registrar.h"
+class BackingStore;
class Browser;
class DictionaryValue;
class ListValue;
+class SkBitmap;
class TabContents;
class TabStripModel;
@@ -134,9 +136,19 @@ class DetectTabLanguageFunction : public AsyncExtensionFunction,
NotificationRegistrar registrar_;
DECLARE_EXTENSION_FUNCTION_NAME("tabs.detectLanguage")
};
-class CaptureVisibleTabFunction : public SyncExtensionFunction {
+class CaptureVisibleTabFunction : public AsyncExtensionFunction,
+ public NotificationObserver {
+ private:
~CaptureVisibleTabFunction() {}
virtual bool RunImpl();
+ virtual void CaptureSnapshotFromBackingStore(BackingStore* backing_store);
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+ virtual void SendResultFromBitmap(const SkBitmap& screen_capture);
+
+ NotificationRegistrar registrar_;
+
DECLARE_EXTENSION_FUNCTION_NAME("tabs.captureVisibleTab")
};
diff --git a/chrome/browser/renderer_host/render_view_host.cc b/chrome/browser/renderer_host/render_view_host.cc
index f1a48f1..fa2c4b2 100644
--- a/chrome/browser/renderer_host/render_view_host.cc
+++ b/chrome/browser/renderer_host/render_view_host.cc
@@ -575,6 +575,10 @@ void RenderViewHost::CaptureThumbnail() {
Send(new ViewMsg_CaptureThumbnail(routing_id()));
}
+void RenderViewHost::CaptureSnapshot() {
+ Send(new ViewMsg_CaptureSnapshot(routing_id()));
+}
+
void RenderViewHost::JavaScriptMessageBoxClosed(IPC::Message* reply_msg,
bool success,
const std::wstring& prompt) {
@@ -739,6 +743,7 @@ void RenderViewHost::OnMessageReceived(const IPC::Message& msg) {
IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateEncoding, OnMsgUpdateEncoding)
IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateTargetURL, OnMsgUpdateTargetURL)
IPC_MESSAGE_HANDLER(ViewHostMsg_Thumbnail, OnMsgThumbnail)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_Snapshot, OnMsgScreenshot)
IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateInspectorSettings,
OnUpdateInspectorSettings);
IPC_MESSAGE_HANDLER(ViewHostMsg_Close, OnMsgClose)
@@ -1023,6 +1028,13 @@ void RenderViewHost::OnMsgThumbnail(const GURL& url,
delegate_->UpdateThumbnail(url, bitmap, score);
}
+void RenderViewHost::OnMsgScreenshot(const SkBitmap& bitmap) {
+ NotificationService::current()->Notify(
+ NotificationType::TAB_SNAPSHOT_TAKEN,
+ Source<RenderViewHost>(this),
+ Details<const SkBitmap>(&bitmap));
+}
+
void RenderViewHost::OnUpdateInspectorSettings(
const std::string& raw_settings) {
delegate_->UpdateInspectorSettings(raw_settings);
diff --git a/chrome/browser/renderer_host/render_view_host.h b/chrome/browser/renderer_host/render_view_host.h
index 0d923ca..7c3dbb3 100644
--- a/chrome/browser/renderer_host/render_view_host.h
+++ b/chrome/browser/renderer_host/render_view_host.h
@@ -294,6 +294,9 @@ class RenderViewHost : public RenderWidgetHost,
// Captures a thumbnail representation of the page.
void CaptureThumbnail();
+ // Captures a snapshot of the page.
+ void CaptureSnapshot();
+
// Notifies the RenderView that the JavaScript message that was shown was
// closed by the user.
void JavaScriptMessageBoxClosed(IPC::Message* reply_msg,
@@ -470,6 +473,7 @@ class RenderViewHost : public RenderWidgetHost,
void OnMsgThumbnail(const GURL& url,
const ThumbnailScore& score,
const SkBitmap& bitmap);
+ void OnMsgScreenshot(const SkBitmap& bitmap);
void OnMsgClose();
void OnMsgRequestMove(const gfx::Rect& pos);
void OnMsgDidRedirectProvisionalLoad(int32 page_id,
diff --git a/chrome/common/notification_type.h b/chrome/common/notification_type.h
index 97a28f3..d88b97e 100644
--- a/chrome/common/notification_type.h
+++ b/chrome/common/notification_type.h
@@ -227,6 +227,9 @@ class NotificationType {
// are Details<std::string> and the source is Source<RenderViewHost>.
TAB_LANGUAGE_DETERMINED,
+ // Sent after the renderer returns a snapshot of tab contents.
+ TAB_SNAPSHOT_TAKEN,
+
// Send after the code is run in specified tab.
TAB_CODE_EXECUTED,
diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h
index 453cdf3..b4e1e92 100644
--- a/chrome/common/render_messages_internal.h
+++ b/chrome/common/render_messages_internal.h
@@ -128,6 +128,10 @@ IPC_BEGIN_MESSAGES(View)
// render view responds with a ViewHostMsg_Thumbnail.
IPC_MESSAGE_ROUTED0(ViewMsg_CaptureThumbnail)
+ // Tells the render view to capture a thumbnail image of the page. The
+ // render view responds with a ViewHostMsg_Snapshot.
+ IPC_MESSAGE_ROUTED0(ViewMsg_CaptureSnapshot)
+
// Tells the render view to switch the CSS to print media type, renders every
// requested pages and switch back the CSS to display media type.
IPC_MESSAGE_ROUTED0(ViewMsg_PrintPages)
@@ -1139,6 +1143,10 @@ IPC_BEGIN_MESSAGES(ViewHost)
ThumbnailScore /* score */,
SkBitmap /* bitmap */)
+ // Send a snapshot of the tab contents to the render host.
+ IPC_MESSAGE_ROUTED1(ViewHostMsg_Snapshot,
+ SkBitmap /* bitmap */)
+
// Notification that the url for the favicon of a site has been determined.
IPC_MESSAGE_ROUTED2(ViewHostMsg_UpdateFavIconURL,
int32 /* page_id */,
diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc
index da24728..2dc6b75 100644
--- a/chrome/renderer/render_view.cc
+++ b/chrome/renderer/render_view.cc
@@ -17,10 +17,12 @@
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/field_trial.h"
+#include "base/histogram.h"
#include "base/process_util.h"
#include "base/singleton.h"
#include "base/string_piece.h"
#include "base/string_util.h"
+#include "base/time.h"
#include "build/build_config.h"
#include "chrome/common/bindings_policy.h"
#include "chrome/common/child_process_logging.h"
@@ -172,7 +174,6 @@ using WebKit::WebWorkerClient;
// define to write the time necessary for thumbnail/DOM text retrieval,
// respectively, into the system debug log
-// #define TIME_BITMAP_RETRIEVAL
// #define TIME_TEXT_RETRIEVAL
// maximum number of characters in the document to index, any text beyond this
@@ -230,6 +231,35 @@ static bool UrlMatchesPermissions(
return false;
}
+static bool PaintViewIntoCanvas(WebView* view,
+ skia::PlatformCanvas& canvas) {
+ view->layout();
+ const WebSize& size = view->size();
+
+ if (!canvas.initialize(size.width, size.height, true))
+ return false;
+
+ view->paint(webkit_glue::ToWebCanvas(&canvas),
+ WebRect(0, 0, size.width, size.height));
+ // TODO: Add a way to snapshot the whole page, not just the currently
+ // visible part.
+
+ return true;
+}
+
+// Calculates how "boring" a thumbnail is. The boring score is the
+// 0,1 ranged percentage of pixels that are the most common
+// luma. Higher boring scores indicate that a higher percentage of a
+// bitmap are all the same brightness.
+static double CalculateBoringScore(SkBitmap* bitmap) {
+ int histogram[256] = {0};
+ color_utils::BuildLumaHistogram(bitmap, histogram);
+
+ int color_count = *std::max_element(histogram, histogram + 256);
+ int pixel_count = bitmap->width() * bitmap->height();
+ return static_cast<double>(color_count) / pixel_count;
+}
+
///////////////////////////////////////////////////////////////////////////////
int32 RenderView::next_page_id_ = 1;
@@ -422,6 +452,7 @@ void RenderView::OnMessageReceived(const IPC::Message& message) {
IPC_BEGIN_MESSAGE_MAP(RenderView, message)
IPC_MESSAGE_HANDLER(ViewMsg_CaptureThumbnail, SendThumbnail)
+ IPC_MESSAGE_HANDLER(ViewMsg_CaptureSnapshot, SendSnapshot)
IPC_MESSAGE_HANDLER(ViewMsg_PrintPages, OnPrintPages)
IPC_MESSAGE_HANDLER(ViewMsg_PrintingDone, OnPrintingDone)
IPC_MESSAGE_HANDLER(ViewMsg_Navigate, OnNavigate)
@@ -551,6 +582,24 @@ void RenderView::SendThumbnail() {
Send(new ViewHostMsg_Thumbnail(routing_id_, url, score, thumbnail));
}
+void RenderView::SendSnapshot() {
+ SkBitmap snapshot;
+ bool error = false;
+
+ WebFrame* main_frame = webview()->mainFrame();
+ if (!main_frame)
+ error = true;
+
+ if (!error && !CaptureSnapshot(webview(), &snapshot))
+ error = true;
+
+ DCHECK(error == snapshot.empty()) <<
+ "Snapshot should be empty on error, non-empty otherwise.";
+
+ // Send the snapshot to the browser process.
+ Send(new ViewHostMsg_Snapshot(routing_id_, snapshot));
+}
+
void RenderView::OnPrintPages() {
DCHECK(webview());
if (webview()) {
@@ -666,18 +715,13 @@ bool RenderView::CaptureThumbnail(WebView* view,
int h,
SkBitmap* thumbnail,
ThumbnailScore* score) {
-#ifdef TIME_BITMAP_RETRIEVAL
- double begin = time_util::GetHighResolutionTimeNow();
-#endif
-
- view->layout();
- const WebSize& size = view->size();
+ base::TimeTicks beginning_time = base::TimeTicks::Now();
skia::PlatformCanvas canvas;
- if (!canvas.initialize(size.width, size.height, true))
+
+ // Paint |view| into |canvas|.
+ if (!PaintViewIntoCanvas(view, canvas))
return false;
- view->paint(webkit_glue::ToWebCanvas(&canvas),
- WebRect(0, 0, size.width, size.height));
skia::BitmapPlatformDevice& device =
static_cast<skia::BitmapPlatformDevice&>(canvas.getTopPlatformDevice());
@@ -725,22 +769,28 @@ bool RenderView::CaptureThumbnail(WebView* view,
score->boring_score = CalculateBoringScore(thumbnail);
-#ifdef TIME_BITMAP_RETRIEVAL
- double end = time_util::GetHighResolutionTimeNow();
- char buf[128];
- sprintf_s(buf, "thumbnail in %gms\n", (end - begin) * 1000);
- OutputDebugStringA(buf);
-#endif
+ HISTOGRAM_TIMES("Renderer4.Thumbnail",
+ base::TimeTicks::Now() - beginning_time);
return true;
}
-double RenderView::CalculateBoringScore(SkBitmap* bitmap) {
- int histogram[256] = {0};
- color_utils::BuildLumaHistogram(bitmap, histogram);
+bool RenderView::CaptureSnapshot(WebView* view, SkBitmap* snapshot) {
+ base::TimeTicks beginning_time = base::TimeTicks::Now();
- int color_count = *std::max_element(histogram, histogram + 256);
- int pixel_count = bitmap->width() * bitmap->height();
- return static_cast<double>(color_count) / pixel_count;
+ skia::PlatformCanvas canvas;
+ if (!PaintViewIntoCanvas(view, canvas))
+ return false;
+
+ skia::BitmapPlatformDevice& device =
+ static_cast<skia::BitmapPlatformDevice&>(canvas.getTopPlatformDevice());
+
+ const SkBitmap& bitmap = device.accessBitmap(false);
+ if (!bitmap.copyTo(snapshot, SkBitmap::kARGB_8888_Config))
+ return false;
+
+ HISTOGRAM_TIMES("Renderer4.Snapshot",
+ base::TimeTicks::Now() - beginning_time);
+ return true;
}
void RenderView::OnNavigate(const ViewMsg_Navigate_Params& params) {
diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h
index 32ea8c6..012c7cc 100644
--- a/chrome/renderer/render_view.h
+++ b/chrome/renderer/render_view.h
@@ -508,11 +508,9 @@ class RenderView : public RenderWidget,
SkBitmap* thumbnail,
ThumbnailScore* score);
- // Calculates how "boring" a thumbnail is. The boring score is the
- // 0,1 ranged percentage of pixels that are the most common
- // luma. Higher boring scores indicate that a higher percentage of a
- // bitmap are all the same brightness.
- double CalculateBoringScore(SkBitmap* bitmap);
+ // Capture a snapshot of a view. This is used to allow an extension
+ // to get a snapshot of a tab using chrome.tabs.captureVisibleTab().
+ bool CaptureSnapshot(WebKit::WebView* view, SkBitmap* snapshot);
bool RunJavaScriptMessage(int type,
const std::wstring& message,
@@ -526,6 +524,7 @@ class RenderView : public RenderWidget,
// RenderView IPC message handlers
void SendThumbnail();
+ void SendSnapshot();
void OnPrintPages();
void OnPrintingDone(int document_cookie, bool success);
void OnNavigate(const ViewMsg_Navigate_Params& params);