diff options
30 files changed, 822 insertions, 272 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 5230bab..55dcc52 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 7acf628..ec3201b 100755 --- a/chrome/chrome_renderer.gypi +++ b/chrome/chrome_renderer.gypi @@ -109,8 +109,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 533d779..2171cc5 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -85,6 +85,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', @@ -898,6 +899,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..1a9e875 100644 --- a/chrome/renderer/pepper_devices.h +++ b/chrome/renderer/pepper_devices.h @@ -7,6 +7,7 @@ #include <stdint.h> +#include "base/gfx/rect.h" #include "base/scoped_ptr.h" #include "base/shared_memory.h" #include "base/simple_thread.h" @@ -18,12 +19,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 +37,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); }; diff --git a/chrome/renderer/pepper_devices_unittest.cc b/chrome/renderer/pepper_devices_unittest.cc new file mode 100644 index 0000000..89a371f --- /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) +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 API_CALL 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 API_CALL 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..c4c7ca3 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,31 @@ 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_; } + 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. + // Just like UseInProcessPlugins(), 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.cc b/chrome/renderer/render_process_impl.cc index f5d72c8..9d621da 100644 --- a/chrome/renderer/render_process.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.h" - #include "base/basictypes.h" #include "base/command_line.h" #include "base/compiler_specific.h" @@ -31,7 +31,6 @@ #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" @@ -39,13 +38,40 @@ #include "base/mac_util.h" #endif +namespace { + +bool LaunchNaClProcess(const char* url, + int imc_fd, + nacl::Handle* imc_handle, + nacl::Handle* nacl_process_handle, + int* nacl_process_id) { + // TODO(gregoryd): nacl::FileDescriptor will be soon merged with + // base::FileDescriptor + nacl::FileDescriptor imc_descriptor; + base::ProcessHandle nacl_process; + if (!RenderThread::current()->Send( + new ViewHostMsg_LaunchNaCl(ASCIIToWide(url), + imc_fd, + &imc_descriptor, + &nacl_process, + reinterpret_cast<base::ProcessId*>(nacl_process_id)))) { + return false; + } + *imc_handle = nacl::ToNativeHandle(imc_descriptor); + *nacl_process_handle = nacl_process; + return true; +} + +} // namespace + //----------------------------------------------------------------------------- -RenderProcess::RenderProcess() - : ALLOW_THIS_IN_INITIALIZER_LIST(shared_mem_cache_cleaner_( +RenderProcessImpl::RenderProcessImpl() + : RenderProcess(), + ALLOW_THIS_IN_INITIALIZER_LIST(shared_mem_cache_cleaner_( base::TimeDelta::FromSeconds(5), - this, &RenderProcess::ClearTransportDIBCache)), - sequence_number_(0) { + this, &RenderProcessImpl::ClearTransportDIBCache)), + transport_dib_next_sequence_number_(0) { in_process_plugins_ = InProcessPlugins(); for (size_t i = 0; i < arraysize(shared_mem_cache_); ++i) shared_mem_cache_[i] = NULL; @@ -61,9 +87,8 @@ RenderProcess::RenderProcess() GetModuleHandle(L"GDI32.DLL"), "GdiInitializeLanguagePack")); DCHECK(gdi_init_lpk); - if (gdi_init_lpk) { + if (gdi_init_lpk) gdi_init_lpk(0); - } } #endif @@ -76,7 +101,7 @@ RenderProcess::RenderProcess() 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)) { @@ -89,12 +114,11 @@ RenderProcess::RenderProcess() #ifndef DISABLE_NACL if (command_line.HasSwitch(switches::kInternalNaCl)) - RegisterInternalNaClPlugin(RenderProcess::LaunchNaClProcess); + 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(); @@ -106,16 +130,10 @@ RenderProcess::RenderProcess() 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 } -RenderProcess::~RenderProcess() { +RenderProcessImpl::~RenderProcessImpl() { // TODO(port): Try and limit what we pull in for our non-Win unit test bundle. #ifndef NDEBUG // log important leaked objects @@ -126,81 +144,9 @@ RenderProcess::~RenderProcess() { ClearTransportDIBCache(); } -bool RenderProcess::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 RenderProcess::LaunchNaClProcess(const char* url, - int imc_fd, - nacl::Handle* imc_handle, - nacl::Handle* nacl_process_handle, - int* nacl_process_id) { - // TODO(gregoryd): nacl::FileDescriptor will be soon merged with - // base::FileDescriptor - nacl::FileDescriptor imc_descriptor; - base::ProcessHandle nacl_process; - if (!RenderThread::current()->Send( - new ViewHostMsg_LaunchNaCl(ASCIIToWide(url), - imc_fd, - &imc_descriptor, - &nacl_process, - reinterpret_cast<base::ProcessId*>(nacl_process_id)))) { - return false; - } - *imc_handle = nacl::ToNativeHandle(imc_descriptor); - *nacl_process_handle = nacl_process; - return true; -} - -// ----------------------------------------------------------------------------- -// Platform specific code for dealing with bitmap transport... - -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. - 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 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()); - main_thread()->Send(msg); -#endif - - delete dib; -} - -// ----------------------------------------------------------------------------- - - -skia::PlatformCanvas* RenderProcess::GetDrawingCanvas( - TransportDIB** memory, const gfx::Rect& rect) { +skia::PlatformCanvas* RenderProcessImpl::GetDrawingCanvas( + TransportDIB** memory, + const gfx::Rect& rect) { int width = rect.width(); int height = rect.height(); const size_t stride = skia::PlatformCanvas::StrideForWidth(rect.width()); @@ -221,13 +167,13 @@ skia::PlatformCanvas* RenderProcess::GetDrawingCanvas( if (!GetTransportDIBFromCache(memory, size)) { *memory = CreateTransportDIB(size); if (!*memory) - return false; + return NULL; } return (*memory)->GetPlatformCanvas(width, height); } -void RenderProcess::ReleaseTransportDIB(TransportDIB* mem) { +void RenderProcessImpl::ReleaseTransportDIB(TransportDIB* mem) { if (PutSharedMemInCache(mem)) { shared_mem_cache_cleaner_.Reset(); return; @@ -236,8 +182,31 @@ void RenderProcess::ReleaseTransportDIB(TransportDIB* mem) { FreeTransportDIB(mem); } -bool RenderProcess::GetTransportDIBFromCache(TransportDIB** mem, - size_t size) { +bool RenderProcessImpl::UseInProcessPlugins() const { + return in_process_plugins_; +} + +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) { // 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] && @@ -251,7 +220,25 @@ bool RenderProcess::GetTransportDIBFromCache(TransportDIB** mem, return false; } -int RenderProcess::FindFreeCacheSlot(size_t size) { +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 // - if full, then replace smallest entry which is smaller than |size| @@ -279,20 +266,33 @@ int RenderProcess::FindFreeCacheSlot(size_t size) { return smallest_index; } -bool RenderProcess::PutSharedMemInCache(TransportDIB* mem) { - const int slot = FindFreeCacheSlot(mem->size()); - if (slot == -1) - return false; - - shared_mem_cache_[slot] = mem; - return true; +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 RenderProcess::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; - } - } +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; } diff --git a/chrome/renderer/render_process_impl.h b/chrome/renderer/render_process_impl.h new file mode 100644 index 0000000..11778f0 --- /dev/null +++ b/chrome/renderer/render_process_impl.h @@ -0,0 +1,74 @@ +// 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_IMPL_H_ +#define CHROME_RENDERER_RENDER_PROCESS_IMPL_H_ + +#include "base/timer.h" +#include "chrome/renderer/render_process.h" +#include "chrome/renderer/render_thread.h" +#include "native_client/src/shared/imc/nacl_imc.h" +#include "skia/ext/platform_canvas.h" + +// Implementation of the RenderProcess interface for the regular browser. +// See also MockRenderProcess which implements the active "RenderProcess" when +// running under certain unit tests. +class RenderProcessImpl : public RenderProcess { + public: + RenderProcessImpl(); + ~RenderProcessImpl(); + + // 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; + + // Like UseInProcessPlugins(), but called before RenderProcess is created + // and does not allow overriding by tests. This just checks the command line + // each time. + static bool InProcessPlugins(); + + 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<RenderProcessImpl> shared_mem_cache_cleaner_; + + // TransportDIB sequence number + uint32 transport_dib_next_sequence_number_; + + bool in_process_plugins_; + bool initialized_media_library_; + + DISALLOW_COPY_AND_ASSIGN(RenderProcessImpl); +}; + +#endif // CHROME_RENDERER_RENDER_PROCESS_IMPL_H_ 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 047dbff..2daec5d 100644 --- a/chrome/renderer/render_thread.cc +++ b/chrome/renderer/render_thread.cc @@ -753,7 +753,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 892735f..236cf87 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" @@ -1348,29 +1349,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, @@ -2984,7 +2962,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)) { @@ -3006,10 +2984,10 @@ 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); } else { #if defined(OS_WIN) // In-proc plugins aren't supported on Linux or Mac. return WebPluginDelegateImpl::Create( @@ -3468,6 +3446,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); @@ -3947,6 +3936,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 797402f..b2233bb 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; @@ -439,6 +441,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. @@ -491,7 +498,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); @@ -502,6 +510,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); @@ -1136,6 +1145,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 343db541..03fbbd7 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" @@ -270,8 +271,8 @@ void RenderWidget::OnUpdateRectAck() { current_paint_buf_ = NULL; } - // Notify subclasses - DidPaint(); + // Notify subclasses. + DidFlushPaint(); // Continue painting if necessary... CallDoDeferredUpdate(); @@ -497,6 +498,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 ab591f0..dab2a81 100644 --- a/chrome/renderer/render_widget.h +++ b/chrome/renderer/render_widget.h @@ -161,9 +161,12 @@ class RenderWidget : public IPC::Channel::Listener, void OnMsgRepaint(const gfx::Size& size_to_paint); void OnSetTextDirection(WebKit::WebTextDirection direction); - // 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 10c66cd..0d235c9 100644 --- a/chrome/renderer/webplugin_delegate_pepper.cc +++ b/chrome/renderer/webplugin_delegate_pepper.cc @@ -217,6 +217,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); @@ -251,7 +269,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 *>( @@ -637,6 +655,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 a67b530..8364a60 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 |