diff options
author | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-06 02:53:28 +0000 |
---|---|---|
committer | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-06 02:53:28 +0000 |
commit | 00c39612a3ec84f8d1f26975f3936bffc183591a (patch) | |
tree | 92474ab446150f025becaefd3868e02f72bea1fc | |
parent | 23dd7db09af6da5643e10d839b23d484a5b41eab (diff) | |
download | chromium_src-00c39612a3ec84f8d1f26975f3936bffc183591a.zip chromium_src-00c39612a3ec84f8d1f26975f3936bffc183591a.tar.gz chromium_src-00c39612a3ec84f8d1f26975f3936bffc183591a.tar.bz2 |
Make the pepper 2D flush callback actually function as advertised. It will now
get called asynchronously when the bits are actually copied to the screen,
rather than synchronously from inside the paint function. This makes it useful
for plugins to use the callback for rate limiting. This also adds a lot of
infrastructure for running tests on pepper devices, and includes a unit test
for the new flush behavior. I made the existing RenderProcess object an
abstract interface and made the existing MockProcess (renamed to be more clear)
implement that. This avoids a static cast that would actually crash during a
unit test because some code was hardcoded to expect a RenderProcess object.
This fixes base's IDMap iterator which has apparently never been used for an
IDMap with ownership semantics.
TEST=Unit test included
BUG=none
Original review URL: http://codereview.chromium.org/661124
Review URL: http://codereview.chromium.org/664001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@40813 0039d316-1c4b-4281-b951-d872f2087c98
29 files changed, 722 insertions, 238 deletions
diff --git a/base/id_map.h b/base/id_map.h index acfba42..00ef16a 100644 --- a/base/id_map.h +++ b/base/id_map.h @@ -101,7 +101,7 @@ class IDMap { template<class ReturnType> class Iterator { public: - Iterator(IDMap<T>* map) + Iterator(IDMap<T, OS>* map) : map_(map), iter_(map_->data_.begin()) { ++map_->iteration_depth_; @@ -139,7 +139,7 @@ class IDMap { } } - IDMap<T>* map_; + IDMap<T, OS>* map_; typename HashTable::const_iterator iter_; }; diff --git a/chrome/browser/renderer_host/browser_render_process_host.cc b/chrome/browser/renderer_host/browser_render_process_host.cc index 4ad1a5c..3d70253 100644 --- a/chrome/browser/renderer_host/browser_render_process_host.cc +++ b/chrome/browser/renderer_host/browser_render_process_host.cc @@ -51,7 +51,7 @@ #include "chrome/common/process_watcher.h" #include "chrome/common/render_messages.h" #include "chrome/common/result_codes.h" -#include "chrome/renderer/render_process.h" +#include "chrome/renderer/render_process_impl.h" #include "chrome/renderer/render_thread.h" #include "grit/generated_resources.h" #include "ipc/ipc_logging.h" @@ -89,7 +89,7 @@ class RendererMainThread : public base::Thread { CoInitialize(NULL); #endif - render_process_ = new RenderProcess(); + render_process_ = new RenderProcessImpl(); render_process_->set_main_thread(new RenderThread(channel_id_)); // It's a little lame to manually set this flag. But the single process // RendererThread will receive the WM_QUIT. We don't need to assert on diff --git a/chrome/chrome_renderer.gypi b/chrome/chrome_renderer.gypi index 2f27359..6802bf1 100755 --- a/chrome/chrome_renderer.gypi +++ b/chrome/chrome_renderer.gypi @@ -111,8 +111,9 @@ 'renderer/print_web_view_helper_linux.cc', 'renderer/print_web_view_helper_mac.mm', 'renderer/print_web_view_helper_win.cc', - 'renderer/render_process.cc', 'renderer/render_process.h', + 'renderer/render_process_impl.cc', + 'renderer/render_process_impl.h', 'renderer/render_thread.cc', 'renderer/render_thread.h', 'renderer/render_view.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 82a8529..3b7da95 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -87,6 +87,7 @@ 'renderer/mock_keyboard_driver_win.h', 'renderer/mock_printer.cc', 'renderer/mock_printer.h', + 'renderer/mock_render_process.cc', 'renderer/mock_render_process.h', 'renderer/mock_render_thread.cc', 'renderer/mock_render_thread.h', @@ -910,6 +911,7 @@ 'renderer/net/render_dns_master_unittest.cc', 'renderer/net/render_dns_queue_unittest.cc', 'renderer/paint_aggregator_unittest.cc', + 'renderer/pepper_devices_unittest.cc', 'renderer/render_process_unittest.cc', 'renderer/render_thread_unittest.cc', 'renderer/render_view_unittest.cc', diff --git a/chrome/common/transport_dib.h b/chrome/common/transport_dib.h index e36e8bf..4f8051e 100644 --- a/chrome/common/transport_dib.h +++ b/chrome/common/transport_dib.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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. @@ -90,10 +90,15 @@ class TransportDIB { static Handle DefaultHandleValue() { return -1; } #endif - // Create a new TransportDIB - // size: the minimum size, in bytes - // epoch: Windows only: a global counter. See comment above. - // returns: NULL on failure + // Create a new TransportDIB, returning NULL on failure. + // + // The size is the minimum size in bytes of the memory backing the transport + // DIB (we may actually allocate more than that to give us better reuse when + // cached). + // + // The sequence number is used to uniquely identify the transport DIB. It + // should be unique for all transport DIBs ever created in the same + // renderer. static TransportDIB* Create(size_t size, uint32 sequence_num); // Map the referenced transport DIB. Returns NULL on failure. diff --git a/chrome/renderer/mock_render_process.cc b/chrome/renderer/mock_render_process.cc new file mode 100644 index 0000000..a71fcab --- /dev/null +++ b/chrome/renderer/mock_render_process.cc @@ -0,0 +1,44 @@ +// Copyright (c) 2010 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/renderer/mock_render_process.h" + +#include "base/gfx/rect.h" +#include "chrome/common/transport_dib.h" + +MockRenderProcess::MockRenderProcess() + : transport_dib_next_sequence_number_(0) { +} + +MockRenderProcess::~MockRenderProcess() { +} + +skia::PlatformCanvas* MockRenderProcess::GetDrawingCanvas( + TransportDIB** memory, + const gfx::Rect& rect) { + size_t stride = skia::PlatformCanvas::StrideForWidth(rect.width()); + size_t size = stride * rect.height(); + + // Unlike RenderProcessImpl, when we're a test, we can just create transport + // DIBs in the current process, since there is no sandbox protecting us (and + // no browser process to ask for one in any case). + *memory = TransportDIB::Create(size, transport_dib_next_sequence_number_++); + if (!*memory) + return NULL; + return (*memory)->GetPlatformCanvas(rect.width(), rect.height()); +} + +void MockRenderProcess::ReleaseTransportDIB(TransportDIB* memory) { + delete memory; +} + +bool MockRenderProcess::UseInProcessPlugins() const { + return true; +} + +bool MockRenderProcess::HasInitializedMediaLibrary() const { + return false; +} + + diff --git a/chrome/renderer/mock_render_process.h b/chrome/renderer/mock_render_process.h index 49bc02c..fd286a1 100644 --- a/chrome/renderer/mock_render_process.h +++ b/chrome/renderer/mock_render_process.h @@ -1,20 +1,30 @@ -// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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_RENDERER_MOCK_RENDER_PROCESS_H_ #define CHROME_RENDERER_MOCK_RENDER_PROCESS_H_ -#include "chrome/common/child_process.h" +#include "chrome/renderer/render_process.h" -class ChildThread; - -// This class is a trivial mock of the child process singleton. It is necessary -// so we don't trip DCHECKs in ChildProcess::ReleaseProcess() when destroying -// a render widget instance. -class MockProcess : public ChildProcess { +// This class is a mock of the child process singleton which we use during +// running of the RenderView unit tests. +class MockRenderProcess : public RenderProcess { public: - explicit MockProcess() : ChildProcess() {} + MockRenderProcess(); + virtual ~MockRenderProcess(); + + // RenderProcess implementation. + virtual skia::PlatformCanvas* GetDrawingCanvas(TransportDIB** memory, + const gfx::Rect& rect); + virtual void ReleaseTransportDIB(TransportDIB* memory); + virtual bool UseInProcessPlugins() const; + virtual bool HasInitializedMediaLibrary() const; + + private: + uint32 transport_dib_next_sequence_number_; + + DISALLOW_COPY_AND_ASSIGN(MockRenderProcess); }; #endif // CHROME_RENDERER_MOCK_RENDER_PROCESS_H_ diff --git a/chrome/renderer/pepper_devices.cc b/chrome/renderer/pepper_devices.cc index cfd8076..c47b846 100644 --- a/chrome/renderer/pepper_devices.cc +++ b/chrome/renderer/pepper_devices.cc @@ -3,17 +3,27 @@ // found in the LICENSE file. #include "chrome/renderer/pepper_devices.h" +#include "chrome/renderer/webplugin_delegate_pepper.h" #include "skia/ext/platform_canvas.h" +#include "webkit/glue/plugins/plugin_instance.h" +#include "webkit/glue/webplugin.h" namespace { - const uint32 kBytesPerPixel = 4; // Only 8888 RGBA for now. + +const uint32 kBytesPerPixel = 4; // Only 8888 RGBA for now. + } // namespace int Graphics2DDeviceContext::next_buffer_id_ = 0; +Graphics2DDeviceContext::Graphics2DDeviceContext( + WebPluginDelegatePepper* plugin_delegate) + : plugin_delegate_(plugin_delegate) { +} + NPError Graphics2DDeviceContext::Initialize( - gfx::Rect window_rect, const NPDeviceContext2DConfig* config, - NPDeviceContext2D* context) { + gfx::Rect window_rect, const NPDeviceContext2DConfig* config, + NPDeviceContext2D* context) { int width = window_rect.width(); int height = window_rect.height(); uint32 buffer_size = width * height * kBytesPerPixel; @@ -75,22 +85,47 @@ NPError Graphics2DDeviceContext::Flush(SkBitmap* committed_bitmap, paint.setXfermodeMode(SkXfermode::kSrc_Mode); committed_canvas.drawBitmapRect( canvas_->getTopPlatformDevice().accessBitmap(false), - &src_rect, dest_rect); + &src_rect, dest_rect, &paint); committed_bitmap->setIsOpaque(false); - // Invoke the callback to inform the caller the work was done. - // TODO(brettw) this is not how we want this to work, this should - // happen when the frame is painted so the plugin knows when it can draw - // the next frame. - // - // This should also be called in the failure cases as well. - if (callback != NULL) - (*callback)(id, context, NPERR_NO_ERROR, user_data); + // Cause the updated part of the screen to be repainted. This will happen + // asynchronously. + // TODO(brettw) is this the coorect coordinate system? + gfx::Rect dest_gfx_rect(context->dirty.left, context->dirty.top, + context->dirty.right - context->dirty.left, + context->dirty.bottom - context->dirty.top); + + plugin_delegate_->instance()->webplugin()->InvalidateRect(dest_gfx_rect); + + // Save the callback to execute later. See |unpainted_flush_callbacks_| in + // the header file. + if (callback) { + unpainted_flush_callbacks_.push_back( + FlushCallbackData(callback, id, context, user_data)); + } return NPERR_NO_ERROR; } +void Graphics2DDeviceContext::RenderViewInitiatedPaint() { + // Move all "unpainted" callbacks to the painted state. See + // |unpainted_flush_callbacks_| in the header for more. + std::copy(unpainted_flush_callbacks_.begin(), + unpainted_flush_callbacks_.end(), + std::back_inserter(painted_flush_callbacks_)); + unpainted_flush_callbacks_.clear(); +} + +void Graphics2DDeviceContext::RenderViewFlushedPaint() { + // Notify all "painted" callbacks. See |unpainted_flush_callbacks_| in the + // header for more. + for (size_t i = 0; i < painted_flush_callbacks_.size(); i++) { + const FlushCallbackData& data = painted_flush_callbacks_[i]; + data.function(data.npp, data.context, NPERR_NO_ERROR, data.user_data); + } + painted_flush_callbacks_.clear(); +} AudioDeviceContext::~AudioDeviceContext() { if (stream_id_) { diff --git a/chrome/renderer/pepper_devices.h b/chrome/renderer/pepper_devices.h index ea23d5a..3c9f98c 100644 --- a/chrome/renderer/pepper_devices.h +++ b/chrome/renderer/pepper_devices.h @@ -5,8 +5,8 @@ #ifndef CHROME_RENDERER_PEPPER_DEVICES_H_ #define CHROME_RENDERER_PEPPER_DEVICES_H_ -#include <stdint.h> - +#include "base/basictypes.h" +#include "base/gfx/rect.h" #include "base/scoped_ptr.h" #include "base/shared_memory.h" #include "base/simple_thread.h" @@ -18,12 +18,16 @@ #include "third_party/npapi/bindings/npapi_extensions.h" #include "third_party/skia/include/core/SkBitmap.h" +class WebPluginDelegatePepper; + // Lists all contexts currently open for painting. These are ones requested by // the plugin but not destroyed by it yet. The source pointer is the raw // pixels. We use this to look up the corresponding transport DIB when the // plugin tells us to flush or destroy it. class Graphics2DDeviceContext { public: + explicit Graphics2DDeviceContext(WebPluginDelegatePepper* plugin_delegate); + NPError Initialize(gfx::Rect window_rect, const NPDeviceContext2DConfig* config, NPDeviceContext2D* context); @@ -32,15 +36,57 @@ class Graphics2DDeviceContext { NPDeviceFlushContextCallbackPtr callback, NPP id, void* user_data); + // Notifications that the render view has rendered the page and that it has + // been flushed to the screen. + void RenderViewInitiatedPaint(); + void RenderViewFlushedPaint(); + TransportDIB* transport_dib() { return transport_dib_.get(); } private: + struct FlushCallbackData { + FlushCallbackData(NPDeviceFlushContextCallbackPtr f, + NPP n, + NPDeviceContext2D* c, + NPUserData* u) + : function(f), + npp(n), + context(c), + user_data(u) { + } + + NPDeviceFlushContextCallbackPtr function; + NPP npp; + NPDeviceContext2D* context; + NPUserData* user_data; + }; + typedef std::vector<FlushCallbackData> FlushCallbackVector; + + WebPluginDelegatePepper* plugin_delegate_; + static int32 next_buffer_id_; scoped_ptr<TransportDIB> transport_dib_; // The canvas associated with the transport DIB, containing the mapped // memory of the image. scoped_ptr<skia::PlatformCanvas> canvas_; + + // The plugin may be constantly giving us paint messages. "Unpainted" ones + // are paint requests which have never been painted. These could have been + // done while the RenderView was already waiting for an ACK from a previous + // paint, so won't generate a new one yet. + // + // "Painted" ones are those paints that have been painted by RenderView, but + // for which the ACK from the browser has not yet been received. + // + // When we get updates from a plugin with a callback, it is first added to + // the unpainted callbacks. When the renderer has initiated a paint, we'll + // move it to the painted callbacks list. When the renderer receives a flush, + // we'll execute the callback and remove it from the list. + FlushCallbackVector unpainted_flush_callbacks_; + FlushCallbackVector painted_flush_callbacks_; + + DISALLOW_COPY_AND_ASSIGN(Graphics2DDeviceContext); }; @@ -60,7 +106,7 @@ class AudioDeviceContext : public AudioMessageFilter::Delegate, NPDeviceContextAudio* context); base::SharedMemory* shared_memory() { return shared_memory_.get(); } - uint32_t shared_memory_size() { return shared_memory_size_; } + uint32 shared_memory_size() { return shared_memory_size_; } base::SyncSocket* socket() { return socket_.get(); } private: diff --git a/chrome/renderer/pepper_devices_unittest.cc b/chrome/renderer/pepper_devices_unittest.cc new file mode 100644 index 0000000..e71d12b --- /dev/null +++ b/chrome/renderer/pepper_devices_unittest.cc @@ -0,0 +1,248 @@ +// Copyright (c) 2010 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 <map> +#include <vector> + +#include "base/string_util.h" +#include "build/build_config.h" +#include "chrome/common/render_messages.h" +#include "chrome/renderer/pepper_devices.h" +#include "chrome/renderer/webplugin_delegate_pepper.h" +#include "chrome/test/render_view_test.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/npapi/bindings/npapi.h" +#include "third_party/npapi/bindings/npruntime.h" +#include "third_party/WebKit/WebKit/chromium/public/WebPlugin.h" +#include "third_party/WebKit/WebKit/chromium/public/WebPluginParams.h" +#include "third_party/WebKit/WebKit/chromium/public/WebRect.h" +#include "webkit/glue/plugins/plugin_instance.h" +#include "webkit/glue/plugins/plugin_list.h" +#include "webkit/glue/webplugin_impl.h" + +class PepperDeviceTest; + +namespace { + +const char kTestPluginMimeType[] = "chrome-test/pepper-device-test"; + +// This maps the NPP instances to the test object so our C callbacks can easily +// get back to the object. There will normally be only one item in this map. +static std::map<NPP, PepperDeviceTest*> active_tests; + +NPError NPP_New(NPMIMEType plugin_type, NPP instance, + uint16 mode, int16 argc, char* argn[], + char* argv[], NPSavedData* saved) { + // Watch out: active_tests won't contain the NPP pointer until after this + // call is complete, so don't use it. + return NPERR_NO_ERROR; +} + +NPError NPP_Destroy(NPP instance, NPSavedData** saved) { + if (!instance) + return NPERR_INVALID_INSTANCE_ERROR; + return NPERR_NO_ERROR; +} + +NPError NPP_SetWindow(NPP instance, NPWindow* window) { + return NPERR_NO_ERROR; +} + +int16 NPP_HandleEvent(NPP instance, void* event) { + return 0; +} + +NPError NPP_GetValue(NPP instance, NPPVariable variable, void* value) { + if (!instance) + return NPERR_INVALID_INSTANCE_ERROR; + switch (variable) { + case NPPVpluginNeedsXEmbed: + *static_cast<NPBool*>(value) = 1; + return NPERR_NO_ERROR; + default: + return NPERR_INVALID_PARAM; + } +} + +NPError NPP_SetValue(NPP instance, NPNVariable variable, void* value) { + return NPERR_NO_ERROR; +} + +NPError API_CALL NP_GetEntryPoints(NPPluginFuncs* plugin_funcs) { + plugin_funcs->newp = NPP_New; + plugin_funcs->destroy = NPP_Destroy; + plugin_funcs->setwindow = NPP_SetWindow; + plugin_funcs->event = NPP_HandleEvent; + plugin_funcs->getvalue = NPP_GetValue; + plugin_funcs->setvalue = NPP_SetValue; + return NPERR_NO_ERROR; +} + +#if defined(OS_MACOSX) || defined(OS_WIN) +NPError API_CALL NP_Initialize(NPNetscapeFuncs* browser_funcs) { + return NPERR_NO_ERROR; +} +#else +NPError API_CALL NP_Initialize(NPNetscapeFuncs* browser_funcs, + NPPluginFuncs* plugin_funcs) { + NP_GetEntryPoints(plugin_funcs); + return NPERR_NO_ERROR; +} +#endif + +NPError API_CALL NP_Shutdown() { + return NPERR_NO_ERROR; +} + +} // namespace + +// PepperDeviceTest ------------------------------------------------------------ + +class PepperDeviceTest : public RenderViewTest { + public: + PepperDeviceTest(); + ~PepperDeviceTest(); + + const FilePath& plugin_path() const { return version_info_.path; } + + WebPluginDelegatePepper* pepper_plugin() const { return pepper_plugin_; } + + NPP npp() const { return pepper_plugin_->instance()->npp(); } + + protected: + // Logs that the given flush command was called in flush_calls. + static void FlushCalled(NPP instance, + NPDeviceContext* context, + NPError err, + NPUserData* user_data); + + // A log of flush commands we can use to check the async callbacks. + struct FlushData { + NPP instance; + NPDeviceContext* context; + NPError err; + NPUserData* user_data; + }; + std::vector<FlushData> flush_calls_; + + private: + // testing::Test implementation. + virtual void SetUp(); + virtual void TearDown(); + + NPAPI::PluginVersionInfo version_info_; + + scoped_ptr<webkit_glue::WebPluginImpl> plugin_; + WebPluginDelegatePepper* pepper_plugin_; // FIXME(brettw): check lifetime. +}; + +PepperDeviceTest::PepperDeviceTest() { + version_info_.path = FilePath(FILE_PATH_LITERAL("pepper-device-tester")); + version_info_.product_name = ASCIIToWide("Pepper device test plugin"); + version_info_.file_description = ASCIIToWide("Pepper device test plugin"); + version_info_.file_version = ASCIIToWide("1"); + version_info_.mime_types = ASCIIToWide(kTestPluginMimeType); + NPAPI::PluginEntryPoints entry_points = { +#if !defined(OS_POSIX) || defined(OS_MACOSX) + NP_GetEntryPoints, +#endif + NP_Initialize, + NP_Shutdown + }; + version_info_.entry_points = entry_points; +} + +PepperDeviceTest::~PepperDeviceTest() { +} + +void PepperDeviceTest::SetUp() { + RenderViewTest::SetUp(); + + NPAPI::PluginList::Singleton()->RegisterInternalPlugin(version_info_); + + // Create the WebKit plugin with no delegates (this seems to work + // sufficiently for the test). + WebKit::WebPluginParams params; + plugin_.reset(new webkit_glue::WebPluginImpl( + NULL, params, base::WeakPtr<webkit_glue::WebPluginPageDelegate>())); + + // Create a pepper plugin for the RenderView. + pepper_plugin_ = WebPluginDelegatePepper::Create( + plugin_path(), kTestPluginMimeType, view_->AsWeakPtr()); + ASSERT_TRUE(pepper_plugin_); + ASSERT_TRUE(pepper_plugin_->Initialize(GURL(), std::vector<std::string>(), + std::vector<std::string>(), + plugin_.get(), false)); + + // Normally the RenderView creates the pepper plugin and registers it with + // its internal list. Since we're creating it manually, we have to reach in + // and register it to prevent tear-down from asserting. + view_->current_pepper_plugins_.insert(pepper_plugin_); + + active_tests[npp()] = this; + + // Need to specify a window size or graphics calls will fail on the 0x0 + // bitmap. + gfx::Rect rect(0, 0, 100, 100); + view_->OnResize(rect.size(), gfx::Rect()); + pepper_plugin_->UpdateGeometry(rect, rect); +} + +void PepperDeviceTest::TearDown() { + active_tests.erase(active_tests.find(npp())); + + plugin_.reset(); + if (pepper_plugin_) + pepper_plugin_->PluginDestroyed(); + + NPAPI::PluginList::Singleton()->UnregisterInternalPlugin(version_info_.path); + + RenderViewTest::TearDown(); +} + +// static +void PepperDeviceTest::FlushCalled(NPP instance, + NPDeviceContext* context, + NPError err, + NPUserData* user_data) { + if (active_tests.find(instance) == active_tests.end()) + return; + PepperDeviceTest* that = active_tests[instance]; + + FlushData flush_data; + flush_data.instance = instance; + flush_data.context = context; + flush_data.err = err; + flush_data.user_data = user_data; + that->flush_calls_.push_back(flush_data); +} + +// ----------------------------------------------------------------------------- + +TEST_F(PepperDeviceTest, Flush) { + // Create a 2D device. + NPDeviceContext2DConfig config; + NPDeviceContext2D context; + EXPECT_EQ(NPERR_NO_ERROR, + pepper_plugin()->Device2DInitializeContext(&config, &context)); + + // Flush the bitmap. Here we fake the invalidate call to the RenderView since + // there isn't an actual visible web page that would otherwise get painted. + // The callback should not get called synchronously. + pepper_plugin()->Device2DFlushContext(npp(), &context, &FlushCalled, NULL); + view_->didInvalidateRect(WebKit::WebRect(0, 0, 100, 100)); + EXPECT_TRUE(flush_calls_.empty()); + + // Run the message loop which should process the pending paints, there should + // still be no callbacks since the stuff hasn't been copied to the screen, + // but there should be a paint message sent to the browser. + MessageLoop::current()->RunAllPending(); + EXPECT_TRUE(flush_calls_.empty()); + EXPECT_TRUE(render_thread_.sink().GetFirstMessageMatching( + ViewHostMsg_UpdateRect::ID)); + + // Send a paint ACK, this should trigger the callback. + view_->OnMessageReceived(ViewMsg_UpdateRect_ACK(view_->routing_id())); + EXPECT_EQ(1u, flush_calls_.size()); +} diff --git a/chrome/renderer/render_process.h b/chrome/renderer/render_process.h index 4c682dc..ed002cf 100644 --- a/chrome/renderer/render_process.h +++ b/chrome/renderer/render_process.h @@ -1,29 +1,34 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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_RENDERER_RENDER_PROCESS_H__ -#define CHROME_RENDERER_RENDER_PROCESS_H__ +#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 "native_client/src/shared/imc/nacl_imc.h" #include "skia/ext/platform_canvas.h" +class TransportDIB; + namespace gfx { class Rect; } -class TransportDIB; +namespace skia { +class PlatformCanvas; +} -// Represents the renderer end of the browser<->renderer connection. The -// opposite end is the RenderProcessHost. This is a singleton object for -// each renderer. +// A abstract interface representing the renderer end of the browser<->renderer +// connection. The opposite end is the RenderProcessHost. This is a singleton +// object for each renderer. +// +// RenderProcessImpl implements this interface for the regular browser. +// MockRenderProcess implements this interface for certain tests, especially +// ones derived from RenderViewTest. class RenderProcess : public ChildProcess { public: - RenderProcess(); - ~RenderProcess(); + RenderProcess() {} + virtual ~RenderProcess() {} // Get a canvas suitable for drawing and transporting to the browser // memory: (output) the transport DIB memory @@ -32,72 +37,27 @@ class RenderProcess : public ChildProcess { // // When no longer needed, you should pass the TransportDIB to // ReleaseTransportDIB so that it can be recycled. - skia::PlatformCanvas* GetDrawingCanvas( - TransportDIB** memory, const gfx::Rect& rect); + virtual skia::PlatformCanvas* GetDrawingCanvas(TransportDIB** memory, + const gfx::Rect& rect) = 0; // Frees shared memory allocated by AllocSharedMemory. You should only use // this function to free the SharedMemory object. - void ReleaseTransportDIB(TransportDIB* memory); + virtual void ReleaseTransportDIB(TransportDIB* memory) = 0; - // Returns true if plugins should be loaded in-process. - bool in_process_plugins() const { return in_process_plugins_; } + // Returns true if plugisn should be loaded in-process. + virtual bool UseInProcessPlugins() const = 0; - bool initialized_media_library() const { return initialized_media_library_; } + virtual bool HasInitializedMediaLibrary() const = 0; - // Returns a pointer to the RenderProcess singleton instance. + // Returns a pointer to the RenderProcess singleton instance. Assuming that + // we're actually a renderer or a renderer test, this static cast will + // be correct. static RenderProcess* current() { return static_cast<RenderProcess*>(ChildProcess::current()); } - // Just like in_process_plugins(), but called before RenderProcess is created. - static bool InProcessPlugins(); - - // Sends a message to the browser process asking to launch a new NaCl process. - // Called from NaCl plugin code. - static bool LaunchNaClProcess(const char* url, - int imc_fd, - nacl::Handle* imc_handle, - nacl::Handle* nacl_process_handle, - int* nacl_process_id); - private: - // 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 - // true if the SharedMemory object was stored in the cache; otherwise, false - // is returned. - bool PutSharedMemInCache(TransportDIB* memory); - - void ClearTransportDIBCache(); - - // 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*); - - // 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. - TransportDIB* shared_mem_cache_[2]; - - // This DelayTimer cleans up our cache 5 seconds after the last use. - base::DelayTimer<RenderProcess> shared_mem_cache_cleaner_; - - // TransportDIB sequence number - uint32 sequence_number_; - - bool in_process_plugins_; - bool initialized_media_library_; - DISALLOW_COPY_AND_ASSIGN(RenderProcess); }; -#endif // CHROME_RENDERER_RENDER_PROCESS_H__ +#endif // CHROME_RENDERER_RENDER_PROCESS_H_ diff --git a/chrome/renderer/render_process_impl.cc b/chrome/renderer/render_process_impl.cc index 9d621da..a508e77 100644 --- a/chrome/renderer/render_process_impl.cc +++ b/chrome/renderer/render_process_impl.cc @@ -4,14 +4,14 @@ #include "build/build_config.h" -#include "chrome/renderer/render_process_impl.h" - #if defined(OS_WIN) #include <windows.h> #include <objidl.h> #include <mlang.h> #endif +#include "chrome/renderer/render_process_impl.h" + #include "base/basictypes.h" #include "base/command_line.h" #include "base/compiler_specific.h" @@ -31,6 +31,7 @@ #include "ipc/ipc_channel.h" #include "ipc/ipc_message_utils.h" #include "media/base/media.h" +#include "media/base/media_switches.h" #include "native_client/src/trusted/plugin/nacl_entry_points.h" #include "webkit/glue/webkit_glue.h" @@ -67,8 +68,7 @@ bool LaunchNaClProcess(const char* url, //----------------------------------------------------------------------------- RenderProcessImpl::RenderProcessImpl() - : RenderProcess(), - ALLOW_THIS_IN_INITIALIZER_LIST(shared_mem_cache_cleaner_( + : ALLOW_THIS_IN_INITIALIZER_LIST(shared_mem_cache_cleaner_( base::TimeDelta::FromSeconds(5), this, &RenderProcessImpl::ClearTransportDIBCache)), transport_dib_next_sequence_number_(0) { @@ -87,8 +87,9 @@ RenderProcessImpl::RenderProcessImpl() GetModuleHandle(L"GDI32.DLL"), "GdiInitializeLanguagePack")); DCHECK(gdi_init_lpk); - if (gdi_init_lpk) + if (gdi_init_lpk) { gdi_init_lpk(0); + } } #endif @@ -101,7 +102,7 @@ RenderProcessImpl::RenderProcessImpl() const CommandLine& command_line = *CommandLine::ForCurrentProcess(); if (command_line.HasSwitch(switches::kJavaScriptFlags)) { webkit_glue::SetJavaScriptFlags( - command_line.GetSwitchValue(switches::kJavaScriptFlags)); + command_line.GetSwitchValue(switches::kJavaScriptFlags)); } if (command_line.HasSwitch(switches::kEnableWatchdog)) { @@ -117,8 +118,9 @@ RenderProcessImpl::RenderProcessImpl() RegisterInternalNaClPlugin(LaunchNaClProcess); #endif - if (!command_line.HasSwitch(switches::kDisableByteRangeSupport)) + if (!command_line.HasSwitch(switches::kDisableByteRangeSupport)) { webkit_glue::SetMediaCacheEnabled(true); + } #if defined(OS_MACOSX) FilePath bundle_path = mac_util::MainAppBundlePath(); @@ -130,6 +132,12 @@ RenderProcessImpl::RenderProcessImpl() initialized_media_library_ = PathService::Get(base::DIR_MODULE, &module_path) && media::InitializeMediaLibrary(module_path); + + // TODO(hclam): Add more checks here. Currently this is not used. + if (CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableOpenMax)) { + media::InitializeOpenMaxLibrary(module_path); + } #endif } @@ -144,9 +152,59 @@ RenderProcessImpl::~RenderProcessImpl() { ClearTransportDIBCache(); } +bool RenderProcessImpl::InProcessPlugins() { + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); +#if defined(OS_LINUX) + // Plugin processes require a UI message loop, and the Linux message loop + // implementation only allows one UI loop per process. + if (command_line.HasSwitch(switches::kInProcessPlugins)) + NOTIMPLEMENTED() << ": in process plugins not supported on Linux"; + return command_line.HasSwitch(switches::kInProcessPlugins); +#else + return command_line.HasSwitch(switches::kInProcessPlugins) || + command_line.HasSwitch(switches::kSingleProcess); +#endif +} + +// ----------------------------------------------------------------------------- +// Platform specific code for dealing with bitmap transport... + +TransportDIB* RenderProcessImpl::CreateTransportDIB(size_t size) { +#if defined(OS_WIN) || defined(OS_LINUX) + // Windows and Linux create transport DIBs inside the renderer + return TransportDIB::Create(size, transport_dib_next_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. + TransportDIB::Handle handle; + IPC::Message* msg = new ViewHostMsg_AllocTransportDIB(size, &handle); + if (!main_thread()->Send(msg)) + return NULL; + if (handle.fd < 0) + return NULL; + return TransportDIB::Map(handle); +#endif // defined(OS_MACOSX) +} + +void RenderProcessImpl::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()); + main_thread()->Send(msg); +#endif + + delete dib; +} + +// ----------------------------------------------------------------------------- + + skia::PlatformCanvas* RenderProcessImpl::GetDrawingCanvas( - TransportDIB** memory, - const gfx::Rect& rect) { + TransportDIB** memory, const gfx::Rect& rect) { int width = rect.width(); int height = rect.height(); const size_t stride = skia::PlatformCanvas::StrideForWidth(rect.width()); @@ -167,7 +225,7 @@ skia::PlatformCanvas* RenderProcessImpl::GetDrawingCanvas( if (!GetTransportDIBFromCache(memory, size)) { *memory = CreateTransportDIB(size); if (!*memory) - return NULL; + return false; } return (*memory)->GetPlatformCanvas(width, height); @@ -190,23 +248,8 @@ bool RenderProcessImpl::HasInitializedMediaLibrary() const { return initialized_media_library_; } -// static -bool RenderProcessImpl::InProcessPlugins() { - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); -#if defined(OS_LINUX) - // Plugin processes require a UI message loop, and the Linux message loop - // implementation only allows one UI loop per process. - if (command_line.HasSwitch(switches::kInProcessPlugins)) - NOTIMPLEMENTED() << ": in process plugins not supported on Linux"; - return command_line.HasSwitch(switches::kInProcessPlugins); -#else - return command_line.HasSwitch(switches::kInProcessPlugins) || - command_line.HasSwitch(switches::kSingleProcess); -#endif -} - bool RenderProcessImpl::GetTransportDIBFromCache(TransportDIB** mem, - size_t size) { + 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) { if (shared_mem_cache_[i] && @@ -220,24 +263,6 @@ bool RenderProcessImpl::GetTransportDIBFromCache(TransportDIB** mem, return false; } -bool RenderProcessImpl::PutSharedMemInCache(TransportDIB* mem) { - const int slot = FindFreeCacheSlot(mem->size()); - if (slot == -1) - return false; - - shared_mem_cache_[slot] = mem; - return true; -} - -void RenderProcessImpl::ClearTransportDIBCache() { - for (size_t i = 0; i < arraysize(shared_mem_cache_); ++i) { - if (shared_mem_cache_[i]) { - FreeTransportDIB(shared_mem_cache_[i]); - shared_mem_cache_[i] = NULL; - } - } -} - int RenderProcessImpl::FindFreeCacheSlot(size_t size) { // simple algorithm: // - look for an empty slot to store mem, or @@ -266,33 +291,20 @@ int RenderProcessImpl::FindFreeCacheSlot(size_t size) { return smallest_index; } -TransportDIB* RenderProcessImpl::CreateTransportDIB(size_t size) { -#if defined(OS_WIN) || defined(OS_LINUX) - // Windows and Linux create transport DIBs inside the renderer - return TransportDIB::Create(size, transport_dib_next_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. - TransportDIB::Handle handle; - IPC::Message* msg = new ViewHostMsg_AllocTransportDIB(size, &handle); - if (!main_thread()->Send(msg)) - return NULL; - if (handle.fd < 0) - return NULL; - return TransportDIB::Map(handle); -#endif // defined(OS_MACOSX) -} - -void RenderProcessImpl::FreeTransportDIB(TransportDIB* dib) { - if (!dib) - return; +bool RenderProcessImpl::PutSharedMemInCache(TransportDIB* mem) { + const int slot = FindFreeCacheSlot(mem->size()); + if (slot == -1) + return false; -#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()); - main_thread()->Send(msg); -#endif + shared_mem_cache_[slot] = mem; + return true; +} - delete dib; +void RenderProcessImpl::ClearTransportDIBCache() { + for (size_t i = 0; i < arraysize(shared_mem_cache_); ++i) { + if (shared_mem_cache_[i]) { + FreeTransportDIB(shared_mem_cache_[i]); + shared_mem_cache_[i] = NULL; + } + } } diff --git a/chrome/renderer/render_process_unittest.cc b/chrome/renderer/render_process_unittest.cc index 5692d81..f52d694 100644 --- a/chrome/renderer/render_process_unittest.cc +++ b/chrome/renderer/render_process_unittest.cc @@ -1,11 +1,11 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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 "base/gfx/rect.h" #include "base/sys_info.h" #include "base/string_util.h" -#include "chrome/renderer/render_process.h" +#include "chrome/renderer/render_process_impl.h" #include "testing/gtest/include/gtest/gtest.h" namespace { @@ -17,7 +17,7 @@ class RenderProcessTest : public testing::Test { virtual void SetUp() { // Need a MODE_SERVER to make MODE_CLIENTs (like a RenderThread) happy. channel_ = new IPC::Channel(kThreadName, IPC::Channel::MODE_SERVER, NULL); - render_process_.reset(new RenderProcess()); + render_process_.reset(new RenderProcessImpl); } virtual void TearDown() { @@ -34,7 +34,7 @@ class RenderProcessTest : public testing::Test { private: MessageLoopForIO message_loop_; - scoped_ptr<RenderProcess> render_process_; + scoped_ptr<RenderProcessImpl> render_process_; IPC::Channel *channel_; }; diff --git a/chrome/renderer/render_thread.cc b/chrome/renderer/render_thread.cc index 56b1348..05f4c7b 100644 --- a/chrome/renderer/render_thread.cc +++ b/chrome/renderer/render_thread.cc @@ -53,7 +53,7 @@ #include "chrome/renderer/loadtimes_extension_bindings.h" #include "chrome/renderer/net/render_dns_master.h" #include "chrome/renderer/plugin_channel_host.h" -#include "chrome/renderer/render_process.h" +#include "chrome/renderer/render_process_impl.h" #include "chrome/renderer/render_view.h" #include "chrome/renderer/render_view_visitor.h" #include "chrome/renderer/renderer_webkitclient_impl.h" @@ -203,7 +203,7 @@ void RenderThread::Init() { #if defined(OS_WIN) // If you are running plugins in this thread you need COM active but in // the normal case you don't. - if (RenderProcess::InProcessPlugins()) + if (RenderProcessImpl::InProcessPlugins()) CoInitialize(0); #endif @@ -263,7 +263,7 @@ RenderThread::~RenderThread() { // Clean up plugin channels before this thread goes away. PluginChannelBase::CleanupChannels(); // Don't call COM if the renderer is in the sandbox. - if (RenderProcess::InProcessPlugins()) + if (RenderProcessImpl::InProcessPlugins()) CoUninitialize(); #endif } @@ -755,7 +755,7 @@ void RenderThread::EnsureWebKitInitialized() { } WebRuntimeFeatures::enableMediaPlayer( - RenderProcess::current()->initialized_media_library()); + RenderProcess::current()->HasInitializedMediaLibrary()); WebRuntimeFeatures::enableSockets( !command_line.HasSwitch(switches::kDisableWebSockets)); diff --git a/chrome/renderer/render_thread.h b/chrome/renderer/render_thread.h index a7ad1fb..2aaf768 100644 --- a/chrome/renderer/render_thread.h +++ b/chrome/renderer/render_thread.h @@ -52,6 +52,12 @@ class WebStorageEventDispatcher; // The RenderThreadBase is the minimal interface that a RenderView/Widget // expects from a render thread. The interface basically abstracts a way to send // and receive messages. +// +// TODO(brettw) this should be refactored like RenderProcess/RenderProcessImpl: +// This class should be named RenderThread and the implementation below should +// be RenderThreadImpl. The ::current() getter on the impl should then be moved +// here so we can provide another implementation of RenderThread for tests +// without having to check for NULL all the time. class RenderThreadBase { public: virtual ~RenderThreadBase() {} @@ -91,6 +97,10 @@ class RenderThread : public RenderThreadBase, // Returns the one render thread for this process. Note that this should only // be accessed when running on the render thread itself + // + // TODO(brettw) this should be on the abstract base class instead of here, + // and return the base class' interface instead. Currently this causes + // problems with testing. See the comment above RenderThreadBase above. static RenderThread* current(); // Returns the routing ID of the RenderWidget containing the current script diff --git a/chrome/renderer/render_thread_unittest.cc b/chrome/renderer/render_thread_unittest.cc index e22cabb..368e3d4 100644 --- a/chrome/renderer/render_thread_unittest.cc +++ b/chrome/renderer/render_thread_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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. @@ -18,7 +18,7 @@ class RenderThreadTest : public testing::Test { virtual void SetUp() { // Need a MODE_SERVER to make MODE_CLIENTs (like a RenderThread) happy. channel_ = new IPC::Channel(kThreadName, IPC::Channel::MODE_SERVER, NULL); - mock_process_.reset(new MockProcess()); + mock_process_.reset(new MockRenderProcess); mock_process_->set_main_thread(new RenderThread(kThreadName)); } @@ -36,7 +36,7 @@ class RenderThreadTest : public testing::Test { protected: MessageLoop message_loop_; - scoped_ptr<MockProcess> mock_process_; + scoped_ptr<MockRenderProcess> mock_process_; IPC::Channel *channel_; }; diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc index 1e72592..8acdcaf 100644 --- a/chrome/renderer/render_view.cc +++ b/chrome/renderer/render_view.cc @@ -53,6 +53,7 @@ #include "chrome/renderer/plugin_channel_host.h" #include "chrome/renderer/print_web_view_helper.h" #include "chrome/renderer/render_process.h" +#include "chrome/renderer/render_thread.h" #include "chrome/renderer/renderer_webstoragenamespace_impl.h" #include "chrome/renderer/spellchecker/spellcheck.h" #include "chrome/renderer/user_script_slave.h" @@ -1349,29 +1350,6 @@ void RenderView::OpenURL( // WebViewDelegate ------------------------------------------------------------ -void RenderView::DidPaint() { - WebFrame* main_frame = webview()->mainFrame(); - - if (main_frame->provisionalDataSource()) { - // If we have a provisional frame we are between the start - // and commit stages of loading...ignore this paint. - return; - } - - WebDataSource* ds = main_frame->dataSource(); - NavigationState* navigation_state = NavigationState::FromDataSource(ds); - DCHECK(navigation_state); - - Time now = Time::Now(); - if (navigation_state->first_paint_time().is_null()) { - navigation_state->set_first_paint_time(now); - } - if (navigation_state->first_paint_after_load_time().is_null() && - !navigation_state->finish_load_time().is_null()) { - navigation_state->set_first_paint_after_load_time(now); - } -} - void RenderView::LoadNavigationErrorPage(WebFrame* frame, const WebURLRequest& failed_request, const WebURLError& error, @@ -2988,7 +2966,7 @@ webkit_glue::WebPluginDelegate* RenderView::CreatePluginDelegate( mime_type_to_use = &mime_type; bool use_pepper_host = false; - bool in_process_plugin = RenderProcess::current()->in_process_plugins(); + bool in_process_plugin = RenderProcess::current()->UseInProcessPlugins(); // Check for trusted Pepper plugins. const char kPepperPrefix[] = "pepper-"; if (StartsWithASCII(*mime_type_to_use, kPepperPrefix, true)) { @@ -3010,10 +2988,11 @@ webkit_glue::WebPluginDelegate* RenderView::CreatePluginDelegate( } if (in_process_plugin) { if (use_pepper_host) { - return WebPluginDelegatePepper::Create( - path, - *mime_type_to_use, - AsWeakPtr()); + WebPluginDelegatePepper* pepper_plugin = + WebPluginDelegatePepper::Create(path, *mime_type_to_use, + AsWeakPtr()); + current_pepper_plugins_.insert(pepper_plugin); + return pepper_plugin; } else { #if defined(OS_WIN) // In-proc plugins aren't supported on Linux or Mac. return WebPluginDelegateImpl::Create( @@ -3472,6 +3451,17 @@ void RenderView::InsertCSS(const std::wstring& frame_xpath, web_frame->insertStyleText(WebString::fromUTF8(css), WebString::fromUTF8(id)); } +void RenderView::OnPepperPluginDestroy( + WebPluginDelegatePepper* pepper_plugin) { + std::set<WebPluginDelegatePepper*>::iterator found_pepper = + current_pepper_plugins_.find(pepper_plugin); + if (found_pepper == current_pepper_plugins_.end()) { + NOTREACHED(); + return; + } + current_pepper_plugins_.erase(found_pepper); +} + void RenderView::OnScriptEvalRequest(const std::wstring& frame_xpath, const std::wstring& jscript) { EvaluateScript(frame_xpath, jscript); @@ -3950,6 +3940,53 @@ void RenderView::OnResize(const gfx::Size& new_size, RenderWidget::OnResize(new_size, resizer_rect); } +void RenderView::DidInitiatePaint() { + // Notify any pepper plugins that we started painting. The plugin "should" + // never notified that we started painting, this is used for internal + // bookkeeping only, so we know that the set can not change under us. + for (std::set<WebPluginDelegatePepper*>::iterator i = + current_pepper_plugins_.begin(); + i != current_pepper_plugins_.end(); ++i) + (*i)->RenderViewInitiatedPaint(); +} + +void RenderView::DidFlushPaint() { + // Notify any pepper plugins that we painted. This will call into the plugin, + // and we it may ask to close itself as a result. This will, in turn, modify + // our set, possibly invalidating the iterator. So we iterate on a copy that + // won't change out from under us. + std::set<WebPluginDelegatePepper*> plugins = current_pepper_plugins_; + for (std::set<WebPluginDelegatePepper*>::iterator i = plugins.begin(); + i != plugins.end(); ++i) { + // The copy above makes sure our iterator is never invalid if some plugins + // are destroyed. But some plugin may decide to close all of its views in + // response to a paint in one of them, so we need to make sure each one is + // still "current" before using it. + if (current_pepper_plugins_.find(*i) != current_pepper_plugins_.end()) + (*i)->RenderViewFlushedPaint(); + } + + WebFrame* main_frame = webview()->mainFrame(); + + // If we have a provisional frame we are between the start and commit stages + // of loading and we don't want to save stats. + if (!main_frame->provisionalDataSource()) { + WebDataSource* ds = main_frame->dataSource(); + NavigationState* navigation_state = NavigationState::FromDataSource(ds); + DCHECK(navigation_state); + + Time now = Time::Now(); + if (navigation_state->first_paint_time().is_null()) { + navigation_state->set_first_paint_time(now); + } + if (navigation_state->first_paint_after_load_time().is_null() && + !navigation_state->finish_load_time().is_null()) { + navigation_state->set_first_paint_after_load_time(now); + } + } +} + + void RenderView::OnClearFocusedNode() { if (webview()) webview()->clearFocusedNode(); diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h index 0248eb2..c149320 100644 --- a/chrome/renderer/render_view.h +++ b/chrome/renderer/render_view.h @@ -76,7 +76,9 @@ class GeolocationDispatcher; class GURL; class ListValue; class NavigationState; +class PepperDeviceTest; class PrintWebViewHelper; +class WebPluginDelegatePepper; class WebPluginDelegateProxy; struct ContextMenuMediaParams; struct ThumbnailScore; @@ -440,6 +442,11 @@ class RenderView : public RenderWidget, const std::string& css, const std::string& id); + // Informs us that the given pepper plugin we created is being deleted the + // pointer must not be dereferenced as this is called from the destructor of + // the plugin. + void OnPepperPluginDestroy(WebPluginDelegatePepper* pepper_plugin); + // Whether content state (such as form state and scroll position) should be // sent to the browser immediately. This is normally false, but set to true // by some tests. @@ -492,7 +499,8 @@ class RenderView : public RenderWidget, virtual void Close(); virtual void OnResize(const gfx::Size& new_size, const gfx::Rect& resizer_rect); - virtual void DidPaint(); + virtual void DidInitiatePaint(); + virtual void DidFlushPaint(); virtual void DidHandleKeyEvent(); #if OS_MACOSX virtual void OnSetFocus(bool enable); @@ -503,6 +511,7 @@ class RenderView : public RenderWidget, private: // For unit tests. friend class RenderViewTest; + friend class PepperDeviceTest; FRIEND_TEST(RenderViewTest, OnLoadAlternateHTMLText); FRIEND_TEST(RenderViewTest, OnNavStateChanged); FRIEND_TEST(RenderViewTest, OnImeStateChanged); @@ -1137,6 +1146,10 @@ class RenderView : public RenderWidget, TextTranslatorImpl text_translator_; scoped_ptr<PageTranslator> page_translator_; + // A list of all pepper plugins that we've created that haven't been + // destroyed yet. + std::set<WebPluginDelegatePepper*> current_pepper_plugins_; + // The FormManager for this RenderView. FormManager form_manager_; diff --git a/chrome/renderer/render_widget.cc b/chrome/renderer/render_widget.cc index 7d59256..0096683 100644 --- a/chrome/renderer/render_widget.cc +++ b/chrome/renderer/render_widget.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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. @@ -15,6 +15,7 @@ #include "chrome/common/render_messages.h" #include "chrome/common/transport_dib.h" #include "chrome/renderer/render_process.h" +#include "chrome/renderer/render_thread.h" #include "skia/ext/platform_canvas.h" #include "third_party/skia/include/core/SkShader.h" #include "third_party/WebKit/WebKit/chromium/public/WebCursorInfo.h" @@ -271,8 +272,8 @@ void RenderWidget::OnUpdateRectAck() { current_paint_buf_ = NULL; } - // Notify subclasses - DidPaint(); + // Notify subclasses. + DidFlushPaint(); // Continue painting if necessary... CallDoDeferredUpdate(); @@ -498,6 +499,9 @@ void RenderWidget::DoDeferredUpdate() { next_paint_flags_ = 0; UpdateIME(); + + // Let derived classes know we've painted. + DidInitiatePaint(); } /////////////////////////////////////////////////////////////////////////////// diff --git a/chrome/renderer/render_widget.h b/chrome/renderer/render_widget.h index cc32898..584930d 100644 --- a/chrome/renderer/render_widget.h +++ b/chrome/renderer/render_widget.h @@ -173,9 +173,12 @@ class RenderWidget : public IPC::Channel::Listener, void OnSetTextDirection(WebKit::WebTextDirection direction); void OnGpuChannelEstablished(const IPC::ChannelHandle& channel_handle); - // Override point to notify that a paint has happened. This fires after the - // browser side has updated the screen for a newly painted region. - virtual void DidPaint() {} + // Override point to notify derived classes that a paint has happened. + // DidInitiatePaint happens when we've generated a new bitmap and sent it to + // the browser. DidFlushPaint happens once we've received the ACK that the + // screen has actually been updated. + virtual void DidInitiatePaint() {} + virtual void DidFlushPaint() {} // Sets the "hidden" state of this widget. All accesses to is_hidden_ should // use this method so that we can properly inform the RenderThread of our diff --git a/chrome/renderer/render_widget_unittest.cc b/chrome/renderer/render_widget_unittest.cc index b649725f..5808bc0 100644 --- a/chrome/renderer/render_widget_unittest.cc +++ b/chrome/renderer/render_widget_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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. @@ -28,7 +28,7 @@ class RenderWidgetTest : public testing::Test { private: // testing::Test virtual void SetUp() { - mock_process_.reset(new MockProcess()); + mock_process_.reset(new MockRenderProcess); render_thread_.set_routing_id(kRouteId); widget_ = RenderWidget::Create(kOpenerId, &render_thread_, true); ASSERT_TRUE(widget_); @@ -38,7 +38,7 @@ class RenderWidgetTest : public testing::Test { mock_process_.reset(); } - scoped_ptr<MockProcess> mock_process_; + scoped_ptr<MockRenderProcess> mock_process_; }; TEST_F(RenderWidgetTest, CreateAndCloseWidget) { diff --git a/chrome/renderer/renderer_main.cc b/chrome/renderer/renderer_main.cc index d4e2c5f..2556ba7 100644 --- a/chrome/renderer/renderer_main.cc +++ b/chrome/renderer/renderer_main.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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. @@ -23,7 +23,7 @@ #include "chrome/common/main_function_params.h" #include "chrome/common/net/net_resource_provider.h" #include "chrome/renderer/renderer_main_platform_delegate.h" -#include "chrome/renderer/render_process.h" +#include "chrome/renderer/render_process_impl.h" #include "chrome/renderer/render_thread.h" #include "grit/generated_resources.h" #include "net/base/net_module.h" @@ -219,7 +219,7 @@ int RendererMain(const MainFunctionParams& parameters) { #else // The main message loop of the renderer services doesn't have IO or UI tasks, // unless in-process-plugins is used. - MessageLoop main_message_loop(RenderProcess::InProcessPlugins() ? + MessageLoop main_message_loop(RenderProcessImpl::InProcessPlugins() ? MessageLoop::TYPE_UI : MessageLoop::TYPE_DEFAULT); #endif @@ -255,7 +255,7 @@ int RendererMain(const MainFunctionParams& parameters) { #if !defined(OS_LINUX) // TODO(markus): Check if it is OK to unconditionally move this // instruction down. - RenderProcess render_process; + RenderProcessImpl render_process; render_process.set_main_thread(new RenderThread()); #endif bool run_loop = true; @@ -263,7 +263,7 @@ int RendererMain(const MainFunctionParams& parameters) { run_loop = platform.EnableSandbox(); } #if defined(OS_LINUX) - RenderProcess render_process; + RenderProcessImpl render_process; render_process.set_main_thread(new RenderThread()); #endif diff --git a/chrome/renderer/webplugin_delegate_pepper.cc b/chrome/renderer/webplugin_delegate_pepper.cc index e6efd40..4c62381 100644 --- a/chrome/renderer/webplugin_delegate_pepper.cc +++ b/chrome/renderer/webplugin_delegate_pepper.cc @@ -225,6 +225,24 @@ FilePath WebPluginDelegatePepper::GetPluginPath() { return instance()->plugin_lib()->plugin_info().path; } +void WebPluginDelegatePepper::RenderViewInitiatedPaint() { + // Broadcast event to all 2D contexts. + Graphics2DMap::iterator iter2d(&graphic2d_contexts_); + while (!iter2d.IsAtEnd()) { + iter2d.GetCurrentValue()->RenderViewInitiatedPaint(); + iter2d.Advance(); + } +} + +void WebPluginDelegatePepper::RenderViewFlushedPaint() { + // Broadcast event to all 2D contexts. + Graphics2DMap::iterator iter2d(&graphic2d_contexts_); + while (!iter2d.IsAtEnd()) { + iter2d.GetCurrentValue()->RenderViewFlushedPaint(); + iter2d.Advance(); + } +} + WebPluginResourceClient* WebPluginDelegatePepper::CreateResourceClient( unsigned long resource_id, const GURL& url, int notify_id) { return instance()->CreateStream(resource_id, url, std::string(), notify_id); @@ -259,7 +277,7 @@ NPError WebPluginDelegatePepper::Device2DInitializeContext( // it will have a window handle. plugin_->SetWindow(NULL); - scoped_ptr<Graphics2DDeviceContext> g2d(new Graphics2DDeviceContext()); + scoped_ptr<Graphics2DDeviceContext> g2d(new Graphics2DDeviceContext(this)); NPError status = g2d->Initialize(window_rect_, config, context); if (NPERR_NO_ERROR == status) { context->reserved = reinterpret_cast<void *>( @@ -662,6 +680,9 @@ WebPluginDelegatePepper::WebPluginDelegatePepper( WebPluginDelegatePepper::~WebPluginDelegatePepper() { DestroyInstance(); + + if (render_view_) + render_view_->OnPepperPluginDestroy(this); } void WebPluginDelegatePepper::ForwardSetWindow() { diff --git a/chrome/renderer/webplugin_delegate_pepper.h b/chrome/renderer/webplugin_delegate_pepper.h index 181b40b..0bcbc06 100644 --- a/chrome/renderer/webplugin_delegate_pepper.h +++ b/chrome/renderer/webplugin_delegate_pepper.h @@ -37,6 +37,8 @@ class WebPluginDelegatePepper : public webkit_glue::WebPluginDelegate { const std::string& mime_type, const base::WeakPtr<RenderView>& render_view); + NPAPI::PluginInstance* instance() { return instance_.get(); } + // WebPluginDelegate implementation virtual bool Initialize(const GURL& url, const std::vector<std::string>& arg_names, @@ -144,6 +146,12 @@ class WebPluginDelegatePepper : public webkit_glue::WebPluginDelegate { // Returns the path for the library implementing this plugin. FilePath GetPluginPath(); + // Notifications when the RenderView painted the screen (InitiatedPaint) and + // when an ack was received that the browser copied it to the screen + // (FlushedPaint). + void RenderViewInitiatedPaint(); + void RenderViewFlushedPaint(); + private: WebPluginDelegatePepper( const base::WeakPtr<RenderView>& render_view, @@ -157,8 +165,6 @@ class WebPluginDelegatePepper : public webkit_glue::WebPluginDelegate { //----------------------------------------- // used for windowed and windowless plugins - NPAPI::PluginInstance* instance() { return instance_.get(); } - // Closes down and destroys our plugin instance. void DestroyInstance(); @@ -191,7 +197,8 @@ class WebPluginDelegatePepper : public webkit_glue::WebPluginDelegate { std::vector<gfx::Rect> cutout_rects_; // Open device contexts - IDMap<Graphics2DDeviceContext, IDMapOwnPointer> graphic2d_contexts_; + typedef IDMap<Graphics2DDeviceContext, IDMapOwnPointer> Graphics2DMap; + Graphics2DMap graphic2d_contexts_; IDMap<AudioDeviceContext, IDMapOwnPointer> audio_contexts_; // Plugin graphics context implementation diff --git a/chrome/test/render_view_test.cc b/chrome/test/render_view_test.cc index 001e7b1..f920a2f 100644 --- a/chrome/test/render_view_test.cc +++ b/chrome/test/render_view_test.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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. @@ -13,6 +13,7 @@ #include "chrome/renderer/extensions/extension_process_bindings.h" #include "chrome/renderer/extensions/js_only_v8_extensions.h" #include "chrome/renderer/extensions/renderer_extension_bindings.h" +#include "chrome/renderer/mock_render_process.h" #include "chrome/renderer/renderer_main_platform_delegate.h" #include "third_party/WebKit/WebKit/chromium/public/WebFrame.h" #include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h" @@ -34,6 +35,12 @@ const int32 kRouteId = 5; const int32 kOpenerId = 7; } // namespace +RenderViewTest::RenderViewTest() { +} + +RenderViewTest::~RenderViewTest() { +} + void RenderViewTest::ProcessPendingMessages() { msg_loop_.PostTask(FROM_HERE, new MessageLoop::QuitTask()); msg_loop_.Run(); @@ -89,7 +96,7 @@ void RenderViewTest::SetUp() { Extension::kPermissionNames + Extension::kNumPermissions); ExtensionProcessBindings::SetAPIPermissions("", permissions); - mock_process_.reset(new MockProcess()); + mock_process_.reset(new MockRenderProcess); render_thread_.set_routing_id(kRouteId); @@ -102,6 +109,7 @@ void RenderViewTest::SetUp() { // Attach a pseudo keyboard device to this object. mock_keyboard_.reset(new MockKeyboard()); } + void RenderViewTest::TearDown() { // Try very hard to collect garbage before shutting down. GetMainFrame()->collectGarbage(); diff --git a/chrome/test/render_view_test.h b/chrome/test/render_view_test.h index add9d55..6a36097 100644 --- a/chrome/test/render_view_test.h +++ b/chrome/test/render_view_test.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 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. @@ -13,7 +13,6 @@ #include "chrome/common/native_web_keyboard_event.h" #include "chrome/common/sandbox_init_wrapper.h" #include "chrome/renderer/mock_keyboard.h" -#include "chrome/renderer/mock_render_process.h" #include "chrome/renderer/mock_render_thread.h" #include "chrome/renderer/render_view.h" #include "chrome/renderer/renderer_main_platform_delegate.h" @@ -21,10 +20,12 @@ #include "testing/gtest/include/gtest/gtest.h" #include "third_party/WebKit/WebKit/chromium/public/WebFrame.h" +class MockRenderProcess; + class RenderViewTest : public testing::Test { public: - RenderViewTest() {} - ~RenderViewTest() {} + RenderViewTest(); + ~RenderViewTest(); protected: // Spins the message loop to process all messages that are currently pending. @@ -56,7 +57,7 @@ class RenderViewTest : public testing::Test { MessageLoop msg_loop_; MockRenderThread render_thread_; - scoped_ptr<MockProcess> mock_process_; + scoped_ptr<MockRenderProcess> mock_process_; scoped_refptr<RenderView> view_; RendererWebKitClientImpl webkitclient_; scoped_ptr<MockKeyboard> mock_keyboard_; diff --git a/gpu/gpu_plugin/gpu_plugin.h b/gpu/gpu_plugin/gpu_plugin.h index b973d7cf..05c98a9 100644 --- a/gpu/gpu_plugin/gpu_plugin.h +++ b/gpu/gpu_plugin/gpu_plugin.h @@ -19,7 +19,7 @@ NPError API_CALL NP_GetEntryPoints(NPPluginFuncs* funcs); #if defined(OS_POSIX) && !defined(OS_MACOSX) NPError API_CALL NP_Initialize(NPNetscapeFuncs *browser_funcs, - NPPluginFuncs* plugin_funcs); + NPPluginFuncs* plugin_funcs); #else NPError API_CALL NP_Initialize(NPNetscapeFuncs* browser_funcs); #endif diff --git a/webkit/glue/plugins/plugin_list.cc b/webkit/glue/plugins/plugin_list.cc index f7643b4..3994f15 100644 --- a/webkit/glue/plugins/plugin_list.cc +++ b/webkit/glue/plugins/plugin_list.cc @@ -58,6 +58,17 @@ void PluginList::RegisterInternalPlugin(const PluginVersionInfo& info) { internal_plugins_.push_back(info); } +void PluginList::UnregisterInternalPlugin(const FilePath& path) { + AutoLock lock(lock_); + for (size_t i = 0; i < internal_plugins_.size(); i++) { + if (internal_plugins_[i].path == path) { + internal_plugins_.erase(internal_plugins_.begin() + i); + return; + } + } + NOTREACHED(); +} + bool PluginList::ReadPluginInfo(const FilePath &filename, WebPluginInfo* info, const PluginEntryPoints** entry_points) { diff --git a/webkit/glue/plugins/plugin_list.h b/webkit/glue/plugins/plugin_list.h index dea1ab9..c920397 100644 --- a/webkit/glue/plugins/plugin_list.h +++ b/webkit/glue/plugins/plugin_list.h @@ -91,6 +91,12 @@ class PluginList { // be loaded using PluginList::LoadPlugin(). void RegisterInternalPlugin(const PluginVersionInfo& info); + // Removes a specified internal plugin from the list. The search will match + // on the path from the version info previously registered. + // + // This is generally only necessary for tests. + void UnregisterInternalPlugin(const FilePath& path); + // Creates a WebPluginInfo structure given a plugin's path. On success // returns true, with the information being put into "info". If it's an // internal plugin, "entry_points" is filled in as well with a |