diff options
author | skerner@chromium.org <skerner@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-11 22:40:34 +0000 |
---|---|---|
committer | skerner@chromium.org <skerner@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-11 22:40:34 +0000 |
commit | e5518c760e5a8b4269eeab6849d670b0138c9462 (patch) | |
tree | af4a4bded911ffe719fbd0739780992399a3d0a8 | |
parent | 279e814726e6a3657c3b9e6ce458e13fd895df96 (diff) | |
download | chromium_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.cc | 77 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_tabs_module.h | 14 | ||||
-rw-r--r-- | chrome/browser/renderer_host/render_view_host.cc | 12 | ||||
-rw-r--r-- | chrome/browser/renderer_host/render_view_host.h | 4 | ||||
-rw-r--r-- | chrome/common/notification_type.h | 3 | ||||
-rw-r--r-- | chrome/common/render_messages_internal.h | 8 | ||||
-rw-r--r-- | chrome/renderer/render_view.cc | 94 | ||||
-rw-r--r-- | chrome/renderer/render_view.h | 9 |
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); |