diff options
author | agl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-02-20 02:00:04 +0000 |
---|---|---|
committer | agl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-02-20 02:00:04 +0000 |
commit | e68e62fa169c45bd779bfe890aa4fcdaa24d267d (patch) | |
tree | efdb18adec880e7f780d8cde4e12893d3a20234f | |
parent | 7fe07d0726bad485fa40150aa9f7cecb1318217e (diff) | |
download | chromium_src-e68e62fa169c45bd779bfe890aa4fcdaa24d267d.zip chromium_src-e68e62fa169c45bd779bfe890aa4fcdaa24d267d.tar.gz chromium_src-e68e62fa169c45bd779bfe890aa4fcdaa24d267d.tar.bz2 |
Bitmap transport
This patch reworks bitmap transport on all platforms. Linux and Mac
are switched from serialising bitmaps over the IPC channel to using
shared memory. All platforms gain a shared memory mapping cache on the
host side.
The concept of a TransportDIB (device independent bitmap) is added to
encapsulate most of the platform specifics.
On Linux, we use SysV shared memory. This is because X shared pixmaps,
which predate POSIX SHM, can only use SysV. By using SysV between
renderer and browser, we open up the possibility to map the shared
memory directly from the renderer to the X server.
On Mac, we use POSIX shared memory. However, since this needs
filesystem access and the Mac renderer is sandboxed from the
filesystem, we add two new messages from renderer -> browser:
The first, AllocTransportDIB, synchronously creates a transport DIB in
the browser and passes a handle back to the renderer. The second,
FreeTransportDIB, asynchronously, notifies the browser that it may
close its handle to the shared memory region.
On Mac, the shared memory regions are identified by their inode
numbers on the wire. This means that the browser must keep handles
open to all the allocated shared memory regions (since an inode number
is insufficient to map the region). The alternative design is that the
renderer passes the file descriptor with each paint operation. Since
passing file descriptors is special case in the code, I felt that it
would be best to minimise their use. Creating and freeing transport
DIBs are relatively rare operations relative to paints and scrolls.
On Windows, most of the code remains the same, except that Windows now
uses the mapping cache added in this patch. This allows the browser to
maintain a shared memory mapping for a transport DIB over several
paints. Previously it mapped and unmapped for every operation, causing
lots of TLB and VM churn.
Review URL: http://codereview.chromium.org/21485
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@10071 0039d316-1c4b-4281-b951-d872f2087c98
45 files changed, 1242 insertions, 289 deletions
diff --git a/base/shared_memory.h b/base/shared_memory.h index b44367a..744b348 100644 --- a/base/shared_memory.h +++ b/base/shared_memory.h @@ -8,6 +8,7 @@ #include "build/build_config.h" #if defined(OS_POSIX) +#include <sys/types.h> #include <semaphore.h> #include "base/file_descriptor_posix.h" #endif @@ -24,7 +25,10 @@ namespace base { typedef HANDLE SharedMemoryHandle; typedef HANDLE SharedMemoryLock; #elif defined(OS_POSIX) +// A SharedMemoryId is sufficient to identify a given shared memory segment on a +// system, but insufficient to map it. typedef FileDescriptor SharedMemoryHandle; +typedef ino_t SharedMemoryId; // On POSIX, the lock is implemented as a lockf() on the mapped file, // so no additional member (or definition of SharedMemoryLock) is // needed. @@ -99,6 +103,14 @@ class SharedMemory { // identifier is not portable. SharedMemoryHandle handle() const; +#if defined(OS_POSIX) + // Return a unique identifier for this shared memory segment. Inode numbers + // are technically only unique to a single filesystem. However, we always + // allocate shared memory backing files from the same directory, so will end + // up on the same filesystem. + SharedMemoryId id() const { return inode_; } +#endif + // Closes the open shared memory segment. // It is safe to call Close repeatedly. void Close(); @@ -153,9 +165,10 @@ class SharedMemory { std::wstring name_; #if defined(OS_WIN) - HANDLE mapped_file_; + HANDLE mapped_file_; #elif defined(OS_POSIX) - int mapped_file_; + int mapped_file_; + ino_t inode_; #endif void* memory_; bool read_only_; diff --git a/base/shared_memory_posix.cc b/base/shared_memory_posix.cc index e6f81c5..c948e58 100644 --- a/base/shared_memory_posix.cc +++ b/base/shared_memory_posix.cc @@ -7,6 +7,8 @@ #include <errno.h> #include <fcntl.h> #include <sys/mman.h> +#include <sys/stat.h> +#include <unistd.h> #include "base/file_util.h" #include "base/logging.h" @@ -23,6 +25,7 @@ const char kSemaphoreSuffix[] = "-sem"; SharedMemory::SharedMemory() : mapped_file_(-1), + inode_(0), memory_(NULL), read_only_(false), max_size_(0) { @@ -30,9 +33,16 @@ SharedMemory::SharedMemory() SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only) : mapped_file_(handle.fd), + inode_(0), memory_(NULL), read_only_(read_only), max_size_(0) { + struct stat st; + if (fstat(handle.fd, &st) == 0) { + // If fstat fails, then the file descriptor is invalid and we'll learn this + // fact when Map() fails. + inode_ = st.st_ino; + } } SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only, @@ -206,6 +216,12 @@ bool SharedMemory::CreateOrOpen(const std::wstring &name, mapped_file_ = dup(fileno(fp)); DCHECK(mapped_file_ >= 0); + + struct stat st; + if (fstat(mapped_file_, &st)) + NOTREACHED(); + inode_ = st.st_ino; + return true; } diff --git a/base/timer.h b/base/timer.h index 698d59d..eb00486 100644 --- a/base/timer.h +++ b/base/timer.h @@ -89,7 +89,7 @@ class BaseTimer_Helper { // Used to orphan delayed_task_ so that when it runs it does nothing. void OrphanDelayedTask(); - + // Used to initiated a new delayed task. This has the side-effect of // orphaning delayed_task_ if it is non-null. void InitiateDelayedTask(TimerTask* timer_task); @@ -128,7 +128,7 @@ class BaseTimer : public BaseTimer_Helper { private: typedef BaseTimer<Receiver, kIsRepeating> SelfType; - + class TimerTask : public BaseTimer_Helper::TimerTask { public: TimerTask(TimeDelta delay, Receiver* receiver, ReceiverMethod method) @@ -198,6 +198,65 @@ class OneShotTimer : public BaseTimer<Receiver, false> {}; template <class Receiver> class RepeatingTimer : public BaseTimer<Receiver, true> {}; +//----------------------------------------------------------------------------- +// A Delay timer is like The Button from Lost. Once started, you have to keep +// calling Reset otherwise it will call the given method in the MessageLoop +// thread. +// +// Once created, it is inactive until Reset is called. Once |delay| seconds have +// passed since the last call to Reset, the callback is made. Once the callback +// has been made, it's inactive until Reset is called again. +template <class Receiver> +class DelayTimer { + public: + typedef void (Receiver::*ReceiverMethod)(); + + DelayTimer(TimeDelta delay, Receiver* receiver, ReceiverMethod method) + : receiver_(receiver), + method_(method), + delay_(delay) { + } + + void Reset() { + DelayFor(delay_); + } + + private: + void DelayFor(TimeDelta delay) { + trigger_time_ = Time::Now() + delay; + + // If we already have a timer that will expire at or before the given delay, + // then we have nothing more to do now. + if (timer_.IsRunning() && timer_.GetCurrentDelay() <= delay) + return; + + // The timer isn't running, or will expire too late, so restart it. + timer_.Stop(); + timer_.Start(delay, this, &DelayTimer<Receiver>::Check); + } + + void Check() { + if (trigger_time_.is_null()) + return; + + // If we have not waited long enough, then wait some more. + const Time now = Time::Now(); + if (now < trigger_time_) { + DelayFor(trigger_time_ - now); + return; + } + + (receiver_->*method_)(); + } + + Receiver *const receiver_; + const ReceiverMethod method_; + const TimeDelta delay_; + + OneShotTimer<DelayTimer<Receiver> > timer_; + Time trigger_time_; +}; + } // namespace base #endif // BASE_TIMER_H_ diff --git a/base/timer_unittest.cc b/base/timer_unittest.cc index 8c1d58e..d441636 100644 --- a/base/timer_unittest.cc +++ b/base/timer_unittest.cc @@ -14,10 +14,12 @@ namespace { class OneShotTimerTester { public: - OneShotTimerTester(bool* did_run) : did_run_(did_run) { + OneShotTimerTester(bool* did_run, unsigned milliseconds = 10) + : did_run_(did_run), + delay_ms_(milliseconds) { } void Start() { - timer_.Start(TimeDelta::FromMilliseconds(10), this, + timer_.Start(TimeDelta::FromMilliseconds(delay_ms_), this, &OneShotTimerTester::Run); } private: @@ -27,6 +29,7 @@ class OneShotTimerTester { } bool* did_run_; base::OneShotTimer<OneShotTimerTester> timer_; + const unsigned delay_ms_; }; class OneShotSelfDeletingTimerTester { @@ -138,7 +141,7 @@ void RunTest_RepeatingTimer_Cancel(MessageLoop::Type message_loop_type) { // Now start the timer. a->Start(); - + bool did_run_b = false; RepeatingTimerTester b(&did_run_b); b.Start(); @@ -149,6 +152,97 @@ void RunTest_RepeatingTimer_Cancel(MessageLoop::Type message_loop_type) { EXPECT_TRUE(did_run_b); } +class DelayTimerTarget { + public: + DelayTimerTarget() + : signaled_(false) { + } + + bool signaled() const { return signaled_; } + + void Signal() { + ASSERT_FALSE(signaled_); + signaled_ = true; + } + + private: + bool signaled_; +}; + +void RunTest_DelayTimer_NoCall(MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + // If Delay is never called, the timer shouldn't go off. + DelayTimerTarget target; + base::DelayTimer<DelayTimerTarget> timer( + TimeDelta::FromMilliseconds(1), &target, &DelayTimerTarget::Signal); + + bool did_run = false; + OneShotTimerTester tester(&did_run); + tester.Start(); + MessageLoop::current()->Run(); + + ASSERT_FALSE(target.signaled()); +} + +void RunTest_DelayTimer_OneCall(MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + DelayTimerTarget target; + base::DelayTimer<DelayTimerTarget> timer( + TimeDelta::FromMilliseconds(1), &target, &DelayTimerTarget::Signal); + timer.Reset(); + + bool did_run = false; + OneShotTimerTester tester(&did_run, 100 /* milliseconds */); + tester.Start(); + MessageLoop::current()->Run(); + + ASSERT_TRUE(target.signaled()); +} + +struct ResetHelper { + ResetHelper(base::DelayTimer<DelayTimerTarget>* timer, + DelayTimerTarget* target) + : timer_(timer), + target_(target) { + } + + void Reset() { + ASSERT_FALSE(target_->signaled()); + timer_->Reset(); + } + + private: + base::DelayTimer<DelayTimerTarget> *const timer_; + DelayTimerTarget *const target_; +}; + +void RunTest_DelayTimer_Reset(MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + // If Delay is never called, the timer shouldn't go off. + DelayTimerTarget target; + base::DelayTimer<DelayTimerTarget> timer( + TimeDelta::FromMilliseconds(1), &target, &DelayTimerTarget::Signal); + timer.Reset(); + + ResetHelper reset_helper(&timer, &target); + + base::OneShotTimer<ResetHelper> timers[20]; + for (size_t i = 0; i < arraysize(timers); ++i) { + timers[i].Start(TimeDelta::FromMilliseconds(i * 10), &reset_helper, + &ResetHelper::Reset); + } + + bool did_run = false; + OneShotTimerTester tester(&did_run, 300); + tester.Start(); + MessageLoop::current()->Run(); + + ASSERT_TRUE(target.signaled()); +} + } // namespace //----------------------------------------------------------------------------- @@ -187,6 +281,24 @@ TEST(TimerTest, RepeatingTimer_Cancel) { RunTest_RepeatingTimer_Cancel(MessageLoop::TYPE_IO); } +TEST(TimerTest, DelayTimer_NoCall) { + RunTest_DelayTimer_NoCall(MessageLoop::TYPE_DEFAULT); + RunTest_DelayTimer_NoCall(MessageLoop::TYPE_UI); + RunTest_DelayTimer_NoCall(MessageLoop::TYPE_IO); +} + +TEST(TimerTest, DelayTimer_OneCall) { + RunTest_DelayTimer_OneCall(MessageLoop::TYPE_DEFAULT); + RunTest_DelayTimer_OneCall(MessageLoop::TYPE_UI); + RunTest_DelayTimer_OneCall(MessageLoop::TYPE_IO); +} + +TEST(TimerTest, DelayTimer_Reset) { + RunTest_DelayTimer_Reset(MessageLoop::TYPE_DEFAULT); + RunTest_DelayTimer_Reset(MessageLoop::TYPE_UI); + RunTest_DelayTimer_Reset(MessageLoop::TYPE_IO); +} + TEST(TimerTest, MessageLoopShutdown) { // This test is designed to verify that shutdown of the // message loop does not cause crashes if there were pending diff --git a/chrome/browser/renderer_host/backing_store.cc b/chrome/browser/renderer_host/backing_store.cc index da988768..a68e785 100644 --- a/chrome/browser/renderer_host/backing_store.cc +++ b/chrome/browser/renderer_host/backing_store.cc @@ -60,7 +60,7 @@ BackingStore* BackingStoreManager::PrepareBackingStore( RenderWidgetHost* host, const gfx::Rect& backing_store_rect, base::ProcessHandle process_handle, - BitmapWireData bitmap_section, + TransportDIB* bitmap, const gfx::Rect& bitmap_rect, bool* needs_full_paint) { BackingStore* backing_store = GetBackingStore(host, @@ -76,7 +76,7 @@ BackingStore* BackingStoreManager::PrepareBackingStore( } DCHECK(backing_store != NULL); - backing_store->PaintRect(process_handle, bitmap_section, bitmap_rect); + backing_store->PaintRect(process_handle, bitmap, bitmap_rect); return backing_store; } diff --git a/chrome/browser/renderer_host/backing_store.h b/chrome/browser/renderer_host/backing_store.h index 29415a8..e6113f5 100644 --- a/chrome/browser/renderer_host/backing_store.h +++ b/chrome/browser/renderer_host/backing_store.h @@ -10,7 +10,6 @@ #include "base/gfx/size.h" #include "base/process.h" #include "build/build_config.h" -#include "chrome/common/bitmap_wire_data.h" #include "chrome/common/mru_cache.h" #if defined(OS_WIN) @@ -20,6 +19,7 @@ #endif class RenderWidgetHost; +class TransportDIB; // BackingStore ---------------------------------------------------------------- @@ -39,14 +39,14 @@ class BackingStore { // Paints the bitmap from the renderer onto the backing store. bool PaintRect(base::ProcessHandle process, - BitmapWireData bitmap_section, + TransportDIB* bitmap, const gfx::Rect& bitmap_rect); // Scrolls the given rect in the backing store, replacing the given region // identified by |bitmap_rect| by the bitmap in the file identified by the // given file handle. void ScrollRect(base::ProcessHandle process, - BitmapWireData bitmap, const gfx::Rect& bitmap_rect, + TransportDIB* bitmap, const gfx::Rect& bitmap_rect, int dx, int dy, const gfx::Rect& clip_rect, const gfx::Size& view_size); @@ -113,7 +113,7 @@ class BackingStoreManager { static BackingStore* PrepareBackingStore(RenderWidgetHost* host, const gfx::Rect& backing_store_rect, base::ProcessHandle process_handle, - BitmapWireData bitmap_section, + TransportDIB* bitmap, const gfx::Rect& bitmap_rect, bool* needs_full_paint); diff --git a/chrome/browser/renderer_host/backing_store_posix.cc b/chrome/browser/renderer_host/backing_store_posix.cc index f925ef9..1d195fb 100644 --- a/chrome/browser/renderer_host/backing_store_posix.cc +++ b/chrome/browser/renderer_host/backing_store_posix.cc @@ -5,6 +5,7 @@ #include "chrome/browser/renderer_host/backing_store.h" #include "base/logging.h" +#include "chrome/common/transport_dib.h" #include "skia/ext/platform_canvas.h" #include "skia/include/SkBitmap.h" #include "skia/include/SkCanvas.h" @@ -19,20 +20,20 @@ BackingStore::~BackingStore() { } bool BackingStore::PaintRect(base::ProcessHandle process, - BitmapWireData bitmap, + TransportDIB* bitmap, const gfx::Rect& bitmap_rect) { - if (bitmap.width() != bitmap_rect.width() || - bitmap.height() != bitmap_rect.height() || - bitmap.config() != SkBitmap::kARGB_8888_Config) { - return false; - } + SkBitmap skbitmap; + skbitmap.setConfig(SkBitmap::kARGB_8888_Config, bitmap_rect.width(), + bitmap_rect.height(), 4 * bitmap_rect.width()); + + skbitmap.setPixels(bitmap->memory()); - canvas_.drawBitmap(bitmap, bitmap_rect.x(), bitmap_rect.y()); + canvas_.drawBitmap(skbitmap, bitmap_rect.x(), bitmap_rect.y()); return true; } void BackingStore::ScrollRect(base::ProcessHandle process, - BitmapWireData bitmap, + TransportDIB* bitmap, const gfx::Rect& bitmap_rect, int dx, int dy, const gfx::Rect& clip_rect, @@ -59,12 +60,6 @@ void BackingStore::ScrollRect(base::ProcessHandle process, DCHECK(clip_rect.bottom() <= canvas_.getDevice()->height()); DCHECK(clip_rect.right() <= canvas_.getDevice()->width()); - if (bitmap.width() != bitmap_rect.width() || - bitmap.height() != bitmap_rect.height() || - bitmap.config() != SkBitmap::kARGB_8888_Config) { - return; - } - const SkBitmap &backing_bitmap = canvas_.getDevice()->accessBitmap(true); const int stride = backing_bitmap.rowBytes(); uint8_t* x = static_cast<uint8_t*>(backing_bitmap.getPixels()); @@ -123,6 +118,6 @@ void BackingStore::ScrollRect(base::ProcessHandle process, } // Now paint the new bitmap data. - canvas_.drawBitmap(bitmap, bitmap_rect.x(), bitmap_rect.y()); + PaintRect(process, bitmap, bitmap_rect); return; } diff --git a/chrome/browser/renderer_host/backing_store_win.cc b/chrome/browser/renderer_host/backing_store_win.cc index 2ca9396..dcc1ad4 100644 --- a/chrome/browser/renderer_host/backing_store_win.cc +++ b/chrome/browser/renderer_host/backing_store_win.cc @@ -6,7 +6,7 @@ #include "base/gfx/gdi_util.h" #include "chrome/browser/renderer_host/render_widget_host.h" -#include "chrome/common/win_util.h" +#include "chrome/common/transport_dib.h" // BackingStore (Windows) ------------------------------------------------------ @@ -31,14 +31,8 @@ BackingStore::~BackingStore() { } bool BackingStore::PaintRect(base::ProcessHandle process, - BitmapWireData bitmap_section, + TransportDIB* bitmap, const gfx::Rect& bitmap_rect) { - // The bitmap received is valid only in the renderer process. - HANDLE valid_bitmap = - win_util::GetSectionFromProcess(bitmap_section, process, false); - if (!valid_bitmap) - return false; - if (!backing_store_dib_) { backing_store_dib_ = CreateDIB(hdc_, size_.width(), size_.height(), true, NULL); @@ -48,8 +42,7 @@ bool BackingStore::PaintRect(base::ProcessHandle process, // TODO(darin): protect against integer overflow DWORD size = 4 * bitmap_rect.width() * bitmap_rect.height(); - void* backing_store_data = MapViewOfFile(valid_bitmap, FILE_MAP_READ, 0, 0, - size); + // These values are shared with gfx::PlatformDevice BITMAPINFOHEADER hdr; gfx::CreateBitmapHeader(bitmap_rect.width(), bitmap_rect.height(), &hdr); @@ -65,18 +58,16 @@ bool BackingStore::PaintRect(base::ProcessHandle process, 0, 0, // source x,y paint_rect.width(), paint_rect.height(), - backing_store_data, + bitmap->memory(), reinterpret_cast<BITMAPINFO*>(&hdr), DIB_RGB_COLORS, SRCCOPY); - UnmapViewOfFile(backing_store_data); - CloseHandle(valid_bitmap); return true; } void BackingStore::ScrollRect(base::ProcessHandle process, - BitmapWireData bitmap, + TransportDIB* bitmap, const gfx::Rect& bitmap_rect, int dx, int dy, const gfx::Rect& clip_rect, diff --git a/chrome/browser/renderer_host/backing_store_xcb.cc b/chrome/browser/renderer_host/backing_store_xcb.cc new file mode 100644 index 0000000..ba6a12b --- /dev/null +++ b/chrome/browser/renderer_host/backing_store_xcb.cc @@ -0,0 +1,40 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/renderer_host/backing_store.h" + +#include <xcb/xcb.h> + +#include "base/logging.h" +#include "chrome/common/transport_dib.h" + +#ifdef NDEBUG +#define XCB_CALL(func, ...) func(__VA_ARGS__) +#else +#define XCB_CALL(func, ...) do { \ + xcb_void_cookie_t cookie = func##_checked(__VA_ARGS__); \ + xcb_generic_error_t* error = xcb_request_check(connection_, cookie); \ + if (error) { \ + CHECK(false) << "XCB error" \ + << " code:" << error->error_code \ + << " type:" << error->response_type \ + << " sequence:" << error->sequence; \ + } \ +} while(false); +#endif + +BackingStore::BackingStore(const gfx::Size& size, + xcb_connection_t* connection, + xcb_window_t window, + bool use_shared_memory) + : connection_(connection), + use_shared_memory_(use_shared_memory), + pixmap_(xcb_generate_id(connection)) { + XCB_CALL(xcb_create_pixmap, connection_, 32, pixmap, window, size.width(), + size.height()); +} + +BackingStore::~BackingStore() { + XCB_CALL(xcb_free_pixmap, pixmap_); +} diff --git a/chrome/browser/renderer_host/browser_render_process_host.cc b/chrome/browser/renderer_host/browser_render_process_host.cc index eda2f1a..d17ab83 100644 --- a/chrome/browser/renderer_host/browser_render_process_host.cc +++ b/chrome/browser/renderer_host/browser_render_process_host.cc @@ -128,7 +128,10 @@ void BrowserRenderProcessHost::RegisterPrefs(PrefService* prefs) { BrowserRenderProcessHost::BrowserRenderProcessHost(Profile* profile) : RenderProcessHost(profile), visible_widgets_(0), - backgrounded_(true) { + backgrounded_(true), + ALLOW_THIS_IN_INITIALIZER_LIST(cached_dibs_cleaner_( + base::TimeDelta::FromSeconds(5), + this, &BrowserRenderProcessHost::ClearTransportDIBCache)) { DCHECK(host_id() >= 0); // We use a negative host_id_ in destruction. widget_helper_ = new RenderWidgetHelper(host_id()); @@ -170,6 +173,8 @@ BrowserRenderProcessHost::~BrowserRenderProcessHost() { NotificationService::current()->RemoveObserver(this, NotificationType::USER_SCRIPTS_LOADED, NotificationService::AllSources()); + + ClearTransportDIBCache(); } // When we're started with the --start-renderers-manually flag, we pop up a @@ -605,6 +610,65 @@ bool BrowserRenderProcessHost::FastShutdownIfPossible() { return true; } +// This is a platform specific function for mapping a transport DIB given its id +TransportDIB* BrowserRenderProcessHost::MapTransportDIB( + TransportDIB::Id dib_id) { +#if defined(OS_WIN) + // On Windows we need to duplicate the handle from the remote process + HANDLE section = win_util::GetSectionFromProcess( + dib_id.handle, GetRendererProcessHandle(), false /* read write */); + return TransportDIB::Map(section); +#elif defined(OS_MACOSX) + // On OSX, the browser allocates all DIBs and keeps a file descriptor around + // for each. + return widget_helper_->MapTransportDIB(dib_id); +#elif defined(OS_LINUX) + return TransportDIB::Map(dib_id); +#endif // defined(OS_LINUX) +} + +TransportDIB* BrowserRenderProcessHost::GetTransportDIB( + TransportDIB::Id dib_id) { + const std::map<TransportDIB::Id, TransportDIB*>::iterator + i = cached_dibs_.find(dib_id); + if (i != cached_dibs_.end()) { + cached_dibs_cleaner_.Reset(); + return i->second; + } + + TransportDIB* dib = MapTransportDIB(dib_id); + if (!dib) + return NULL; + + if (cached_dibs_.size() >= MAX_MAPPED_TRANSPORT_DIBS) { + // Clean a single entry from the cache + std::map<TransportDIB::Id, TransportDIB*>::iterator smallest_iterator; + size_t smallest_size = std::numeric_limits<size_t>::max(); + + for (std::map<TransportDIB::Id, TransportDIB*>::iterator + i = cached_dibs_.begin(); i != cached_dibs_.end(); ++i) { + if (i->second->size() <= smallest_size) + smallest_iterator = i; + } + + delete smallest_iterator->second; + cached_dibs_.erase(smallest_iterator); + } + + cached_dibs_[dib_id] = dib; + cached_dibs_cleaner_.Reset(); + return dib; +} + +void BrowserRenderProcessHost::ClearTransportDIBCache() { + for (std::map<TransportDIB::Id, TransportDIB*>::iterator + i = cached_dibs_.begin(); i != cached_dibs_.end(); ++i) { + delete i->second; + } + + cached_dibs_.clear(); +} + bool BrowserRenderProcessHost::Send(IPC::Message* msg) { if (!channel_.get()) { delete msg; diff --git a/chrome/browser/renderer_host/browser_render_process_host.h b/chrome/browser/renderer_host/browser_render_process_host.h index be7e225..dad06b5 100644 --- a/chrome/browser/renderer_host/browser_render_process_host.h +++ b/chrome/browser/renderer_host/browser_render_process_host.h @@ -13,6 +13,8 @@ #include "base/ref_counted.h" #include "base/scoped_ptr.h" #include "base/shared_memory.h" +#include "base/timer.h" +#include "chrome/common/transport_dib.h" #include "chrome/browser/renderer_host/audio_renderer_host.h" #include "chrome/browser/renderer_host/render_process_host.h" #include "chrome/common/notification_observer.h" @@ -62,6 +64,7 @@ class BrowserRenderProcessHost : public RenderProcessHost, virtual void WidgetHidden(); virtual void AddWord(const std::wstring& word); virtual bool FastShutdownIfPossible(); + virtual TransportDIB* GetTransportDIB(TransportDIB::Id dib_id); // IPC::Channel::Sender via RenderProcessHost. virtual bool Send(IPC::Message* msg); @@ -99,6 +102,7 @@ class BrowserRenderProcessHost : public RenderProcessHost, void OnClipboardReadText(std::wstring* result); void OnClipboardReadAsciiText(std::string* result); void OnClipboardReadHTML(std::wstring* markup, GURL* src_url); + void OnUpdatedCacheStats(const CacheManager::UsageStats& stats); // Initialize support for visited links. Send the renderer process its initial @@ -140,6 +144,20 @@ class BrowserRenderProcessHost : public RenderProcessHost, // The host of audio renderers in the renderer process. scoped_refptr<AudioRendererHost> audio_renderer_host_; + // A map of transport DIB ids to cached TransportDIBs + std::map<TransportDIB::Id, TransportDIB*> cached_dibs_; + enum { + // This is the maximum size of |cached_dibs_| + MAX_MAPPED_TRANSPORT_DIBS = 3, + }; + + // Map a transport DIB from its Id and return it. Returns NULL on error. + TransportDIB* MapTransportDIB(TransportDIB::Id dib_id); + + void ClearTransportDIBCache(); + // This is used to clear our cache five seconds after the last use. + base::DelayTimer<BrowserRenderProcessHost> cached_dibs_cleaner_; + DISALLOW_COPY_AND_ASSIGN(BrowserRenderProcessHost); }; diff --git a/chrome/browser/renderer_host/mock_render_process_host.cc b/chrome/browser/renderer_host/mock_render_process_host.cc index c345b59..dc33bc1 100644 --- a/chrome/browser/renderer_host/mock_render_process_host.cc +++ b/chrome/browser/renderer_host/mock_render_process_host.cc @@ -57,6 +57,18 @@ bool MockRenderProcessHost::Send(IPC::Message* msg) { return true; } +TransportDIB* MockRenderProcessHost::GetTransportDIB(TransportDIB::Id dib_id) { +#if defined(OS_WIN) + return TransportDIB::Map(dib_id.handle); +#elif defined(OS_MACOSX) + // On Mac, TransportDIBs are always created in the browser, so we cannot map + // one from a dib_id. + return TransportDIB::Create(100 * 100 * 4, 0); +#elif defined(OS_LINUX) + return TransportDIB::Map(dib_id); +#endif +} + void MockRenderProcessHost::OnMessageReceived(const IPC::Message& msg) { } diff --git a/chrome/browser/renderer_host/mock_render_process_host.h b/chrome/browser/renderer_host/mock_render_process_host.h index d1ed58d..bfe49ee 100644 --- a/chrome/browser/renderer_host/mock_render_process_host.h +++ b/chrome/browser/renderer_host/mock_render_process_host.h @@ -35,6 +35,7 @@ class MockRenderProcessHost : public RenderProcessHost { virtual void WidgetHidden(); virtual void AddWord(const std::wstring& word); virtual bool FastShutdownIfPossible(); + virtual TransportDIB* GetTransportDIB(TransportDIB::Id dib_id); // IPC::Channel::Sender via RenderProcessHost. virtual bool Send(IPC::Message* msg); diff --git a/chrome/browser/renderer_host/render_process_host.h b/chrome/browser/renderer_host/render_process_host.h index 8b90b3d..4f8179c 100644 --- a/chrome/browser/renderer_host/render_process_host.h +++ b/chrome/browser/renderer_host/render_process_host.h @@ -11,6 +11,7 @@ #include "base/process.h" #include "base/scoped_ptr.h" #include "chrome/common/ipc_sync_channel.h" +#include "chrome/common/transport_dib.h" class Profile; @@ -124,6 +125,15 @@ class RenderProcessHost : public IPC::Channel::Sender, // Returns True if it was able to do fast shutdown. virtual bool FastShutdownIfPossible() = 0; + // Transport DIB functions --------------------------------------------------- + + // Return the TransportDIB for the given id. On Linux, this can involve + // mapping shared memory. On Mac, the shared memory is created in the browser + // process and the cached metadata is returned. On Windows, this involves + // duplicating the handle from the remote process. The RenderProcessHost + // still owns the returned DIB. + virtual TransportDIB* GetTransportDIB(TransportDIB::Id dib_id) = 0; + // Static management functions ----------------------------------------------- // Flag to run the renderer in process. This is primarily diff --git a/chrome/browser/renderer_host/render_widget_helper.cc b/chrome/browser/renderer_host/render_widget_helper.cc index a03bb1b..715a85b 100644 --- a/chrome/browser/renderer_host/render_widget_helper.cc +++ b/chrome/browser/renderer_host/render_widget_helper.cc @@ -56,6 +56,10 @@ RenderWidgetHelper::~RenderWidgetHelper() { // The elements of pending_paints_ each hold an owning reference back to this // object, so we should not be destroyed unless pending_paints_ is empty! DCHECK(pending_paints_.empty()); + +#if defined(OS_MACOSX) + ClearAllocatedDIBs(); +#endif } int RenderWidgetHelper::GetNextRoutingID() { @@ -244,3 +248,58 @@ void RenderWidgetHelper::OnSimulateReceivedMessage( if (host) host->OnMessageReceived(message); } + +#if defined(OS_MACOSX) +TransportDIB* RenderWidgetHelper::MapTransportDIB(TransportDIB::Id dib_id) { + AutoLock locked(allocated_dibs_lock_); + + const std::map<TransportDIB::Id, int>::iterator + i = allocated_dibs_.find(dib_id); + if (i == allocated_dibs_.end()) + return NULL; + + base::FileDescriptor fd(dup(i->second), true); + return TransportDIB::Map(fd); +} + +void RenderWidgetHelper::AllocTransportDIB( + size_t size, IPC::Maybe<TransportDIB::Handle>* result) { + base::SharedMemory* shared_memory = new base::SharedMemory(); + if (!shared_memory->Create(L"", false /* read write */, + false /* do not open existing */, size)) { + result->valid = false; + delete shared_memory; + return; + } + + result->valid = true; + shared_memory->GiveToProcess(0 /* pid, not needed */, &result->value); + + // Keep a copy of the file descriptor around + AutoLock locked(allocated_dibs_lock_); + allocated_dibs_[shared_memory->id()] = dup(result->value.fd); +} + +void RenderWidgetHelper::FreeTransportDIB(TransportDIB::Id dib_id) { + AutoLock locked(allocated_dibs_lock_); + + const std::map<TransportDIB::Id, int>::iterator + i = allocated_dibs_.find(dib_id); + + if (i != allocated_dibs_.end()) { + close(i->second); + allocated_dibs_.erase(i); + } else { + DLOG(WARNING) << "Renderer asked us to free unknown transport DIB"; + } +} + +void RenderWidgetHelper::ClearAllocatedDIBs() { + for (std::map<TransportDIB::Id, int>::iterator + i = allocated_dibs_.begin(); i != allocated_dibs_.end(); ++i) { + close(i->second); + } + + allocated_dibs_.clear(); +} +#endif diff --git a/chrome/browser/renderer_host/render_widget_helper.h b/chrome/browser/renderer_host/render_widget_helper.h index 83cedb0..eb13ff0 100644 --- a/chrome/browser/renderer_host/render_widget_helper.h +++ b/chrome/browser/renderer_host/render_widget_helper.h @@ -1,4 +1,3 @@ - // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -12,7 +11,9 @@ #include "base/ref_counted.h" #include "base/lock.h" #include "base/waitable_event.h" +#include "chrome/common/ipc_maybe.h" #include "chrome/common/modal_dialog_event.h" +#include "chrome/common/transport_dib.h" namespace IPC { class Message; @@ -74,6 +75,15 @@ class ResourceDispatcherHost; // GetBackingStore method is called, it will call WaitForPaintMsg if it has // no backingstore. // +// TRANSPORT DIB CREATION +// +// On some platforms (currently the Mac) the renderer cannot create transport +// DIBs because of sandbox limitations. Thus, it has to make synchronous IPCs +// to the browser for them. Since these requests are synchronous, they cannot +// terminate on the UI thread. Thus, in this case, this object performs the +// allocation and maintains the set of allocated transport DIBs which the +// renderers can refer to. +// class RenderWidgetHelper : public base::RefCountedThreadSafe<RenderWidgetHelper> { public: @@ -99,6 +109,11 @@ class RenderWidgetHelper : const base::TimeDelta& max_delay, IPC::Message* msg); +#if defined(OS_MACOSX) + // Given the id of a transport DIB, return a mapping to it or NULL on error. + TransportDIB* MapTransportDIB(TransportDIB::Id dib_id); +#endif + // IO THREAD ONLY ----------------------------------------------------------- @@ -114,6 +129,14 @@ class RenderWidgetHelper : ModalDialogEvent* modal_dialog_event); void CreateNewWidget(int opener_id, bool activatable, int* route_id); +#if defined(OS_MACOSX) + // Called on the IO thread to handle the allocation of a transport DIB + void AllocTransportDIB(size_t size, IPC::Maybe<TransportDIB::Handle>* result); + + // Called on the IO thread to handle the freeing of a transport DIB + void FreeTransportDIB(TransportDIB::Id dib_id); +#endif + private: // A class used to proxy a paint message. PaintMsgProxy objects are created // on the IO thread and destroyed on the UI thread. @@ -141,6 +164,16 @@ class RenderWidgetHelper : int new_render_process_host_id, int new_request_id); +#if defined(OS_MACOSX) + // Called on destruction to release all allocated transport DIBs + void ClearAllocatedDIBs(); + + // On OSX we keep file descriptors to all the allocated DIBs around until + // the renderer frees them. + Lock allocated_dibs_lock_; + std::map<TransportDIB::Id, int> allocated_dibs_; +#endif + // A map of live paint messages. Must hold pending_paints_lock_ to access. // The PaintMsgProxy objects are not owned by this map. (See PaintMsgProxy // for details about how the lifetime of instances are managed.) diff --git a/chrome/browser/renderer_host/render_widget_host.cc b/chrome/browser/renderer_host/render_widget_host.cc index a3c3018..1bd81cd 100644 --- a/chrome/browser/renderer_host/render_widget_host.cc +++ b/chrome/browser/renderer_host/render_widget_host.cc @@ -424,10 +424,20 @@ void RenderWidgetHost::OnMsgPaintRect( DCHECK(!params.bitmap_rect.IsEmpty()); DCHECK(!params.view_size.IsEmpty()); - // Paint the backing store. This will update it with the renderer-supplied - // bits. The view will read out of the backing store later to actually draw - // to the screen. - PaintBackingStoreRect(params.bitmap, params.bitmap_rect, params.view_size); + const size_t size = params.bitmap_rect.height() * + params.bitmap_rect.width() * 4; + TransportDIB* dib = process_->GetTransportDIB(params.bitmap); + if (dib) { + if (dib->size() < size) { + DLOG(WARNING) << "Transport DIB too small for given rectangle"; + process()->ReceivedBadMessage(ViewHostMsg_PaintRect__ID); + } else { + // Paint the backing store. This will update it with the renderer-supplied + // bits. The view will read out of the backing store later to actually draw + // to the screen. + PaintBackingStoreRect(dib, params.bitmap_rect, params.view_size); + } + } // ACK early so we can prefetch the next PaintRect if there is a next one. // This must be done AFTER we're done painting with the bitmap supplied by the @@ -474,10 +484,20 @@ void RenderWidgetHost::OnMsgScrollRect( DCHECK(!params.view_size.IsEmpty()); - // Scroll the backing store. - ScrollBackingStoreRect(params.bitmap, params.bitmap_rect, - params.dx, params.dy, - params.clip_rect, params.view_size); + const size_t size = params.bitmap_rect.height() * + params.bitmap_rect.width() * 4; + TransportDIB* dib = process_->GetTransportDIB(params.bitmap); + if (dib) { + if (dib->size() < size) { + LOG(WARNING) << "Transport DIB too small for given rectangle"; + process()->ReceivedBadMessage(ViewHostMsg_PaintRect__ID); + } else { + // Scroll the backing store. + ScrollBackingStoreRect(dib, params.bitmap_rect, + params.dx, params.dy, + params.clip_rect, params.view_size); + } + } // ACK early so we can prefetch the next ScrollRect if there is a next one. // This must be done AFTER we're done painting with the bitmap supplied by the @@ -561,7 +581,7 @@ void RenderWidgetHost::OnMsgImeUpdateStatus(int control, } } -void RenderWidgetHost::PaintBackingStoreRect(BitmapWireData bitmap, +void RenderWidgetHost::PaintBackingStoreRect(TransportDIB* bitmap, const gfx::Rect& bitmap_rect, const gfx::Size& view_size) { if (is_hidden_) { @@ -590,7 +610,7 @@ void RenderWidgetHost::PaintBackingStoreRect(BitmapWireData bitmap, } } -void RenderWidgetHost::ScrollBackingStoreRect(BitmapWireData bitmap, +void RenderWidgetHost::ScrollBackingStoreRect(TransportDIB* bitmap, const gfx::Rect& bitmap_rect, int dx, int dy, const gfx::Rect& clip_rect, diff --git a/chrome/browser/renderer_host/render_widget_host.h b/chrome/browser/renderer_host/render_widget_host.h index 48fc912..8c8f007 100644 --- a/chrome/browser/renderer_host/render_widget_host.h +++ b/chrome/browser/renderer_host/render_widget_host.h @@ -9,7 +9,6 @@ #include "base/gfx/size.h" #include "base/timer.h" -#include "chrome/common/bitmap_wire_data.h" #include "chrome/common/ipc_channel.h" #include "testing/gtest/include/gtest/gtest_prod.h" @@ -21,6 +20,7 @@ class BackingStore; class PaintObserver; class RenderProcessHost; class RenderWidgetHostView; +class TransportDIB; class WebInputEvent; class WebKeyboardEvent; class WebMouseEvent; @@ -262,14 +262,14 @@ class RenderWidgetHost : public IPC::Channel::Listener { void OnMsgImeUpdateStatus(int control, const gfx::Rect& caret_rect); // Paints the given bitmap to the current backing store at the given location. - void PaintBackingStoreRect(BitmapWireData bitmap, + void PaintBackingStoreRect(TransportDIB* dib, const gfx::Rect& bitmap_rect, const gfx::Size& view_size); // Scrolls the given |clip_rect| in the backing by the given dx/dy amount. The - // |bitmap| and its corresponding location |bitmap_rect| in the backing store + // |dib| and its corresponding location |bitmap_rect| in the backing store // is the newly painted pixels by the renderer. - void ScrollBackingStoreRect(BitmapWireData bitmap, + void ScrollBackingStoreRect(TransportDIB* dib, const gfx::Rect& bitmap_rect, int dx, int dy, const gfx::Rect& clip_rect, diff --git a/chrome/browser/renderer_host/render_widget_host_unittest.cc b/chrome/browser/renderer_host/render_widget_host_unittest.cc index 4f0bfb6..8a31ec91 100644 --- a/chrome/browser/renderer_host/render_widget_host_unittest.cc +++ b/chrome/browser/renderer_host/render_widget_host_unittest.cc @@ -10,10 +10,6 @@ #include "chrome/common/render_messages.h" #include "testing/gtest/include/gtest/gtest.h" -#if defined(OS_POSIX) -#include "skia/ext/platform_canvas.h" -#endif - namespace { // RenderWidgetHostProcess ----------------------------------------------------- @@ -22,9 +18,7 @@ class RenderWidgetHostProcess : public MockRenderProcessHost { public: explicit RenderWidgetHostProcess(Profile* profile) : MockRenderProcessHost(profile), -#if defined(OS_WIN) current_paint_buf_(NULL), -#endif paint_msg_should_reply_(false), paint_msg_reply_flags_(0) { // DANGER! This is a hack. The RenderWidgetHost checks the channel to see @@ -54,11 +48,7 @@ class RenderWidgetHostProcess : public MockRenderProcessHost { const base::TimeDelta& max_delay, IPC::Message* msg); -#if defined(OS_WIN) - scoped_ptr<base::SharedMemory> current_paint_buf_; -#elif defined(OS_POSIX) - skia::PlatformCanvas canvas; -#endif + TransportDIB* current_paint_buf_; // Set to true when WaitForPaintMsg should return a successful paint messaage // reply. False implies timeout. @@ -75,20 +65,10 @@ void RenderWidgetHostProcess::InitPaintRectParams( ViewHostMsg_PaintRect_Params* params) { // Create the shared backing store. const int w = 100, h = 100; + const size_t pixel_size = w * h * 4; -#if defined(OS_WIN) - int pixel_size = w * h * 4; - - current_paint_buf_.reset(new base::SharedMemory()); - ASSERT_TRUE(current_paint_buf_->Create(std::wstring(), false, true, - pixel_size)); - - params->bitmap = current_paint_buf_->handle(); -#elif defined(OS_POSIX) - ASSERT_TRUE(canvas.initialize(w, h, true)); - params->bitmap = canvas.getDevice()->accessBitmap(false); -#endif - + current_paint_buf_ = TransportDIB::Create(pixel_size, 0); + params->bitmap = current_paint_buf_->id(); params->bitmap_rect = gfx::Rect(0, 0, w, h); params->view_size = gfx::Size(w, h); params->flags = paint_msg_reply_flags_; diff --git a/chrome/browser/renderer_host/resource_message_filter.cc b/chrome/browser/renderer_host/resource_message_filter.cc index 8616e29..2215257 100644 --- a/chrome/browser/renderer_host/resource_message_filter.cc +++ b/chrome/browser/renderer_host/resource_message_filter.cc @@ -234,6 +234,12 @@ bool ResourceMessageFilter::OnMessageReceived(const IPC::Message& message) { OnNotifyAudioPacketReady) IPC_MESSAGE_HANDLER(ViewHostMsg_GetAudioVolume, OnGetAudioVolume) IPC_MESSAGE_HANDLER(ViewHostMsg_SetAudioVolume, OnSetAudioVolume) +#if defined(OS_MACOSX) + IPC_MESSAGE_HANDLER(ViewHostMsg_AllocTransportDIB, + OnAllocTransportDIB) + IPC_MESSAGE_HANDLER(ViewHostMsg_FreeTransportDIB, + OnFreeTransportDIB) +#endif IPC_MESSAGE_UNHANDLED( handled = false) IPC_END_MESSAGE_MAP_EX() @@ -794,3 +800,15 @@ void ResourceMessageFilter::OnSetAudioVolume( double left_channel, double right_channel) { // TODO(hclam): delegate to AudioRendererHost and handle this message. } + +#if defined(OS_MACOSX) +void ResourceMessageFilter::OnAllocTransportDIB( + size_t size, IPC::Maybe<TransportDIB::Handle>* handle) { + render_widget_helper_->AllocTransportDIB(size, handle); +} + +void ResourceMessageFilter::OnFreeTransportDIB( + TransportDIB::Id dib_id) { + render_widget_helper_->FreeTransportDIB(dib_id); +} +#endif diff --git a/chrome/browser/renderer_host/resource_message_filter.h b/chrome/browser/renderer_host/resource_message_filter.h index 85ad101..ef389a8 100644 --- a/chrome/browser/renderer_host/resource_message_filter.h +++ b/chrome/browser/renderer_host/resource_message_filter.h @@ -15,8 +15,10 @@ #include "chrome/browser/net/resolve_proxy_msg_helper.h" #include "chrome/browser/renderer_host/resource_dispatcher_host.h" #include "chrome/common/ipc_channel_proxy.h" +#include "chrome/common/ipc_maybe.h" #include "chrome/common/modal_dialog_event.h" #include "chrome/common/notification_observer.h" +#include "chrome/common/transport_dib.h" #include "webkit/glue/cache_manager.h" #if defined(OS_WIN) @@ -167,7 +169,7 @@ class ResourceMessageFilter : public IPC::ChannelProxy::MessageFilter, void OnResourceTypeStats(const CacheManager::ResourceTypeStats& stats); void OnResolveProxy(const GURL& url, IPC::Message* reply_msg); - + // ResolveProxyMsgHelper::Delegate implementation: virtual void OnResolveProxyCompleted(IPC::Message* reply_msg, int result, @@ -203,6 +205,11 @@ class ResourceMessageFilter : public IPC::ChannelProxy::MessageFilter, void OnSetAudioVolume(const IPC::Message& msg, int stream_id, double left_channel, double right_channel); + // Browser side transport DIB allocation + void OnAllocTransportDIB(size_t size, + IPC::Maybe<TransportDIB::Handle>* result); + void OnFreeTransportDIB(TransportDIB::Id dib_id); + // We have our own clipboard service because we want to access the clipboard // on the IO thread instead of forwarding (possibly synchronous) messages to // the UI thread. diff --git a/chrome/chrome.xcodeproj/project.pbxproj b/chrome/chrome.xcodeproj/project.pbxproj index 8ab2f18..706b762 100644 --- a/chrome/chrome.xcodeproj/project.pbxproj +++ b/chrome/chrome.xcodeproj/project.pbxproj @@ -294,6 +294,7 @@ 82FA32330F3A4CC400271C5A /* web_contents_view.cc in Sources */ = {isa = PBXBuildFile; fileRef = B6CCB9F20F1EC32700106F0D /* web_contents_view.cc */; }; 82FA32760F3A537C00271C5A /* web_contents_view_mac.mm in Sources */ = {isa = PBXBuildFile; fileRef = 82FA32750F3A537C00271C5A /* web_contents_view_mac.mm */; }; 82FA33460F3A7F6900271C5A /* render_widget_host_view_mac.mm in Sources */ = {isa = PBXBuildFile; fileRef = 82FA33450F3A7F6900271C5A /* render_widget_host_view_mac.mm */; }; + 8385551004565907D74AD2E0 /* transport_dib_mac.cc in Sources */ = {isa = PBXBuildFile; fileRef = F174BA4A5FF6B3DFC64105AD /* transport_dib_mac.cc */; }; 844EA0870F3E0C3B00B0EF26 /* debugger_host_impl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 844EA0750F3E0C1000B0EF26 /* debugger_host_impl.cpp */; }; 844EA0880F3E0C4500B0EF26 /* debugger_io_socket.cc in Sources */ = {isa = PBXBuildFile; fileRef = 844EA0780F3E0C1000B0EF26 /* debugger_io_socket.cc */; }; 844EA08D0F3E0C5000B0EF26 /* debugger_node.cc in Sources */ = {isa = PBXBuildFile; fileRef = 844EA07A0F3E0C1000B0EF26 /* debugger_node.cc */; }; @@ -2779,6 +2780,7 @@ E4F324790EE5D17E002533CE /* referrer.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = referrer.cc; sourceTree = "<group>"; }; EA72C084DB3FC0FC595E525E /* template_url_model.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = template_url_model.cc; sourceTree = "<group>"; }; EA72CF50C0AB4492A644C703 /* url_fetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = url_fetcher.h; sourceTree = "<group>"; }; + F174BA4A5FF6B3DFC64105AD /* transport_dib_mac.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = transport_dib_mac.cc; sourceTree = "<group>"; }; F4143C8B0F4B1D07008C8F73 /* renderer.sb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = renderer.sb; sourceTree = "<group>"; }; F60D7722C1302E1EC789B67A /* ssl_host_state.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ssl_host_state.cc; path = ssl/ssl_host_state.cc; sourceTree = "<group>"; }; /* End PBXFileReference section */ @@ -3781,6 +3783,7 @@ 4D7BFC0A0E9D4C9F009A6919 /* time_format.cc */, 4D7BFC0B0E9D4C9F009A6919 /* time_format.h */, 4D7BFC0C0E9D4C9F009A6919 /* time_format_unittest.cc */, + F174BA4A5FF6B3DFC64105AD /* transport_dib_mac.cc */, E45076E40F153AB6003BE099 /* unzip.cc */, E45076E30F153AB6003BE099 /* unzip.h */, E45076E80F153B06003BE099 /* unzip_unittest.cc */, @@ -5667,6 +5670,7 @@ 4D7BFC970E9D4D3E009A6919 /* throb_animation.cc in Sources */, 4D7BFC9C0E9D4D46009A6919 /* thumbnail_score.cc in Sources */, E45076AB0F153629003BE099 /* time_format.cc in Sources */, + 8385551004565907D74AD2E0 /* transport_dib_mac.cc in Sources */, E45076E50F153AB6003BE099 /* unzip.cc in Sources */, 406DFE278638D6132B21B2C9 /* url_pattern.cc in Sources */, E46C4B3F0F21095400B393B8 /* url_request_intercept_job.cc in Sources */, diff --git a/chrome/common/bitmap_wire_data.h b/chrome/common/bitmap_wire_data.h deleted file mode 100644 index c4e1edd..0000000 --- a/chrome/common/bitmap_wire_data.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_COMMON_BITMAP_WIRE_DATA_H_ -#define CHROME_COMMON_BITMAP_WIRE_DATA_H_ - -#if defined(OS_POSIX) -class SkBitmap; -#endif - -// BitmapWireData is the type of the bitmap data which is carried from renderer -// to browser over the wire. - -#if defined(OS_WIN) - -// On Windows, the bitmap data is carried out-of-band in a shared memory -// segment. This is the handle to the shared memory. These handles are valid -// only in the context of the renderer process. -// TODO(agl): get a clarification on that last sentence. It doesn't make any -// sense to me -typedef HANDLE BitmapWireData; - -#elif defined(OS_POSIX) - -// On POSIX, we currently serialise the bitmap data over the wire. This will -// change at some point when we too start using shared memory, but we wish to -// use shared memory in a different way so this is a temporary work-around. -// TODO(port): implement drawing with shared backing stores and replace this -// with an IPC no-op type. -typedef SkBitmap BitmapWireData; - -#endif // defined(OS_WIN) - -#endif // CHROME_COMMON_BITMAP_WIRE_DATA_H_ diff --git a/chrome/common/common.scons b/chrome/common/common.scons index 06a9888..b2cc936 100644 --- a/chrome/common/common.scons +++ b/chrome/common/common.scons @@ -275,6 +275,21 @@ if not env.Bit('posix'): 'ipc_channel_posix.cc', ) +if env.Bit('windows'): + input_files.Append( + 'transport_dib_win.cc' + ) + +if env.Bit('linux'): + input_files.Append( + 'transport_dib_linux.cc' + ) + +if env.Bit('mac'): + input_files.Append( + 'transport_dib_mac.cc' + ) + if not env.Bit('mac'): # TODO(port): This should be enabled for all platforms. env.ChromeLibrary('common', input_files) diff --git a/chrome/common/common.vcproj b/chrome/common/common.vcproj index 8ac8d29..67a664e 100644 --- a/chrome/common/common.vcproj +++ b/chrome/common/common.vcproj @@ -537,7 +537,7 @@ RelativePath=".\notification_details.h" > </File> - <File + <File RelativePath=".\notification_observer.h" > </File> @@ -742,6 +742,14 @@ > </File> <File + RelativePath=".\transport_dib.h" + > + </File> + <File + RelativePath=".\transport_dib_win.cc" + > + </File> + <File RelativePath=".\unzip.cc" > </File> diff --git a/chrome/common/ipc_maybe.h b/chrome/common/ipc_maybe.h new file mode 100644 index 0000000..de45dc0 --- /dev/null +++ b/chrome/common/ipc_maybe.h @@ -0,0 +1,19 @@ +#ifndef CHROME_COMMON_MAYBE_H_ +#define CHROME_COMMON_MAYBE_H_ + +namespace IPC { + +// The Maybe type can be used to avoid serialising a type when it's invalid. +// This is most useful in conjunction with FileDescriptor, as there's no +// possible invalid value which can be serialised (one can type to use -1, but +// the IPC channel will fail). It may also be useful if the invalid value is +// otherwise expensive to serialise. +template<typename A> +struct Maybe { + bool valid; + A value; +}; + +} // namespace IPC + +#endif // CHROME_COMMON_MAYBE_H_ diff --git a/chrome/common/ipc_message_utils.h b/chrome/common/ipc_message_utils.h index ae42bb0..428c889 100644 --- a/chrome/common/ipc_message_utils.h +++ b/chrome/common/ipc_message_utils.h @@ -15,8 +15,10 @@ #if defined(OS_POSIX) #include "chrome/common/file_descriptor_set_posix.h" #endif +#include "chrome/common/ipc_maybe.h" #include "chrome/common/ipc_sync_message.h" #include "chrome/common/thumbnail_score.h" +#include "chrome/common/transport_dib.h" #include "webkit/glue/cache_manager.h" #include "webkit/glue/console_message_level.h" #include "webkit/glue/find_in_page_request.h" @@ -1045,6 +1047,55 @@ struct ParamTraits< Tuple6<A, B, C, D, E, F> > { } }; +#if defined(OS_WIN) +template<> +struct ParamTraits<TransportDIB::Id> { + typedef TransportDIB::Id param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.handle); + WriteParam(m, p.sequence_num); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return (ReadParam(m, iter, &r->handle) && + ReadParam(m, iter, &r->sequence_num)); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"TransportDIB("); + LogParam(p.handle, l); + l->append(L", "); + LogParam(p.sequence_num, l); + l->append(L")"); + } +}; +#endif + +template<typename A> +struct ParamTraits<Maybe<A> > { + typedef struct Maybe<A> param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.valid); + if (p.valid) + WriteParam(m, p.value); + } + static bool Read(const Message* m, void** iter, param_type* r) { + if (!ReadParam(m, iter, &r->valid)) + return false; + + if (r->valid) + return ReadParam(m, iter, &r->value); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + if (p.valid) { + l->append(L"Just "); + ParamTraits<A>::Log(p.value, l); + } else { + l->append(L"Nothing"); + } + + } +}; + template <> struct ParamTraits<webkit_glue::WebApplicationInfo> { typedef webkit_glue::WebApplicationInfo param_type; diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h index 8c91097..e78970e 100644 --- a/chrome/common/render_messages.h +++ b/chrome/common/render_messages.h @@ -15,11 +15,11 @@ #include "base/shared_memory.h" #include "chrome/browser/renderer_host/resource_handler.h" #include "chrome/common/accessibility.h" -#include "chrome/common/bitmap_wire_data.h" #include "chrome/common/filter_policy.h" #include "chrome/common/ipc_message_utils.h" #include "chrome/common/modal_dialog_event.h" #include "chrome/common/page_transition_types.h" +#include "chrome/common/transport_dib.h" #include "googleurl/src/gurl.h" #include "media/audio/audio_output.h" #include "net/base/upload_data.h" @@ -150,7 +150,7 @@ struct ViewHostMsg_PaintRect_Flags { struct ViewHostMsg_PaintRect_Params { // The bitmap to be painted into the rect given by bitmap_rect. - BitmapWireData bitmap; + TransportDIB::Id bitmap; // The position and size of the bitmap. gfx::Rect bitmap_rect; @@ -183,7 +183,7 @@ struct ViewHostMsg_PaintRect_Params { // parameters to be reasonably put in a predefined IPC message. struct ViewHostMsg_ScrollRect_Params { // The bitmap to be painted into the rect exposed by scrolling. - BitmapWireData bitmap; + TransportDIB::Id bitmap; // The position and size of the bitmap. gfx::Rect bitmap_rect; diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h index 02a9a43..f02f5cf 100644 --- a/chrome/common/render_messages_internal.h +++ b/chrome/common/render_messages_internal.h @@ -16,6 +16,8 @@ #include "base/gfx/native_widget_types.h" #include "base/shared_memory.h" #include "chrome/common/ipc_message_macros.h" +#include "chrome/common/ipc_maybe.h" +#include "chrome/common/transport_dib.h" #include "skia/include/SkBitmap.h" #include "webkit/glue/console_message_level.h" #include "webkit/glue/dom_operations.h" @@ -1168,4 +1170,19 @@ IPC_BEGIN_MESSAGES(ViewHost) double /* left_channel */, double /* right_channel */) +#if defined(OS_MACOSX) + // On OSX, we cannot allocated shared memory from within the sandbox, so + // this call exists for the renderer to ask the browser to allocate memory + // on its behalf. We return a file descriptor to the POSIX shared memory. + IPC_SYNC_MESSAGE_CONTROL1_1(ViewHostMsg_AllocTransportDIB, + size_t, /* bytes requested */ + IPC::Maybe<TransportDIB::Handle> /* DIB */) + + // Since the browser keeps handles to the allocated transport DIBs, this + // message is sent to tell the browser that it may release them when the + // renderer is finished with them. + IPC_MESSAGE_CONTROL1(ViewHostMsg_FreeTransportDIB, + TransportDIB::Id /* DIB id */) +#endif + IPC_END_MESSAGES(ViewHost) diff --git a/chrome/common/transport_dib.h b/chrome/common/transport_dib.h new file mode 100644 index 0000000..e5fc009 --- /dev/null +++ b/chrome/common/transport_dib.h @@ -0,0 +1,112 @@ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_TRANSPORT_DIB_H_ +#define CHROME_COMMON_TRANSPORT_DIB_H_ + +#include "base/basictypes.h" + +#if defined(OS_WIN) || defined(OS_MACOSX) +#include "base/shared_memory.h" +#endif + +#if defined(OS_WIN) +#include <windows.h> +#endif + +// ----------------------------------------------------------------------------- +// A TransportDIB is a block of memory that is used to transport pixels +// from the renderer process to the browser. +// ----------------------------------------------------------------------------- +class TransportDIB { + public: + ~TransportDIB(); + + // Two typedefs are defined. A Handle is the type which can be sent over + // the wire so that the remote side can map the transport DIB. The Id typedef + // is sufficient to identify the transport DIB when you know that the remote + // side already may have it mapped. +#if defined(OS_WIN) + typedef HANDLE Handle; + // On Windows, the Id type includes a sequence number (epoch) to solve an ABA + // issue: + // 1) Process A creates a transport DIB with HANDLE=1 and sends to B. + // 2) Process B maps the transport DIB and caches 1 -> DIB. + // 3) Process A closes the transport DIB and creates a new one. The new DIB + // is also assigned HANDLE=1. + // 4) Process A sends the Handle to B, but B incorrectly believes that it + // already has it cached. + struct HandleAndSequenceNum { + HandleAndSequenceNum() + : handle(NULL), + sequence_num(0) { + } + + HandleAndSequenceNum(HANDLE h, uint32 seq_num) + : handle(h), + sequence_num(seq_num) { + } + + bool operator< (const HandleAndSequenceNum& other) const { + if (other.handle < handle) + return true; + if (other.sequence_num < sequence_num) + return true; + return false; + } + + HANDLE handle; + uint32 sequence_num; + }; + typedef HandleAndSequenceNum Id; +#elif defined(OS_MACOSX) + typedef base::SharedMemoryHandle Handle; + // On Mac, the inode number of the backing file is used as an id. + typedef base::SharedMemoryId Id; +#elif defined(OS_LINUX) + typedef int Handle; // These two ints are SysV IPC shared memory keys + typedef int Id; +#endif + + // Create a new TransportDIB + // size: the minimum size, in bytes + // epoch: Windows only: a global counter. See comment above. + // returns: NULL on failure + static TransportDIB* Create(size_t size, uint32 sequence_num); + + // Map the referenced transport DIB. Returns NULL on failure. + static TransportDIB* Map(Handle transport_dib); + + // Return a pointer to the shared memory + void* memory() const; + + // Return the maximum size of the shared memory. This is not the amount of + // data which is valid, you have to know that via other means, this is simply + // the maximum amount that /could/ be valid. + size_t size() const { return size_; } + + // Return the identifier which can be used to refer to this shared memory + // on the wire. + Id id() const; + + // Return a handle to the underlying shared memory. This can be sent over the + // wire to give this transport DIB to another process. + Handle handle() const; + + private: + TransportDIB(); +#if defined(OS_WIN) || defined(OS_MACOSX) + explicit TransportDIB(base::SharedMemoryHandle dib); + base::SharedMemory shared_memory_; + uint32 sequence_num_; +#elif defined(OS_LINUX) + int key_; // SysV shared memory id + void* address_; // mapped address +#endif + size_t size_; // length, in bytes +}; + +class MessageLoop; + +#endif // CHROME_COMMON_TRANSPORT_DIB_H_ diff --git a/chrome/common/transport_dib_linux.cc b/chrome/common/transport_dib_linux.cc new file mode 100644 index 0000000..adecf1d --- /dev/null +++ b/chrome/common/transport_dib_linux.cc @@ -0,0 +1,85 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <errno.h> +#include <stdlib.h> +#include <sys/ipc.h> +#include <sys/shm.h> + +#include "base/logging.h" +#include "chrome/common/transport_dib.h" + +// The shmat system call uses this as it's invalid return address +static void *const kInvalidAddress = (void*) -1; + +TransportDIB::TransportDIB() + : key_(-1), + address_(kInvalidAddress), + size_(0) { +} + +TransportDIB::~TransportDIB() { + if (address_ != kInvalidAddress) { + shmdt(address_); + address_ = kInvalidAddress; + } +} + +// static +TransportDIB* TransportDIB::Create(size_t size, uint32 sequence_num) { + // We use a mode of 0666 since the X server won't attach to memory which is + // 0600 since it can't know if it (as a root process) is being asked to map + // someone else's private shared memory region. + const int shmkey = shmget(IPC_PRIVATE, size, 0666); + if (shmkey == -1) { + DLOG(ERROR) << "Failed to create SysV shared memory region" + << " errno:" << errno; + return false; + } + + void* address = shmat(shmkey, NULL /* desired address */, 0 /* flags */); + // Here we mark the shared memory for deletion. Since we attached it in the + // line above, it doesn't actually get deleted but, if we crash, this means + // that the kernel will automatically clean it up for us. + shmctl(shmkey, IPC_RMID, 0); + if (address == kInvalidAddress) + return false; + + TransportDIB* dib = new TransportDIB; + + dib->key_ = shmkey; + dib->address_ = address; + dib->size_ = size; + return dib; +} + +TransportDIB* TransportDIB::Map(Handle shmkey) { + struct shmid_ds shmst; + if (shmctl(shmkey, IPC_STAT, &shmst) == -1) + return NULL; + + void* address = shmat(shmkey, NULL /* desired address */, 0 /* flags */); + if (address == kInvalidAddress) + return NULL; + + TransportDIB* dib = new TransportDIB; + + dib->address_ = address; + dib->size_ = shmst.shm_segsz; + dib->key_ = shmkey; + return dib; +} + +void* TransportDIB::memory() const { + DCHECK_NE(address_, kInvalidAddress); + return address_; +} + +TransportDIB::Id TransportDIB::id() const { + return key_; +} + +TransportDIB::Handle TransportDIB::handle() const { + return key_; +} diff --git a/chrome/common/transport_dib_mac.cc b/chrome/common/transport_dib_mac.cc new file mode 100644 index 0000000..4fb67a7 --- /dev/null +++ b/chrome/common/transport_dib_mac.cc @@ -0,0 +1,63 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/transport_dib.h" + +#include <unistd.h> +#include <sys/stat.h> +#include "base/shared_memory.h" + +TransportDIB::TransportDIB() + : size_(0) { +} + +TransportDIB::TransportDIB(TransportDIB::Handle dib) + : shared_memory_(dib, false /* read write */), + size_(0) { +} + +TransportDIB::~TransportDIB() { +} + +// static +TransportDIB* TransportDIB::Create(size_t size, uint32 sequence_num) { + TransportDIB* dib = new TransportDIB; + if (!dib->shared_memory_.Create(L"", false /* read write */, + false /* do not open existing */, size)) { + delete dib; + return NULL; + } + + dib->size_ = size; + return dib; +} + +// static +TransportDIB* TransportDIB::Map(TransportDIB::Handle handle) { + TransportDIB* dib = new TransportDIB(handle); + struct stat st; + fstat(handle.fd, &st); + + if (!dib->shared_memory_.Map(st.st_size)) { + delete dib; + close(handle.fd); + return false; + } + + dib->size_ = st.st_size; + + return dib; +} + +void* TransportDIB::memory() const { + return shared_memory_.memory(); +} + +TransportDIB::Id TransportDIB::id() const { + return false; +} + +TransportDIB::Handle TransportDIB::handle() const { + return shared_memory_.handle(); +} diff --git a/chrome/common/transport_dib_win.cc b/chrome/common/transport_dib_win.cc new file mode 100644 index 0000000..e9ab424 --- /dev/null +++ b/chrome/common/transport_dib_win.cc @@ -0,0 +1,72 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <limits> +#include <windows.h> + +#include "base/logging.h" +#include "base/sys_info.h" +#include "chrome/common/transport_dib.h" + +TransportDIB::TransportDIB() { +} + +TransportDIB::~TransportDIB() { +} + +TransportDIB::TransportDIB(HANDLE handle) + : shared_memory_(handle, false /* read write */) { +} + +// static +TransportDIB* TransportDIB::Create(size_t size, uint32 sequence_num) { + size_t allocation_granularity = base::SysInfo::VMAllocationGranularity(); + size = size / allocation_granularity + 1; + size = size * allocation_granularity; + + TransportDIB* dib = new TransportDIB; + + if (!dib->shared_memory_.Create(L"", false /* read write */, + true /* open existing */, size)) { + delete dib; + return NULL; + } + + dib->size_ = size; + dib->sequence_num_ = sequence_num; + + return dib; +} + +// static +TransportDIB* TransportDIB::Map(TransportDIB::Handle handle) { + TransportDIB* dib = new TransportDIB(handle); + if (!dib->shared_memory_.Map(0 /* map whole shared memory segment */)) { + DLOG(ERROR) << "Failed to map transport DIB" + << " handle:" << handle + << " error:" << GetLastError(); + delete dib; + return NULL; + } + + // There doesn't seem to be any way to find the size of the shared memory + // region! GetFileSize indicates that the handle is invalid. Thus, we + // conservatively set the size to the maximum and hope that the renderer + // isn't about to ask us to read off the end of the array. + dib->size_ = std::numeric_limits<size_t>::max(); + + return dib; +} + +void* TransportDIB::memory() const { + return shared_memory_.memory(); +} + +TransportDIB::Handle TransportDIB::handle() const { + return shared_memory_.handle(); +} + +TransportDIB::Id TransportDIB::id() const { + return Id(shared_memory_.handle(), sequence_num_); +} diff --git a/chrome/renderer/render_process.cc b/chrome/renderer/render_process.cc index 853693b..198f854f 100644 --- a/chrome/renderer/render_process.cc +++ b/chrome/renderer/render_process.cc @@ -25,6 +25,7 @@ #include "chrome/common/ipc_channel.h" #include "chrome/common/ipc_message_utils.h" #include "chrome/common/render_messages.h" +#include "chrome/common/transport_dib.h" #include "chrome/renderer/render_view.h" #include "webkit/glue/webkit_glue.h" @@ -36,7 +37,10 @@ bool RenderProcess::load_plugins_in_process_ = false; RenderProcess::RenderProcess(const std::wstring& channel_name) : render_thread_(channel_name), - ALLOW_THIS_IN_INITIALIZER_LIST(clearer_factory_(this)) { + ALLOW_THIS_IN_INITIALIZER_LIST(shared_mem_cache_cleaner_( + base::TimeDelta::FromSeconds(5), + this, &RenderProcess::ClearTransportDIBCache)), + sequence_number_(0) { for (size_t i = 0; i < arraysize(shared_mem_cache_); ++i) shared_mem_cache_[i] = NULL; } @@ -48,7 +52,7 @@ RenderProcess::~RenderProcess() { // This race condition causes a crash when the renderer process is shutting // down. render_thread_.Stop(); - ClearSharedMemCache(); + ClearTransportDIBCache(); } // static @@ -124,97 +128,144 @@ bool RenderProcess::ShouldLoadPluginsInProcess() { return load_plugins_in_process_; } -// static -base::SharedMemory* RenderProcess::AllocSharedMemory(size_t size) { - self()->clearer_factory_.RevokeAll(); - - base::SharedMemory* mem = self()->GetSharedMemFromCache(size); - if (mem) - return mem; +// ----------------------------------------------------------------------------- +// Platform specific code for dealing with bitmap transport... - // Round-up size to allocation granularity - size_t allocation_granularity = base::SysInfo::VMAllocationGranularity(); - size = size / allocation_granularity + 1; - size = size * allocation_granularity; +// ----------------------------------------------------------------------------- +// Create a platform canvas object which renders into the given transport +// memory. +// ----------------------------------------------------------------------------- +static skia::PlatformCanvas* CanvasFromTransportDIB( + TransportDIB* dib, const gfx::Rect& rect) { +#if defined(OS_WIN) + return new skia::PlatformCanvas(rect.width(), rect.height(), true, + dib->handle()); +#elif defined(OS_LINUX) || defined(OS_MACOSX) + return new skia::PlatformCanvas(rect.width(), rect.height(), true, + reinterpret_cast<uint8_t*>(dib->memory())); +#endif +} - mem = new base::SharedMemory(); - if (!mem) +TransportDIB* RenderProcess::CreateTransportDIB(size_t size) { +#if defined(OS_WIN) || defined(OS_LINUX) + // Windows and Linux create transport DIBs inside the renderer + return TransportDIB::Create(size, sequence_number_++); +#elif defined(OS_MACOSX) // defined(OS_WIN) || defined(OS_LINUX) + // Mac creates transport DIBs in the browser, so we need to do a sync IPC to + // get one. + IPC::Maybe<TransportDIB::Handle> mhandle; + IPC::Message* msg = new ViewHostMsg_AllocTransportDIB(size, &mhandle); + if (!render_thread_.Send(msg)) return NULL; - if (!mem->Create(L"", false, true, size)) { - delete mem; + if (!mhandle.valid) return NULL; + return TransportDIB::Map(mhandle.value); +#endif // defined(OS_MACOSX) +} + +void RenderProcess::FreeTransportDIB(TransportDIB* dib) { + if (!dib) + return; + +#if defined(OS_MACOSX) + // On Mac we need to tell the browser that it can drop a reference to the + // shared memory. + IPC::Message* msg = new ViewHostMsg_FreeTransportDIB(dib->id()); + render_thread_.Send(msg); +#endif + + delete dib; +} + +// ----------------------------------------------------------------------------- + + +// static +skia::PlatformCanvas* RenderProcess::GetDrawingCanvas( + TransportDIB** memory, const gfx::Rect& rect) { + const size_t stride = skia::PlatformCanvas::StrideForWidth(rect.width()); + const size_t size = stride * rect.height(); + + if (!self()->GetTransportDIBFromCache(memory, size)) { + *memory = self()->CreateTransportDIB(size); + if (!*memory) + return false; } - return mem; + return CanvasFromTransportDIB(*memory, rect); } // static -void RenderProcess::FreeSharedMemory(base::SharedMemory* mem) { +void RenderProcess::ReleaseTransportDIB(TransportDIB* mem) { if (self()->PutSharedMemInCache(mem)) { - self()->ScheduleCacheClearer(); + self()->shared_mem_cache_cleaner_.Reset(); return; } - DeleteSharedMem(mem); -} -// static -void RenderProcess::DeleteSharedMem(base::SharedMemory* mem) { - delete mem; + self()->FreeTransportDIB(mem); } -base::SharedMemory* RenderProcess::GetSharedMemFromCache(size_t size) { +bool RenderProcess::GetTransportDIBFromCache(TransportDIB** mem, + size_t size) { // look for a cached object that is suitable for the requested size. for (size_t i = 0; i < arraysize(shared_mem_cache_); ++i) { - base::SharedMemory* mem = shared_mem_cache_[i]; - if (mem && mem->max_size() >= size) { + if (shared_mem_cache_[i] && + size <= shared_mem_cache_[i]->size()) { + *mem = shared_mem_cache_[i]; shared_mem_cache_[i] = NULL; - return mem; + return true; } } - return NULL; + + return false; } -bool RenderProcess::PutSharedMemInCache(base::SharedMemory* mem) { +int RenderProcess::FindFreeCacheSlot(size_t size) { // simple algorithm: // - look for an empty slot to store mem, or - // - if full, then replace any existing cache entry that is smaller than the - // given shared memory object. + // - if full, then replace smallest entry which is smaller than |size| for (size_t i = 0; i < arraysize(shared_mem_cache_); ++i) { - if (!shared_mem_cache_[i]) { - shared_mem_cache_[i] = mem; - return true; - } + if (shared_mem_cache_[i] == NULL) + return i; } - for (size_t i = 0; i < arraysize(shared_mem_cache_); ++i) { - base::SharedMemory* cached_mem = shared_mem_cache_[i]; - if (cached_mem->max_size() < mem->max_size()) { - shared_mem_cache_[i] = mem; - DeleteSharedMem(cached_mem); - return true; + + size_t smallest_size = size; + int smallest_index = -1; + + for (size_t i = 1; i < arraysize(shared_mem_cache_); ++i) { + const size_t entry_size = shared_mem_cache_[i]->size(); + if (entry_size < smallest_size) { + smallest_size = entry_size; + smallest_index = i; } } - return false; + + if (smallest_index != -1) { + FreeTransportDIB(shared_mem_cache_[smallest_index]); + shared_mem_cache_[smallest_index] = NULL; + } + + return smallest_index; } -void RenderProcess::ClearSharedMemCache() { +bool RenderProcess::PutSharedMemInCache(TransportDIB* mem) { + const int slot = FindFreeCacheSlot(mem->size()); + if (slot == -1) + return false; + + shared_mem_cache_[slot] = mem; + return true; +} + +void RenderProcess::ClearTransportDIBCache() { for (size_t i = 0; i < arraysize(shared_mem_cache_); ++i) { if (shared_mem_cache_[i]) { - DeleteSharedMem(shared_mem_cache_[i]); + FreeTransportDIB(shared_mem_cache_[i]); shared_mem_cache_[i] = NULL; } } } -void RenderProcess::ScheduleCacheClearer() { - // If we already have a deferred clearer, then revoke it so we effectively - // delay cache clearing until idle for our desired interval. - clearer_factory_.RevokeAll(); - - MessageLoop::current()->PostDelayedTask(FROM_HERE, - clearer_factory_.NewRunnableMethod(&RenderProcess::ClearSharedMemCache), - 5000 /* 5 seconds */); -} - void RenderProcess::Cleanup() { // TODO(port) // Try and limit what we pull in for our non-Win unit test bundle diff --git a/chrome/renderer/render_process.h b/chrome/renderer/render_process.h index 6ba0905..9c4cabe 100644 --- a/chrome/renderer/render_process.h +++ b/chrome/renderer/render_process.h @@ -5,13 +5,17 @@ #ifndef CHROME_RENDERER_RENDER_PROCESS_H__ #define CHROME_RENDERER_RENDER_PROCESS_H__ +#include "base/timer.h" #include "chrome/common/child_process.h" #include "chrome/renderer/render_thread.h" +#include "skia/ext/platform_canvas.h" -namespace base { - class SharedMemory; +namespace gfx { +class Rect; } +class TransportDIB; + // Represents the renderer end of the browser<->renderer connection. The // opposite end is the RenderProcessHost. This is a singleton object for // each renderer. @@ -23,19 +27,19 @@ class RenderProcess : public ChildProcess { // Returns true if plugins should be loaded in-process. static bool ShouldLoadPluginsInProcess(); - // Allocates shared memory. When no longer needed, you should pass the - // SharedMemory pointer to FreeSharedMemory so it can be recycled. The size - // reported in the resulting SharedMemory object will be greater than or - // equal to the requested size. This method returns NULL if unable to - // allocate memory for some reason. - static base::SharedMemory* AllocSharedMemory(size_t size); + // Get a canvas suitable for drawing and transporting to the browser + // memory: (output) the transport DIB memory + // rect: the rectangle which will be painted, use for sizing the canvas + // returns: NULL on error + // + // When no longer needed, you should pass the TransportDIB to + // ReleaseTransportDIB so that it can be recycled. + static skia::PlatformCanvas* GetDrawingCanvas( + TransportDIB** memory, const gfx::Rect& rect); // Frees shared memory allocated by AllocSharedMemory. You should only use // this function to free the SharedMemory object. - static void FreeSharedMemory(base::SharedMemory* mem); - - // Deletes the shared memory allocated by AllocSharedMemory. - static void DeleteSharedMem(base::SharedMemory* mem); + static void ReleaseTransportDIB(TransportDIB* memory); private: friend class ChildProcessFactory<RenderProcess>; @@ -50,21 +54,28 @@ class RenderProcess : public ChildProcess { static ChildProcess* ClassFactory(const std::wstring& channel_name); - // Look in the shared memory cache for a suitable object to reuse. Returns - // NULL if there is none. - base::SharedMemory* GetSharedMemFromCache(size_t size); + // Look in the shared memory cache for a suitable object to reuse. + // result: (output) the memory found + // size: the resulting memory will be >= this size, in bytes + // returns: false if a suitable DIB memory could not be found + bool GetTransportDIBFromCache(TransportDIB** result, size_t size); - // Maybe put the given shared memory into the shared memory cache. Returns + // Maybe put the given shared memory into the shared memory cache. Returns // true if the SharedMemory object was stored in the cache; otherwise, false // is returned. - bool PutSharedMemInCache(base::SharedMemory* mem); + bool PutSharedMemInCache(TransportDIB* memory); - void ClearSharedMemCache(); + void ClearTransportDIBCache(); - // We want to lazily clear the shared memory cache if no one has requested - // memory. This methods are used to schedule a deferred call to - // RenderProcess::ClearSharedMemCache. - void ScheduleCacheClearer(); + // Return the index of a free cache slot in which to install a transport DIB + // of the given size. If all entries in the cache are larger than the given + // size, this doesn't free any slots and returns -1. + int FindFreeCacheSlot(size_t size); + + // Create a new transport DIB of, at least, the given size. Return NULL on + // error. + TransportDIB* CreateTransportDIB(size_t size); + void FreeTransportDIB(TransportDIB*); // ChildProcess implementation virtual void Cleanup(); @@ -74,10 +85,13 @@ class RenderProcess : public ChildProcess { // A very simplistic and small cache. If an entry in this array is non-null, // then it points to a SharedMemory object that is available for reuse. - base::SharedMemory* shared_mem_cache_[2]; + TransportDIB* shared_mem_cache_[2]; + + // This DelayTimer cleans up our cache 5 seconds after the last use. + base::DelayTimer<RenderProcess> shared_mem_cache_cleaner_; - // This factory is used to lazily invoke ClearSharedMemCache. - ScopedRunnableMethodFactory<RenderProcess> clearer_factory_; + // TransportDIB sequence number + uint32 sequence_number_; static bool load_plugins_in_process_; diff --git a/chrome/renderer/render_process_unittest.cc b/chrome/renderer/render_process_unittest.cc index 0303d40..7b7b7c6 100644 --- a/chrome/renderer/render_process_unittest.cc +++ b/chrome/renderer/render_process_unittest.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/gfx/rect.h" #include "base/sys_info.h" #include "base/string_util.h" #include "chrome/renderer/render_process.h" @@ -24,11 +25,14 @@ class RenderProcessTest : public testing::Test { }; -TEST_F(RenderProcessTest, TestSharedMemoryAllocOne) { - size_t size = base::SysInfo::VMAllocationGranularity(); - base::SharedMemory* mem = RenderProcess::AllocSharedMemory(size); - ASSERT_TRUE(mem); - RenderProcess::FreeSharedMemory(mem); +TEST_F(RenderProcessTest, TestTransportDIBAllocation) { + const gfx::Rect rect(0, 0, 100, 100); + TransportDIB* dib; + skia::PlatformCanvas* canvas = RenderProcess::GetDrawingCanvas(&dib, rect); + ASSERT_TRUE(dib); + ASSERT_TRUE(canvas); + RenderProcess::ReleaseTransportDIB(dib); + delete canvas; } } // namespace diff --git a/chrome/renderer/render_widget.cc b/chrome/renderer/render_widget.cc index 04f16f1..7782fd5 100644 --- a/chrome/renderer/render_widget.cc +++ b/chrome/renderer/render_widget.cc @@ -11,6 +11,7 @@ #include "base/scoped_ptr.h" #include "build/build_config.h" #include "chrome/common/render_messages.h" +#include "chrome/common/transport_dib.h" #include "chrome/renderer/render_process.h" #include "skia/ext/platform_canvas.h" @@ -104,11 +105,11 @@ RenderWidget::RenderWidget(RenderThreadBase* render_thread, bool activatable) RenderWidget::~RenderWidget() { DCHECK(!webwidget_) << "Leaking our WebWidget!"; if (current_paint_buf_) { - RenderProcess::FreeSharedMemory(current_paint_buf_); + RenderProcess::ReleaseTransportDIB(current_paint_buf_); current_paint_buf_ = NULL; } if (current_scroll_buf_) { - RenderProcess::FreeSharedMemory(current_scroll_buf_); + RenderProcess::ReleaseTransportDIB(current_scroll_buf_); current_scroll_buf_ = NULL; } RenderProcess::ReleaseProcess(); @@ -297,20 +298,21 @@ void RenderWidget::OnPaintRectAck() { // If we sent a PaintRect message with a zero-sized bitmap, then // we should have no current paint buf. if (current_paint_buf_) { - RenderProcess::FreeSharedMemory(current_paint_buf_); + RenderProcess::ReleaseTransportDIB(current_paint_buf_); current_paint_buf_ = NULL; } + // Continue painting if necessary... DoDeferredPaint(); } void RenderWidget::OnScrollRectAck() { -#if defined(OS_WIN) DCHECK(scroll_reply_pending()); - RenderProcess::FreeSharedMemory(current_scroll_buf_); - current_scroll_buf_ = NULL; -#endif + if (current_scroll_buf_) { + RenderProcess::ReleaseTransportDIB(current_scroll_buf_); + current_scroll_buf_ = NULL; + } // Continue scrolling if necessary... DoDeferredScroll(); @@ -379,13 +381,6 @@ void RenderWidget::PaintRect(const gfx::Rect& rect, DidPaint(); } -// static -size_t RenderWidget::GetPaintBufSize(const gfx::Rect& rect) { - // TODO(darin): protect against overflow - const size_t stride = skia::PlatformCanvas::StrideForWidth(rect.width()); - return stride * rect.height(); -} - void RenderWidget::DoDeferredPaint() { if (!webwidget_ || paint_reply_pending() || paint_rect_.IsEmpty()) return; @@ -407,37 +402,23 @@ void RenderWidget::DoDeferredPaint() { paint_rect_ = gfx::Rect(); // Compute a buffer for painting and cache it. -#if defined(OS_WIN) - current_paint_buf_ = - RenderProcess::AllocSharedMemory(GetPaintBufSize(damaged_rect)); - if (!current_paint_buf_) { + skia::PlatformCanvas* canvas = + RenderProcess::GetDrawingCanvas(¤t_paint_buf_, damaged_rect); + if (!canvas) { NOTREACHED(); return; } - skia::PlatformCanvasWin canvas(damaged_rect.width(), damaged_rect.height(), - true, current_paint_buf_->handle()); -#elif defined(OS_POSIX) - // Currently, on POSIX, we are serialising the bitmap data over the IPC - // channel. - skia::PlatformCanvas canvas(damaged_rect.width(), damaged_rect.height(), - true); -#endif // defined(OS_POSIX) - PaintRect(damaged_rect, &canvas); + PaintRect(damaged_rect, canvas); ViewHostMsg_PaintRect_Params params; params.bitmap_rect = damaged_rect; params.view_size = size_; params.plugin_window_moves = plugin_window_moves_; params.flags = next_paint_flags_; + params.bitmap = current_paint_buf_->id(); -#if defined(OS_WIN) - // Windows passes a HANDLE to the shared memory over IPC - params.bitmap = current_paint_buf_->handle(); -#elif defined(OS_POSIX) - // POSIX currently passes the data itself. - params.bitmap = canvas.getDevice()->accessBitmap(false); -#endif // defined(OS_WIN) + delete canvas; plugin_window_moves_.clear(); @@ -498,21 +479,12 @@ void RenderWidget::DoDeferredScroll() { // In case the scroll offset exceeds the width/height of the scroll rect damaged_rect = scroll_rect_.Intersect(damaged_rect); -#if defined(OS_WIN) - current_scroll_buf_ = - RenderProcess::AllocSharedMemory(GetPaintBufSize(damaged_rect)); - if (!current_scroll_buf_) { + skia::PlatformCanvas* canvas = + RenderProcess::GetDrawingCanvas(¤t_scroll_buf_, damaged_rect); + if (!canvas) { NOTREACHED(); return; } - skia::PlatformCanvasWin canvas(damaged_rect.width(), damaged_rect.height(), - true, current_scroll_buf_->handle()); -#elif defined(OS_POSIX) - // Currently, on POSIX, we are serialising the bitmap data over the IPC - // channel. - skia::PlatformCanvas canvas(damaged_rect.width(), damaged_rect.height(), - true); -#endif // defined(OS_POSIX) // Set these parameters before calling Paint, since that could result in // further invalidates (uncommon). @@ -523,22 +495,16 @@ void RenderWidget::DoDeferredScroll() { params.clip_rect = scroll_rect_; params.view_size = size_; params.plugin_window_moves = plugin_window_moves_; - -#if defined(OS_WIN) - // Windows passes a HANDLE to the shared memory over IPC - params.bitmap = current_scroll_buf_->handle(); -#elif defined(OS_POSIX) - // POSIX currently passes the data itself. - params.bitmap = canvas.getDevice()->accessBitmap(false); -#endif // defined(OS_WIN) + params.bitmap = current_scroll_buf_->id(); plugin_window_moves_.clear(); // Mark the scroll operation as no longer pending. scroll_rect_ = gfx::Rect(); - PaintRect(damaged_rect, &canvas); + PaintRect(damaged_rect, canvas); Send(new ViewHostMsg_ScrollRect(routing_id_, params)); + delete canvas; UpdateIME(); } diff --git a/chrome/renderer/render_widget.h b/chrome/renderer/render_widget.h index 4153ce0..b0c4b11 100644 --- a/chrome/renderer/render_widget.h +++ b/chrome/renderer/render_widget.h @@ -14,6 +14,7 @@ #include "base/ref_counted.h" #include "base/shared_memory.h" #include "chrome/common/ipc_channel.h" +#include "chrome/renderer/render_process.h" #include "skia/ext/platform_canvas.h" #include "webkit/glue/webwidget_delegate.h" @@ -86,10 +87,6 @@ class RenderWidget : public IPC::Channel::Listener, // Close the underlying WebWidget. void Close(); - // Get the size of the paint buffer for the given rectangle, rounding up to - // the allocation granularity of the system. - static size_t GetPaintBufSize(const gfx::Rect& rect); - protected: // Friend RefCounted so that the dtor can be non-public. Using this class // without ref-counting is an error. @@ -192,10 +189,10 @@ class RenderWidget : public IPC::Channel::Listener, // The size of the RenderWidget. gfx::Size size_; - // Shared memory handles that are currently in use to transfer an image to - // the browser. - base::SharedMemory* current_paint_buf_; - base::SharedMemory* current_scroll_buf_; + // Transport DIBs that are currently in use to transfer an image to the + // browser. + TransportDIB* current_paint_buf_; + TransportDIB* current_scroll_buf_; // The smallest bounding rectangle that needs to be re-painted. This is non- // empty if a paint event is pending. diff --git a/chrome/renderer/renderer_glue.cc b/chrome/renderer/renderer_glue.cc index a8a9486..a65f326 100644 --- a/chrome/renderer/renderer_glue.cc +++ b/chrome/renderer/renderer_glue.cc @@ -95,7 +95,9 @@ void ScopedClipboardWriterGlue::WriteBitmap(const SkBitmap& bitmap) { gfx::Size size(bitmap.width(), bitmap.height()); // Allocate a shared memory buffer to hold the bitmap bits - shared_buf_ = RenderProcess::AllocSharedMemory(buf_size); + shared_buf_ = new base::SharedMemory; + shared_buf_->Create(L"", false /* read write */, true /* open existing */, + buf_size); if (!shared_buf_ || !shared_buf_->Map(buf_size)) { NOTREACHED(); return; @@ -134,7 +136,7 @@ ScopedClipboardWriterGlue::~ScopedClipboardWriterGlue() { if (shared_buf_) { g_render_thread->Send( new ViewHostMsg_ClipboardWriteObjectsSync(objects_)); - RenderProcess::FreeSharedMemory(shared_buf_); + delete shared_buf_; return; } #endif diff --git a/skia/ext/bitmap_platform_device_linux.h b/skia/ext/bitmap_platform_device_linux.h index cfc72e2..d26c50d 100644 --- a/skia/ext/bitmap_platform_device_linux.h +++ b/skia/ext/bitmap_platform_device_linux.h @@ -59,9 +59,9 @@ class BitmapPlatformDeviceLinux : public PlatformDeviceLinux { class BitmapPlatformDeviceLinuxData; public: - /// Static constructor. I don't understand this, it's just a copy of the mac static BitmapPlatformDeviceLinux* Create(int width, int height, bool is_opaque); + // This doesn't take ownership of |data| static BitmapPlatformDeviceLinux* Create(int width, int height, bool is_opaque, uint8_t* data); static BitmapPlatformDeviceLinux* Create(int width, int height, diff --git a/skia/ext/bitmap_platform_device_mac.cc b/skia/ext/bitmap_platform_device_mac.cc index 9b4beb0..a0f0bf1 100755 --- a/skia/ext/bitmap_platform_device_mac.cc +++ b/skia/ext/bitmap_platform_device_mac.cc @@ -168,24 +168,26 @@ BitmapPlatformDeviceMac* BitmapPlatformDeviceMac::Create(CGContextRef context, #endif } - CGColorSpaceRef color_space = - CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); - // allocate a bitmap context with 4 components per pixel (BGRA). Apple - // recommends these flags for improved CG performance. - CGContextRef bitmap_context = - CGBitmapContextCreate(data, width, height, 8, width*4, - color_space, - kCGImageAlphaPremultipliedFirst | - kCGBitmapByteOrder32Host); - - // Change the coordinate system to match WebCore's - CGContextTranslateCTM(bitmap_context, 0, height); - CGContextScaleCTM(bitmap_context, 1.0, -1.0); - CGColorSpaceRelease(color_space); + if (!context) { + CGColorSpaceRef color_space = + CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); + // allocate a bitmap context with 4 components per pixel (BGRA). Apple + // recommends these flags for improved CG performance. + context = + CGBitmapContextCreate(data, width, height, 8, width*4, + color_space, + kCGImageAlphaPremultipliedFirst | + kCGBitmapByteOrder32Host); + + // Change the coordinate system to match WebCore's + CGContextTranslateCTM(context, 0, height); + CGContextScaleCTM(context, 1.0, -1.0); + CGColorSpaceRelease(color_space); + } // The device object will take ownership of the graphics context. return new BitmapPlatformDeviceMac( - new BitmapPlatformDeviceMacData(bitmap_context), bitmap); + new BitmapPlatformDeviceMacData(context), bitmap); } // The device will own the bitmap, which corresponds to also owning the pixel diff --git a/skia/ext/platform_canvas_linux.cc b/skia/ext/platform_canvas_linux.cc index d436fd7..422693c 100644 --- a/skia/ext/platform_canvas_linux.cc +++ b/skia/ext/platform_canvas_linux.cc @@ -21,6 +21,13 @@ PlatformCanvasLinux::PlatformCanvasLinux(int width, int height, bool is_opaque) SK_CRASH(); } +PlatformCanvasLinux::PlatformCanvasLinux(int width, int height, bool is_opaque, + uint8_t* data) + : SkCanvas() { + if (!initialize(width, height, is_opaque, data)) + SK_CRASH(); +} + PlatformCanvasLinux::~PlatformCanvasLinux() { } @@ -34,6 +41,18 @@ bool PlatformCanvasLinux::initialize(int width, int height, bool is_opaque) { return true; } +bool PlatformCanvasLinux::initialize(int width, int height, bool is_opaque, + uint8_t* data) { + SkDevice* device = + BitmapPlatformDeviceLinux::Create(width, height, is_opaque, data); + if (!device) + return false; + + setDevice(device); + device->unref(); // was created with refcount 1, and setDevice also refs + return true; +} + PlatformDeviceLinux& PlatformCanvasLinux::getTopPlatformDevice() const { // All of our devices should be our special PlatformDevice. SkCanvas::LayerIter iter(const_cast<PlatformCanvasLinux*>(this), false); diff --git a/skia/ext/platform_canvas_linux.h b/skia/ext/platform_canvas_linux.h index 5782284..1cf87a8 100644 --- a/skia/ext/platform_canvas_linux.h +++ b/skia/ext/platform_canvas_linux.h @@ -30,6 +30,7 @@ class PlatformCanvasLinux : public SkCanvas { // For two-part init, call if you use the no-argument constructor above bool initialize(int width, int height, bool is_opaque); + bool initialize(int width, int height, bool is_opaque, uint8_t* data); // Returns the platform device pointer of the topmost rect with a non-empty // clip. Both the windows and mac versions have an equivalent of this method; diff --git a/skia/ext/platform_canvas_mac.cc b/skia/ext/platform_canvas_mac.cc index 380a78b..9ada02d 100755 --- a/skia/ext/platform_canvas_mac.cc +++ b/skia/ext/platform_canvas_mac.cc @@ -25,6 +25,14 @@ PlatformCanvasMac::PlatformCanvasMac(int width, initialize(width, height, is_opaque); } +PlatformCanvasMac::PlatformCanvasMac(int width, + int height, + bool is_opaque, + uint8_t* data) + : SkCanvas() { + initialize(width, height, is_opaque, data); +} + PlatformCanvasMac::~PlatformCanvasMac() { } @@ -40,6 +48,33 @@ bool PlatformCanvasMac::initialize(int width, return true; } +bool PlatformCanvasMac::initialize(int width, + int height, + bool is_opaque, + uint8_t* data) { + CGContextRef context = NULL; + CGColorSpaceRef colorSpace; + + colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); + context = CGBitmapContextCreate( + data, width, height, 8 /* bits per plane */, 4 * width /* stride */, + colorSpace, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host); + CGColorSpaceRelease(colorSpace); + if (!context) + return false; + // Change the coordinate system to match WebCore's + CGContextTranslateCTM(context, 0, height); + CGContextScaleCTM(context, 1.0, -1.0); + + SkDevice* device = createPlatformDevice(width, height, is_opaque, context); + if (!device) + return false; + + setDevice(device); + device->unref(); // was created with refcount 1, and setDevice also refs + return true; +} + CGContextRef PlatformCanvasMac::beginPlatformPaint() { return getTopPlatformDevice().GetBitmapContext(); } diff --git a/skia/ext/platform_canvas_mac.h b/skia/ext/platform_canvas_mac.h index da745f8..935bdc6 100755 --- a/skia/ext/platform_canvas_mac.h +++ b/skia/ext/platform_canvas_mac.h @@ -25,10 +25,13 @@ class PlatformCanvasMac : public SkCanvas { PlatformCanvasMac(int width, int height, bool is_opaque); PlatformCanvasMac(int width, int height, bool is_opaque, CGContextRef context); + PlatformCanvasMac(int width, int height, bool is_opaque, + uint8_t* context); virtual ~PlatformCanvasMac(); // For two-part init, call if you use the no-argument constructor above bool initialize(int width, int height, bool is_opaque); + bool initialize(int width, int height, bool is_opaque, uint8_t* data); // These calls should surround calls to platform drawing routines. The CG // context returned by beginPlatformPaint is the one that can be used to |