summaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
authorjeremy@chromium.org <jeremy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-01-27 10:24:59 +0000
committerjeremy@chromium.org <jeremy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-01-27 10:24:59 +0000
commite344c910c5f4a1c88e3da37e074873a6360f3624 (patch)
tree4c3717fd77e4fcd62fbcb5da37cfe10ca25146fe /app
parent98e053c3baec827f9177a429e46abe36cd4fb9f2 (diff)
downloadchromium_src-e344c910c5f4a1c88e3da37e074873a6360f3624.zip
chromium_src-e344c910c5f4a1c88e3da37e074873a6360f3624.tar.gz
chromium_src-e344c910c5f4a1c88e3da37e074873a6360f3624.tar.bz2
POSIX: Use Shared Mem transport to copy images.
Prior to this change images where copied inline in IPC messages on non-Windows platforms. Copying an oversized image would cause the IPC system to bork and crash the renderer. Changes in this CL: * All platforms use a unified mechanism to copy images using shared memory. * Introduced a new IPC message so the renderer can allocated a shared memory segment on OS X. * On OS X tried to keep as few copies of the image data in memory as possible. BUG=26822 TEST=1)On all platforms: navigate to a webpage, right click on an image and copy. Then try pasting into an image editor. 2)Repro steps in bug should no longer crash the Renderer on Mac/Linux Review URL: http://codereview.chromium.org/552129 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@37247 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'app')
-rw-r--r--app/clipboard/clipboard.cc108
-rw-r--r--app/clipboard/clipboard.h26
-rw-r--r--app/clipboard/clipboard_linux.cc5
-rw-r--r--app/clipboard/clipboard_mac.mm17
-rw-r--r--app/clipboard/clipboard_unittest.cc44
-rw-r--r--app/clipboard/clipboard_win.cc69
6 files changed, 185 insertions, 84 deletions
diff --git a/app/clipboard/clipboard.cc b/app/clipboard/clipboard.cc
index a2050a0..54a7d90 100644
--- a/app/clipboard/clipboard.cc
+++ b/app/clipboard/clipboard.cc
@@ -6,11 +6,22 @@
#include "base/gfx/size.h"
#include "base/logging.h"
+#include "base/scoped_ptr.h"
namespace {
// A compromised renderer could send us bad data, so validate it.
-bool IsBitmapSafe(const Clipboard::ObjectMapParams& params) {
+// This function only checks that the size parameter makes sense, the caller
+// is responsible for further validating the bitmap buffer against
+// |bitmap_bytes|.
+//
+// |params| - Clipboard bitmap contents to validate.
+// |bitmap_bytes| - On return contains the number of bytes needed to store
+// the bitmap data or -1 if the data is invalid.
+// returns: true if the bitmap size is valid, false otherwise.
+bool IsBitmapSafe(const Clipboard::ObjectMapParams& params,
+ size_t* bitmap_bytes) {
+ *bitmap_bytes = -1;
if (params[1].size() != sizeof(gfx::Size))
return false;
const gfx::Size* size =
@@ -23,7 +34,40 @@ bool IsBitmapSafe(const Clipboard::ObjectMapParams& params) {
if (INT_MAX / total_size <= 4)
return false;
total_size *= 4;
- return params[0].size() == total_size;
+ *bitmap_bytes = total_size;
+ return true;
+}
+
+// Validates a plain bitmap on the clipboard.
+// Returns true if the clipboard data makes sense and it's safe to access the
+// bitmap.
+bool ValidatePlainBitmap(const Clipboard::ObjectMapParams& params) {
+ size_t bitmap_bytes = -1;
+ if (!IsBitmapSafe(params, &bitmap_bytes))
+ return false;
+ if (bitmap_bytes != params[0].size())
+ return false;
+ return true;
+}
+
+// Valides a shared bitmap on the clipboard.
+// Returns true if the clipboard data makes sense and it's safe to access the
+// bitmap.
+bool ValidateAndMapSharedBitmap(const Clipboard::ObjectMapParams& params,
+ base::SharedMemory* bitmap_data) {
+ using base::SharedMemory;
+ size_t bitmap_bytes = -1;
+ if (!IsBitmapSafe(params, &bitmap_bytes))
+ return false;
+
+ if (!bitmap_data || !SharedMemory::IsHandleValid(bitmap_data->handle()))
+ return false;
+
+ if (!bitmap_data->Map(bitmap_bytes)) {
+ PLOG(ERROR) << "Failed to map bitmap memory";
+ return false;
+ }
+ return true;
}
} // namespace
@@ -33,7 +77,8 @@ void Clipboard::DispatchObject(ObjectType type, const ObjectMapParams& params) {
if (type != CBF_WEBKIT && (params.empty() || params[0].empty()))
return;
// Some other types need a non-empty 2nd param.
- if ((type == CBF_BOOKMARK || type == CBF_BITMAP || type == CBF_DATA) &&
+ if ((type == CBF_BOOKMARK || type == CBF_BITMAP ||
+ type == CBF_SMBITMAP || type == CBF_DATA) &&
(params.size() != 2 || params[1].empty()))
return;
switch (type) {
@@ -62,11 +107,34 @@ void Clipboard::DispatchObject(ObjectType type, const ObjectMapParams& params) {
break;
case CBF_BITMAP:
- if (!IsBitmapSafe(params))
+ if (!ValidatePlainBitmap(params))
return;
+
WriteBitmap(&(params[0].front()), &(params[1].front()));
break;
+ case CBF_SMBITMAP: {
+ using base::SharedMemory;
+ using base::SharedMemoryHandle;
+
+ if (params[0].size() != sizeof(SharedMemory*))
+ return;
+
+ // It's OK to cast away constness here since we map the handle as
+ // read-only.
+ const char* raw_bitmap_data_const =
+ reinterpret_cast<const char*>(&(params[0].front()));
+ char* raw_bitmap_data = const_cast<char*>(raw_bitmap_data_const);
+ scoped_ptr<SharedMemory> bitmap_data(
+ *reinterpret_cast<SharedMemory**>(raw_bitmap_data));
+
+ if (!ValidateAndMapSharedBitmap(params, bitmap_data.get()))
+ return;
+ WriteBitmap(static_cast<const char*>(bitmap_data->memory()),
+ &(params[1].front()));
+ break;
+ }
+
#if !defined(OS_MACOSX)
case CBF_DATA:
WriteData(&(params[0].front()), params[0].size(),
@@ -78,3 +146,35 @@ void Clipboard::DispatchObject(ObjectType type, const ObjectMapParams& params) {
NOTREACHED();
}
}
+
+// static
+void Clipboard::ReplaceSharedMemHandle(ObjectMap* objects,
+ base::SharedMemoryHandle bitmap_handle,
+ base::ProcessHandle process) {
+ using base::SharedMemory;
+ bool has_shared_bitmap = false;
+
+ for (ObjectMap::iterator iter = objects->begin(); iter != objects->end();
+ ++iter) {
+ if (iter->first == CBF_SMBITMAP) {
+ // The code currently only accepts sending a single bitmap over this way.
+ // Fail hard if we ever encounter more than one shared bitmap structure to
+ // fill.
+ CHECK(!has_shared_bitmap);
+
+#if defined(OS_WIN)
+ SharedMemory* bitmap = new SharedMemory(bitmap_handle, true, process);
+#else
+ SharedMemory* bitmap = new SharedMemory(bitmap_handle, true);
+#endif
+
+ // We store the shared memory object pointer so it can be retrieved by the
+ // UI thread (see DispatchObject()).
+ iter->second[0].clear();
+ for (size_t i = 0; i < sizeof(SharedMemory*); ++i)
+ iter->second[0].push_back(reinterpret_cast<char*>(&bitmap)[i]);
+ has_shared_bitmap = true;
+ }
+ }
+}
+
diff --git a/app/clipboard/clipboard.h b/app/clipboard/clipboard.h
index faaa00c..ee536f6 100644
--- a/app/clipboard/clipboard.h
+++ b/app/clipboard/clipboard.h
@@ -10,7 +10,9 @@
#include <vector>
#include "base/process.h"
+#include "base/shared_memory.h"
#include "base/string16.h"
+#include "testing/gtest/include/gtest/gtest_prod.h"
namespace gfx {
class Size;
@@ -61,7 +63,8 @@ class Clipboard {
// CBF_WEBKIT none empty vector
// CBF_BITMAP pixels byte array
// size gfx::Size struct
- // CBF_SMBITMAP shared_mem shared memory handle
+ // CBF_SMBITMAP shared_mem A pointer to an unmapped base::SharedMemory
+ // object containing the bitmap data.
// size gfx::Size struct
// CBF_DATA format char array
// data byte array
@@ -160,21 +163,26 @@ class Clipboard {
static FormatType GetWebKitSmartPasteFormatType();
// Win: MS HTML Format, Other: Generic HTML format
static FormatType GetHtmlFormatType();
-#if defined(OS_WIN)
static FormatType GetBitmapFormatType();
+
+ // Embeds a pointer to a SharedMemory object pointed to by |bitmap_handle|
+ // belonging to |process| into a shared bitmap [CBF_SMBITMAP] slot in
+ // |objects|. The pointer is deleted by DispatchObjects().
+ //
+ // On non-Windows platforms, |process| is ignored.
+ static void ReplaceSharedMemHandle(ObjectMap* objects,
+ base::SharedMemoryHandle bitmap_handle,
+ base::ProcessHandle process);
+#if defined(OS_WIN)
// Firefox text/html
static FormatType GetTextHtmlFormatType();
static FormatType GetCFHDropFormatType();
static FormatType GetFileDescriptorFormatType();
static FormatType GetFileContentFormatZeroType();
-
- // Duplicates any remote shared memory handle embedded inside |objects| that
- // was created by |process| so that it can be used by this process.
- static void DuplicateRemoteHandles(base::ProcessHandle process,
- ObjectMap* objects);
#endif
private:
+ FRIEND_TEST(ClipboardTest, SharedBitmapTest);
void DispatchObject(ObjectType type, const ObjectMapParams& params);
void WriteText(const char* text_data, size_t text_len);
@@ -200,10 +208,6 @@ class Clipboard {
const char* data_data, size_t data_len);
#endif
#if defined(OS_WIN)
- void WriteBitmapFromSharedMemory(const char* bitmap_data,
- const char* size_data,
- base::ProcessHandle handle);
-
void WriteBitmapFromHandle(HBITMAP source_hbitmap,
const gfx::Size& size);
diff --git a/app/clipboard/clipboard_linux.cc b/app/clipboard/clipboard_linux.cc
index aec46da..740ec76 100644
--- a/app/clipboard/clipboard_linux.cc
+++ b/app/clipboard/clipboard_linux.cc
@@ -378,6 +378,11 @@ Clipboard::FormatType Clipboard::GetHtmlFormatType() {
}
// static
+Clipboard::FormatType Clipboard::GetBitmapFormatType() {
+ return std::string(kMimeBmp);
+}
+
+// static
Clipboard::FormatType Clipboard::GetWebKitSmartPasteFormatType() {
return std::string(kMimeWebkitSmartPaste);
}
diff --git a/app/clipboard/clipboard_mac.mm b/app/clipboard/clipboard_mac.mm
index ae3f1fe..1596eba 100644
--- a/app/clipboard/clipboard_mac.mm
+++ b/app/clipboard/clipboard_mac.mm
@@ -123,9 +123,14 @@ void Clipboard::WriteBitmap(const char* pixel_data, const char* size_data) {
NULL,
false,
kCGRenderingIntentDefault));
+ // Aggressively free storage since image buffers can potentially be very
+ // large.
+ data_provider.reset();
+ data.reset();
scoped_nsobject<NSBitmapImageRep> bitmap(
[[NSBitmapImageRep alloc] initWithCGImage:cgimage]);
+ cgimage.reset();
scoped_nsobject<NSImage> image([[NSImage alloc] init]);
[image addRepresentation:bitmap];
@@ -134,7 +139,11 @@ void Clipboard::WriteBitmap(const char* pixel_data, const char* size_data) {
// For now, spit out the image as a TIFF.
NSPasteboard* pb = GetPasteboard();
[pb addTypes:[NSArray arrayWithObject:NSTIFFPboardType] owner:nil];
- [pb setData:[image TIFFRepresentation] forType:NSTIFFPboardType];
+ NSData *tiff_data = [image TIFFRepresentation];
+ LOG_IF(ERROR, tiff_data == NULL) << "Failed to allocate image for clipboard";
+ if (tiff_data) {
+ [pb setData:tiff_data forType:NSTIFFPboardType];
+ }
}
// Write an extra flavor that signifies WebKit was the last to modify the
@@ -295,6 +304,12 @@ Clipboard::FormatType Clipboard::GetHtmlFormatType() {
}
// static
+Clipboard::FormatType Clipboard::GetBitmapFormatType() {
+ static const std::string type = base::SysNSStringToUTF8(NSTIFFPboardType);
+ return type;
+}
+
+// static
Clipboard::FormatType Clipboard::GetWebKitSmartPasteFormatType() {
static const std::string type =
base::SysNSStringToUTF8(kWebSmartPastePboardType);
diff --git a/app/clipboard/clipboard_unittest.cc b/app/clipboard/clipboard_unittest.cc
index 81f400d..8203bac 100644
--- a/app/clipboard/clipboard_unittest.cc
+++ b/app/clipboard/clipboard_unittest.cc
@@ -213,6 +213,50 @@ TEST_F(ClipboardTest, URLTest) {
#endif // defined(OS_LINUX)
}
+TEST_F(ClipboardTest, SharedBitmapTest) {
+ unsigned int fake_bitmap[] = {
+ 0x46155189, 0xF6A55C8D, 0x79845674, 0xFA57BD89,
+ 0x78FD46AE, 0x87C64F5A, 0x36EDC5AF, 0x4378F568,
+ 0x91E9F63A, 0xC31EA14F, 0x69AB32DF, 0x643A3FD1,
+ };
+ gfx::Size fake_bitmap_size(3, 4);
+ size_t bytes = sizeof(fake_bitmap);
+
+ // Create shared memory region.
+ base::SharedMemory shared_buf;
+ ASSERT_TRUE(shared_buf.Create(L"", false, true, bytes));
+ ASSERT_TRUE(shared_buf.Map(bytes));
+ memcpy(shared_buf.memory(), fake_bitmap, bytes);
+ base::SharedMemoryHandle handle_to_share;
+ base::ProcessHandle current_process = NULL;
+#if defined(OS_WIN)
+ current_process = GetCurrentProcess();
+#endif
+ shared_buf.ShareToProcess(current_process, &handle_to_share);
+ ASSERT_TRUE(shared_buf.Unmap());
+
+ // Setup data for clipboard.
+ Clipboard::ObjectMapParam placeholder_param;
+ Clipboard::ObjectMapParam size_param;
+ const char* size_data = reinterpret_cast<const char*>(&fake_bitmap_size);
+ for (size_t i = 0; i < sizeof(fake_bitmap_size); ++i)
+ size_param.push_back(size_data[i]);
+
+ Clipboard::ObjectMapParams params;
+ params.push_back(placeholder_param);
+ params.push_back(size_param);
+
+ Clipboard::ObjectMap objects;
+ objects[Clipboard::CBF_SMBITMAP] = params;
+ Clipboard::ReplaceSharedMemHandle(&objects, handle_to_share, current_process);
+
+ Clipboard clipboard;
+ clipboard.WriteObjects(objects);
+
+ EXPECT_TRUE(clipboard.IsFormatAvailable(Clipboard::GetBitmapFormatType(),
+ Clipboard::BUFFER_STANDARD));
+}
+
#if defined(OS_WIN) || (defined(OS_POSIX) && !defined(OS_MACOSX))
TEST_F(ClipboardTest, DataTest) {
Clipboard clipboard;
diff --git a/app/clipboard/clipboard_win.cc b/app/clipboard/clipboard_win.cc
index 9074fc5..5e3bc42 100644
--- a/app/clipboard/clipboard_win.cc
+++ b/app/clipboard/clipboard_win.cc
@@ -162,12 +162,7 @@ void Clipboard::WriteObjects(const ObjectMap& objects,
for (ObjectMap::const_iterator iter = objects.begin();
iter != objects.end(); ++iter) {
- if (iter->first == CBF_SMBITMAP)
- WriteBitmapFromSharedMemory(&(iter->second[0].front()),
- &(iter->second[1].front()),
- process);
- else
- DispatchObject(static_cast<ObjectType>(iter->first), iter->second);
+ DispatchObject(static_cast<ObjectType>(iter->first), iter->second);
}
}
@@ -250,44 +245,6 @@ void Clipboard::WriteBitmap(const char* pixel_data, const char* size_data) {
::ReleaseDC(NULL, dc);
}
-void Clipboard::WriteBitmapFromSharedMemory(const char* bitmap_data,
- const char* size_data,
- base::ProcessHandle process) {
- const gfx::Size* size = reinterpret_cast<const gfx::Size*>(size_data);
-
- // bitmap_data has an encoded shared memory object. See
- // DuplicateRemoteHandles().
- char* ptr = const_cast<char*>(bitmap_data);
- scoped_ptr<const base::SharedMemory> bitmap(*
- reinterpret_cast<const base::SharedMemory**>(ptr));
-
- // TODO(darin): share data in gfx/bitmap_header.cc somehow.
- BITMAPINFO bm_info = {0};
- bm_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
- bm_info.bmiHeader.biWidth = size->width();
- // Sets the vertical orientation.
- bm_info.bmiHeader.biHeight = -size->height();
- bm_info.bmiHeader.biPlanes = 1;
- bm_info.bmiHeader.biBitCount = 32;
- bm_info.bmiHeader.biCompression = BI_RGB;
-
- HDC dc = ::GetDC(NULL);
-
- // We can create an HBITMAP directly using the shared memory handle, saving
- // a memcpy.
- HBITMAP source_hbitmap =
- ::CreateDIBSection(dc, &bm_info, DIB_RGB_COLORS, NULL,
- bitmap->handle(), 0);
-
- if (source_hbitmap) {
- // Now we can write the HBITMAP to the clipboard
- WriteBitmapFromHandle(source_hbitmap, *size);
- }
-
- ::DeleteObject(source_hbitmap);
- ::ReleaseDC(NULL, dc);
-}
-
void Clipboard::WriteBitmapFromHandle(HBITMAP source_hbitmap,
const gfx::Size& size) {
// We would like to just call ::SetClipboardData on the source_hbitmap,
@@ -615,30 +572,6 @@ Clipboard::FormatType Clipboard::GetFileContentFormatZeroType() {
}
// static
-void Clipboard::DuplicateRemoteHandles(base::ProcessHandle process,
- ObjectMap* objects) {
- for (ObjectMap::iterator iter = objects->begin(); iter != objects->end();
- ++iter) {
- if (iter->first == CBF_SMBITMAP) {
- // There is a shared memory handle encoded on the first ObjectMapParam.
- // Use it to open a local handle to the memory.
- char* bitmap_data = &(iter->second[0].front());
- base::SharedMemoryHandle* remote_bitmap_handle =
- reinterpret_cast<base::SharedMemoryHandle*>(bitmap_data);
-
- base::SharedMemory* bitmap = new base::SharedMemory(*remote_bitmap_handle,
- false, process);
-
- // We store the object where the remote handle was located so it can
- // be retrieved by the UI thread (see WriteBitmapFromSharedMemory()).
- iter->second[0].clear();
- for (size_t i = 0; i < sizeof(bitmap); i++)
- iter->second[0].push_back(reinterpret_cast<char*>(&bitmap)[i]);
- }
- }
-}
-
-// static
Clipboard::FormatType Clipboard::GetWebKitSmartPasteFormatType() {
return IntToString(ClipboardUtil::GetWebKitSmartPasteFormat()->cfFormat);
}