diff options
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 |