diff options
author | jeremy@chromium.org <jeremy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-27 10:24:59 +0000 |
---|---|---|
committer | jeremy@chromium.org <jeremy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-27 10:24:59 +0000 |
commit | e344c910c5f4a1c88e3da37e074873a6360f3624 (patch) | |
tree | 4c3717fd77e4fcd62fbcb5da37cfe10ca25146fe /app | |
parent | 98e053c3baec827f9177a429e46abe36cd4fb9f2 (diff) | |
download | chromium_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.cc | 108 | ||||
-rw-r--r-- | app/clipboard/clipboard.h | 26 | ||||
-rw-r--r-- | app/clipboard/clipboard_linux.cc | 5 | ||||
-rw-r--r-- | app/clipboard/clipboard_mac.mm | 17 | ||||
-rw-r--r-- | app/clipboard/clipboard_unittest.cc | 44 | ||||
-rw-r--r-- | app/clipboard/clipboard_win.cc | 69 |
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); } |