diff --git a/content/common/browser_plugin_messages.h b/content/common/browser_plugin_messages.h index db40c45554c58d..ae4c622c646f43 100644 --- a/content/common/browser_plugin_messages.h +++ b/content/common/browser_plugin_messages.h @@ -13,6 +13,7 @@ #include "ipc/ipc_channel_handle.h" #include "ipc/ipc_message_macros.h" #include "ipc/ipc_message_utils.h" +#include "ppapi/c/pp_instance.h" #include "ui/gfx/size.h" #define IPC_MESSAGE_START BrowserPluginMsgStart @@ -20,53 +21,68 @@ // Browser plugin messages // ----------------------------------------------------------------------------- -// These messages are from the host renderer to the browser process +// These messages are from the embedder to the browser process // A renderer sends this to the browser process when it wants to // create a browser plugin. The browser will create a guest renderer process // if necessary. -IPC_MESSAGE_ROUTED4(BrowserPluginHostMsg_OpenChannel, +IPC_MESSAGE_ROUTED4(BrowserPluginHostMsg_NavigateFromEmbedder, int /* plugin instance id*/, long long /* frame id */, std::string /* src */, gfx::Size /* size */) +// Initially before we create a guest renderer, browser plugin containers +// have a placeholder called BrowserPlugin where each BrowserPlugin has a unique +// ID. During pepper plugin initialization, the embedder page and the plugin +// negotiate an ID of type PP_Instance. The browser talks to the guest +// RenderView via yet another identifier called the routing ID. The browser +// has to keep track of how all these identifiers are associated with one +// another. +// For reference: +// 1. The embedder page sees the guest renderer as a plugin and so it talks +// to the guest via the PP_Instance identifer. +// 2. The guest renderer talks to the browser and vice versa via a routing ID. +// 3. The BrowserPlugin ID uniquely identifies a browser plugin container +// instance within an embedder. +// This identifier exists prior to the existence of the routing ID and the +// PP_Instance identifier. +// The purpose of this message is to tell the browser to map a PP_Instance +// identifier to BrowserPlugin identifier. +IPC_MESSAGE_ROUTED2(BrowserPluginHostMsg_MapInstance, + int /* container_id */, + PP_Instance /* instance */) + // ----------------------------------------------------------------------------- // These messages are from the browser process to the guest renderer. -// Creates a channel to talk to a renderer. The guest renderer will respond -// with BrowserPluginHostMsg_ChannelCreated. -IPC_MESSAGE_CONTROL2(BrowserPluginMsg_CreateChannel, - base::ProcessHandle /* host_renderer_process_handle */, - int /* host_renderer_id */) +IPC_MESSAGE_CONTROL2(BrowserPluginMsg_CompleteNavigation, + int /* guest_routing_id */, + PP_Instance /* instance */) // ----------------------------------------------------------------------------- // These messages are from the guest renderer to the browser process -// Reply to BrowserPluginMsg_CreateChannel. The handle will be NULL if the -// channel could not be established. This could be because the IPC could not be -// created for some weird reason, but more likely that the renderer failed to -// initialize properly. -IPC_MESSAGE_CONTROL1(BrowserPluginHostMsg_ChannelCreated, - IPC::ChannelHandle /* handle */) - -// Indicates the guest renderer is ready in response to a ViewMsg_New -IPC_MESSAGE_ROUTED0(BrowserPluginHostMsg_GuestReady) +IPC_MESSAGE_ROUTED1(BrowserPluginHostMsg_ConnectToChannel, + IPC::ChannelHandle /* handle */) -// A host renderer sends this message to the browser when it wants +// A embedder sends this message to the browser when it wants // to resize a guest plugin container so that the guest is relaid out // according to the new size. IPC_MESSAGE_ROUTED2(BrowserPluginHostMsg_ResizeGuest, int32, /* width */ int32 /* height */) +IPC_MESSAGE_ROUTED2(BrowserPluginHostMsg_NavigateFromGuest, + PP_Instance /* instance */, + std::string /* src */) + // ----------------------------------------------------------------------------- -// These messages are from the browser process to the host renderer. +// These messages are from the browser process to the embedder. // A guest instance is ready to be placed. -IPC_MESSAGE_ROUTED3(BrowserPluginMsg_GuestReady_ACK, - int /* instance id */, - base::ProcessHandle /* plugin_process_handle */, - IPC::ChannelHandle /* handle to channel */) - +IPC_MESSAGE_CONTROL3(BrowserPluginMsg_LoadGuest, + int /* instance id */, + int /* guest_process_id */, + IPC::ChannelHandle /* channel_handle */) diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi index 04babf23fb6920..0d67650261826b 100644 --- a/content/content_renderer.gypi +++ b/content/content_renderer.gypi @@ -153,10 +153,18 @@ 'renderer/pepper/pepper_proxy_channel_delegate_impl.h', 'renderer/plugin_channel_host.cc', 'renderer/plugin_channel_host.h', + 'renderer/browser_plugin/browser_plugin.cc', + 'renderer/browser_plugin/browser_plugin.h', + 'renderer/browser_plugin/browser_plugin_channel_manager.cc', + 'renderer/browser_plugin/browser_plugin_channel_manager.h', 'renderer/browser_plugin/browser_plugin_constants.cc', 'renderer/browser_plugin/browser_plugin_constants.h', - 'renderer/browser_plugin/browser_plugin_placeholder.cc', - 'renderer/browser_plugin/browser_plugin_placeholder.h', + 'renderer/browser_plugin/browser_plugin_registry.cc', + 'renderer/browser_plugin/browser_plugin_registry.h', + 'renderer/browser_plugin/browser_plugin_var_serialization_rules.cc', + 'renderer/browser_plugin/browser_plugin_var_serialization_rules.h', + 'renderer/browser_plugin/guest_to_embedder_channel.cc', + 'renderer/browser_plugin/guest_to_embedder_channel.h', 'renderer/render_process.h', 'renderer/render_process_impl.cc', 'renderer/render_process_impl.h', diff --git a/content/renderer/browser_plugin/browser_plugin.cc b/content/renderer/browser_plugin/browser_plugin.cc new file mode 100644 index 00000000000000..9f8a9b1e13a805 --- /dev/null +++ b/content/renderer/browser_plugin/browser_plugin.cc @@ -0,0 +1,167 @@ +// Copyright (c) 2012 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 "content/renderer/browser_plugin/browser_plugin.h" + +#include "base/atomic_sequence_num.h" +#include "base/id_map.h" +#include "base/lazy_instance.h" +#include "base/process.h" +#include "base/string_number_conversions.h" +#include "base/string_piece.h" +#include "base/string_util.h" +#include "base/values.h" +#include "content/common/browser_plugin_messages.h" +#include "content/public/common/url_constants.h" +#include "content/renderer/render_view_impl.h" +#include "ipc/ipc_channel_handle.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebPlugin.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginContainer.h" +#include "webkit/plugins/ppapi/ppapi_plugin_instance.h" +#include "webkit/plugins/ppapi/ppapi_webplugin_impl.h" +#include "webkit/plugins/webview_plugin.h" + +static int g_next_id = 0; + +// The global list of all Browser Plugin Placeholders within a process. +base::LazyInstance >::Leaky + g_all_browser_plugins = LAZY_INSTANCE_INITIALIZER; + +using WebKit::WebPlugin; +using WebKit::WebPluginContainer; +using webkit::WebViewPlugin; + +void Register(int id, BrowserPlugin* browser_plugin) { + g_all_browser_plugins.Get().AddWithID(browser_plugin, id); +} + +void Unregister(int id) { + if (g_all_browser_plugins.Get().Lookup(id)) + g_all_browser_plugins.Get().Remove(id); +} + +// static +WebKit::WebPlugin* BrowserPlugin::Create( + RenderViewImpl* render_view, + WebKit::WebFrame* frame, + const WebKit::WebPluginParams& params) { + // TODO(fsamuel): Figure out what the lifetime is of this class. + // It seems we need to blow away this object once a WebPluginContainer is + // gone. How do we detect it's gone? A WebKit change perhaps? + BrowserPlugin* browser_plugin = new BrowserPlugin( + render_view, frame, params, ""); + return browser_plugin->placeholder(); +} + +// static +BrowserPlugin* BrowserPlugin::FromID(int id) { + return g_all_browser_plugins.Get().Lookup(id); +} + +BrowserPlugin::BrowserPlugin( + RenderViewImpl* render_view, + WebKit::WebFrame* frame, + const WebKit::WebPluginParams& params, + const std::string& html_data) + : render_view_(render_view), + plugin_params_(params), + placeholder_(webkit::WebViewPlugin::Create( + NULL, + render_view->GetWebkitPreferences(), + html_data, + GURL(chrome::kAboutBlankURL))), + plugin_(NULL) { + id_ = ++g_next_id; + Register(id_, this); + + // By default we do not navigate and simply stay with an + // about:blank placeholder. + gfx::Size size; + std::string src; + ParsePluginParameters(0, 0, "", &size, &src); + + if (!src.empty()) { + render_view->Send(new BrowserPluginHostMsg_NavigateFromEmbedder( + render_view->GetRoutingID(), + id_, + frame->identifier(), + src, + size)); + } +} + +BrowserPlugin::~BrowserPlugin() { + Unregister(id_); +} + +void BrowserPlugin::ParsePluginParameters( + int default_width, + int default_height, + const std::string& default_src, + gfx::Size* size, + std::string* src) { + int width = default_width; + int height = default_height; + + // Get the plugin parameters from the attributes vector + for (unsigned i = 0; i < plugin_params_.attributeNames.size(); ++i) { + std::string attributeName = plugin_params_.attributeNames[i].utf8(); + if (LowerCaseEqualsASCII(attributeName, "width")) { + std::string attributeValue = plugin_params_.attributeValues[i].utf8(); + CHECK(base::StringToInt(attributeValue, &width)); + } else if (LowerCaseEqualsASCII(attributeName, "height")) { + std::string attributeValue = plugin_params_.attributeValues[i].utf8(); + CHECK(base::StringToInt(attributeValue, &height)); + } else if (LowerCaseEqualsASCII(attributeName, "src")) { + *src = plugin_params_.attributeValues[i].utf8(); + } + } + // If we didn't find the attributes set or they're not sensible, + // we reset our attributes to the default. + if (src->empty()) { + *src = default_src; + } + + size->SetSize(width, height); +} + +void BrowserPlugin::LoadGuest( + int guest_process_id, + const IPC::ChannelHandle& channel_handle) { + webkit::ppapi::WebPluginImpl* new_guest = + render_view()->CreateBrowserPlugin(channel_handle, + guest_process_id, + plugin_params()); + Replace(new_guest); +} + +void BrowserPlugin::Replace( + webkit::ppapi::WebPluginImpl* new_plugin) { + WebKit::WebPlugin* current_plugin = + plugin_ ? static_cast(plugin_) : placeholder_; + WebKit::WebPluginContainer* container = current_plugin->container(); + if (!new_plugin || !new_plugin->initialize(container)) + return; + + // Clear the container's backing texture ID and the script objects. + if (plugin_) + plugin_->instance()->BindGraphics(plugin_->instance()->pp_instance(), 0); + + // Inform the browser process of the association between the browser plugin's + // instance ID and the pepper channel's PP_Instance identifier. + render_view()->Send(new BrowserPluginHostMsg_MapInstance( + render_view()->GetRoutingID(), + id_, + new_plugin->instance()->pp_instance())); + // TODO(fsamuel): We should delay the swapping out of the current plugin + // until after the guest's WebGraphicsContext3D has been initialized. That + // way, we immediately have something to render onto the screen. + container->setPlugin(new_plugin); + container->invalidate(); + container->reportGeometry(); + if (plugin_) + plugin_->destroy(); + plugin_ = new_plugin; +} diff --git a/content/renderer/browser_plugin/browser_plugin.h b/content/renderer/browser_plugin/browser_plugin.h new file mode 100644 index 00000000000000..206a317db696e0 --- /dev/null +++ b/content/renderer/browser_plugin/browser_plugin.h @@ -0,0 +1,91 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_H_ +#define CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_H_ +#pragma once + +#include "base/process.h" +#include "content/renderer/render_view_impl.h" +#include "ipc/ipc_channel_handle.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginParams.h" +#include "ui/gfx/size.h" +#include "webkit/plugins/webview_plugin.h" + +namespace content { +class RenderView; +} + +namespace WebKit { +class WebPlugin; +} + +// A browser plugin is a plugin container that hosts an out-of-process "guest" +// RenderView. Loading up a new process, creating a new RenderView, navigating +// to a given URL, and establishing a guest-to-embedder channel can take +// hundreds of milliseconds. Furthermore, a RenderView's associated browser-side +// WebContents, RenderViewHost, and SiteInstance must be created and accessed on +// the UI thread of the browser process. +// +// To avoid blocking the embedder RenderView and to avoid introducing the +// potential for deadlock, BrowserPlugin attaches a placeholder that takes +// place of the guest RenderView until the guest has established a connection +// with its embedder RenderView. This permits asynchronously loading of the +// guest while the embedder RenderView is permitted to continue to receive and +// process events. +// +// Furthermore, certain navigations can swap to a new guest RenderView on an +// different process. BrowserPlugin is the consistent facade that the embedder's +// WebKit instance talks to regardless of which process it's communicating with. +class BrowserPlugin { + public: + // Creates a new WebViewPlugin with a BrowserPlugin as a delegate. + static WebKit::WebPlugin* Create( + RenderViewImpl* render_view, + WebKit::WebFrame* frame, + const WebKit::WebPluginParams& params); + + static BrowserPlugin* FromID(int id); + + webkit::WebViewPlugin* placeholder() { return placeholder_; } + + webkit::ppapi::WebPluginImpl* plugin() { return plugin_; } + + const WebKit::WebPluginParams& plugin_params() const { + return plugin_params_; + } + + void LoadGuest(int guest_process_id, + const IPC::ChannelHandle& channel_handle); + + RenderViewImpl* render_view() { return render_view_; } + + private: + BrowserPlugin(RenderViewImpl* render_view, + WebKit::WebFrame* frame, + const WebKit::WebPluginParams& params, + const std::string& html_data); + virtual ~BrowserPlugin(); + + // Parses the width, height, and source URL of the browser plugin + // from the element's attributes and outputs them. If not found, it outputs + // the defaults specified here as parameters. + void ParsePluginParameters(int default_width, int default_height, + const std::string& default_src, + gfx::Size* size, + std::string* src); + // Replace the current guest with a new guest. + void Replace(webkit::ppapi::WebPluginImpl* new_plugin); + + RenderViewImpl* render_view_; + WebKit::WebPluginParams plugin_params_; + webkit::WebViewPlugin* placeholder_; + webkit::ppapi::WebPluginImpl* plugin_; + int id_; + + DISALLOW_COPY_AND_ASSIGN(BrowserPlugin); +}; + +#endif // CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_H_ diff --git a/content/renderer/browser_plugin/browser_plugin_channel_manager.cc b/content/renderer/browser_plugin/browser_plugin_channel_manager.cc new file mode 100644 index 00000000000000..ec6365c3d39931 --- /dev/null +++ b/content/renderer/browser_plugin/browser_plugin_channel_manager.cc @@ -0,0 +1,122 @@ +// Copyright (c) 2012 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 "content/renderer/browser_plugin/browser_plugin_channel_manager.h" + +#include "base/process_util.h" +#include "content/common/browser_plugin_messages.h" +#include "content/common/view_messages.h" +#include "content/renderer/browser_plugin/browser_plugin.h" +#include "content/renderer/browser_plugin/guest_to_embedder_channel.h" +#include "content/renderer/render_thread_impl.h" +#include "content/renderer/render_view_impl.h" +#include "ppapi/c/pp_instance.h" + +namespace content { + +BrowserPluginChannelManager::BrowserPluginChannelManager() { +} + +BrowserPluginChannelManager::~BrowserPluginChannelManager() { +} + +void BrowserPluginChannelManager::CreateRenderView( + const ViewMsg_New_Params& params) { + IPC::ChannelHandle plugin_handle; + plugin_handle.name = + IPC::Channel::GenerateVerifiedChannelID(params.embedder_channel_name); + bool success = true; + scoped_refptr channel = + GetChannelByName(params.embedder_channel_name); + if (!channel) { + channel = new GuestToEmbedderChannel(params.embedder_channel_name); + success = channel->InitChannel(plugin_handle); + +#if defined(OS_POSIX) + // On POSIX, transfer ownership of the renderer-side (client) FD. + // This ensures this process will be notified when it is closed even if a + // connection is not established. + plugin_handle.socket = + base::FileDescriptor(channel->TakeRendererFD(), true); + if (plugin_handle.socket.fd == -1) + success = false; +#endif + DCHECK(success); + embedder_channels_[params.embedder_channel_name] = channel; + } + DCHECK(pending_guests_.find(params.view_id) == + pending_guests_.end()); + pending_guests_[params.view_id] = + RenderViewImpl::Create( + params.parent_window, + params.opener_route_id, + params.renderer_preferences, + params.web_preferences, + new SharedRenderViewCounter(0), + params.view_id, + params.surface_id, + params.session_storage_namespace_id, + params.frame_name, + false, + params.swapped_out, + params.next_page_id, + params.screen_info, + channel, + params.accessibility_mode)->AsWeakPtr(); + RenderThreadImpl::current()->Send( + new BrowserPluginHostMsg_ConnectToChannel(params.view_id, + success ? plugin_handle : IPC::ChannelHandle())); +} + +bool BrowserPluginChannelManager::OnControlMessageReceived( + const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(BrowserPluginChannelManager, message) + IPC_MESSAGE_HANDLER(BrowserPluginMsg_CompleteNavigation, + OnCompleteNavigation) + IPC_MESSAGE_HANDLER(BrowserPluginMsg_LoadGuest, OnLoadGuest) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + + return handled; +} + +GuestToEmbedderChannel* BrowserPluginChannelManager::GetChannelByName( + const std::string& embedder_channel_name) { + EmbedderChannelNameToChannelMap::iterator it = + embedder_channels_.find(embedder_channel_name); + if (it != embedder_channels_.end()) + return it->second; + return NULL; +} + +void BrowserPluginChannelManager::RemoveChannelByName( + const std::string& embedder_channel_name) { + embedder_channels_.erase(embedder_channel_name); +} + +void BrowserPluginChannelManager::OnCompleteNavigation( + int guest_routing_id, + PP_Instance instance) { + CHECK(pending_guests_.find(guest_routing_id) != + pending_guests_.end()); + RenderViewImpl* render_view = pending_guests_[guest_routing_id]; + pending_guests_.erase(guest_routing_id); + GuestToEmbedderChannel* channel = render_view->guest_to_embedder_channel(); + // Associate the RenderView with the provided PP_Instance ID, request the + // receipt of events, and initialize the graphics context. + channel->AddGuest(instance, render_view); + channel->RequestInputEvents(instance); + render_view->GuestReady(instance); +} + +void BrowserPluginChannelManager::OnLoadGuest( + int instance_id, + int guest_process_id, + const IPC::ChannelHandle& channel_handle) { + BrowserPlugin* browser_plugin = BrowserPlugin::FromID(instance_id); + browser_plugin->LoadGuest(guest_process_id, channel_handle); +} + +} // namespace content diff --git a/content/renderer/browser_plugin/browser_plugin_channel_manager.h b/content/renderer/browser_plugin/browser_plugin_channel_manager.h new file mode 100644 index 00000000000000..ab33f0a6103c90 --- /dev/null +++ b/content/renderer/browser_plugin/browser_plugin_channel_manager.h @@ -0,0 +1,73 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_CHANNEL_MANAGER_H_ +#define CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_CHANNEL_MANAGER_H_ +#pragma once + +#include + +#include "base/id_map.h" +#include "content/public/renderer/render_process_observer.h" +#include "content/renderer/browser_plugin/guest_to_embedder_channel.h" +#include "content/renderer/render_view_impl.h" + +class GuestToEmbedderChannel; +struct ViewMsg_New_Params; + +namespace content { + +// BrowserPluginChannelManager manages the lifetime of GuestToEmbedderChannels. +// When a new RenderView is requested, it checks the embedder channel name +// in ViewMsg_New_Params and decides whether to reuse an existing channel or +// create a new channel. On the guest renderer process, it informs a +// RenderView once a channel has been established with its embedder. +// On the embedder side, it tells BrowserPlugin to load the guest +// PluginInstance once a channel has been established. +class BrowserPluginChannelManager + : public RenderProcessObserver { + public: + BrowserPluginChannelManager(); + + virtual ~BrowserPluginChannelManager(); + + void CreateRenderView(const ViewMsg_New_Params& params); + + // Get the GuestToEmbedderChannel associated with the given + // embedder_channel_name. + GuestToEmbedderChannel* GetChannelByName( + const std::string& embedder_channel_name); + + // Remove the pointer to the GuestToEmbedderChannel associated with the given + // routing_id. + void RemoveChannelByName(const std::string& embedder_channel_name); + + private: + typedef std::map > + EmbedderChannelNameToChannelMap; + + void OnCompleteNavigation(int guest_routing_id, + PP_Instance instance); + + void OnLoadGuest(int instance_id, + int guest_renderer_id, + const IPC::ChannelHandle& channel_handle); + + // RenderProcessObserver override. Call on render thread. + virtual bool OnControlMessageReceived(const IPC::Message& message) OVERRIDE; + + // Map from Host process ID to GuestToEmbedderChannel + EmbedderChannelNameToChannelMap embedder_channels_; + + // Map from Routing ID to RenderViewImpl that points to RenderViewImpl + // guests that have been constructed but don't have a PP_Instance and + // so they aren't yet ready to composite. + std::map > pending_guests_; + + DISALLOW_COPY_AND_ASSIGN(BrowserPluginChannelManager); +}; + +} // namespace content + +#endif // CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_CHANNEL_MANAGER_H_ diff --git a/content/renderer/browser_plugin/browser_plugin_placeholder.cc b/content/renderer/browser_plugin/browser_plugin_placeholder.cc deleted file mode 100644 index 21ac8ee27680cf..00000000000000 --- a/content/renderer/browser_plugin/browser_plugin_placeholder.cc +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright (c) 2012 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 "content/renderer/browser_plugin/browser_plugin_placeholder.h" - -#include "base/atomic_sequence_num.h" -#include "base/id_map.h" -#include "base/lazy_instance.h" -#include "base/process.h" -#include "base/string_number_conversions.h" -#include "base/string_piece.h" -#include "base/string_util.h" -#include "base/values.h" -#include "content/common/view_messages.h" -#include "content/public/renderer/render_view.h" -#include "ipc/ipc_channel_handle.h" -#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" -#include "third_party/WebKit/Source/WebKit/chromium/public/WebPlugin.h" -#include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginContainer.h" -#include "webkit/plugins/webview_plugin.h" - -static base::StaticAtomicSequenceNumber g_next_id; - -// The global list of all Browser Plugin Placeholders within a process. -base::LazyInstance >::Leaky - g_all_placeholders = LAZY_INSTANCE_INITIALIZER; - -using webkit::WebViewPlugin; -using WebKit::WebPlugin; -using WebKit::WebPluginContainer; - -const char* const kPluginPlaceholderDataURL = - "about:blank"; - -// static -webkit::WebViewPlugin* BrowserPluginPlaceholder::Create( - content::RenderView* render_view, - WebKit::WebFrame* frame, - const WebKit::WebPluginParams& params) { - // TODO(fsamuel): Need a better loading screen at some point. Maybe this can - // be a part of the API? - // |browser_plugin| will destroy itself when its WebViewPlugin is going away. - BrowserPluginPlaceholder* browser_plugin = new BrowserPluginPlaceholder( - render_view, frame, params, ""); - return browser_plugin->plugin(); -} - -// static -BrowserPluginPlaceholder* BrowserPluginPlaceholder::FromID(int id) { - return g_all_placeholders.Get().Lookup(id); -} - -BrowserPluginPlaceholder::BrowserPluginPlaceholder( - content::RenderView* render_view, - WebKit::WebFrame* frame, - const WebKit::WebPluginParams& params, - const std::string& html_data) - : render_view_(render_view), - plugin_params_(params), - plugin_(webkit::WebViewPlugin::Create( - this, render_view->GetWebkitPreferences(), html_data, - GURL(kPluginPlaceholderDataURL))) { - id_ = g_next_id.GetNext(); - RegisterPlaceholder(GetID(), this); - - // By default we navigate to google.com - GetPluginParameters(0, 0, "http://www.google.com/"); - - // TODO(fsamuel): Request a browser plugin instance from the - // browser process. -} - -BrowserPluginPlaceholder::~BrowserPluginPlaceholder() { - UnregisterPlaceholder(GetID()); -} - -void BrowserPluginPlaceholder::RegisterPlaceholder( - int id, - BrowserPluginPlaceholder* placeholder) { - g_all_placeholders.Get().AddWithID(placeholder, id); -} - -void BrowserPluginPlaceholder::UnregisterPlaceholder(int id) { - if (g_all_placeholders.Get().Lookup(id)) - g_all_placeholders.Get().Remove(id); -} - -const WebKit::WebPluginParams& BrowserPluginPlaceholder::plugin_params() const { - return plugin_params_; -} - -void BrowserPluginPlaceholder::GetPluginParameters( - int default_width, int default_height, - const std::string& default_src) { - int width = default_width; - int height = default_height; - - // Get the plugin parameters from the attributes vector - for (unsigned i = 0; i < plugin_params_.attributeNames.size(); ++i) { - std::string attributeName = plugin_params_.attributeNames[i].utf8(); - if (LowerCaseEqualsASCII(attributeName, "width")) { - std::string attributeValue = plugin_params_.attributeValues[i].utf8(); - CHECK(base::StringToInt(attributeValue, &width)); - } else if (LowerCaseEqualsASCII(attributeName, "height")) { - std::string attributeValue = plugin_params_.attributeValues[i].utf8(); - CHECK(base::StringToInt(attributeValue, &height)); - } else if (LowerCaseEqualsASCII(attributeName, "src")) { - src_ = plugin_params_.attributeValues[i].utf8(); - } - } - // If we didn't find the attributes set or they're not sensible, - // we reset our attributes to the default. - if (src_.empty()) - src_ = default_src; - - size_.SetSize(width, height); -} - -void BrowserPluginPlaceholder::GuestReady( - base::ProcessHandle process_handle, - const IPC::ChannelHandle& channel_handle) { - // TODO(fsamuel): Once the guest renderer is ready, - // it will inform the host renderer and the BrowserPluginPlaceholder - // can swap itself out with the guest. - NOTIMPLEMENTED(); -} - -void BrowserPluginPlaceholder::LoadGuest(WebKit::WebPlugin* new_plugin) { - WebKit::WebPluginContainer* container = plugin_->container(); - if (!new_plugin || !new_plugin->initialize(container)) - return; - plugin_->RestoreTitleText(); - container->setPlugin(new_plugin); - container->invalidate(); - container->reportGeometry(); - plugin_->ReplayReceivedData(new_plugin); - plugin_->destroy(); -} - -void BrowserPluginPlaceholder::WillDestroyPlugin() { - delete this; -} diff --git a/content/renderer/browser_plugin/browser_plugin_placeholder.h b/content/renderer/browser_plugin/browser_plugin_placeholder.h deleted file mode 100644 index b7c229d6d5bad2..00000000000000 --- a/content/renderer/browser_plugin/browser_plugin_placeholder.h +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (c) 2012 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 CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_PLACEHOLDER_H_ -#define CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_PLACEHOLDER_H_ -#pragma once - -#include "base/process.h" -#include "ipc/ipc_channel_handle.h" -#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h" -#include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginParams.h" -#include "ui/gfx/size.h" -#include "webkit/plugins/webview_plugin.h" - -namespace content { -class RenderView; -} - -namespace WebKit { -class WebPlugin; -} - -// A browser plugin is a plugin container that hosts an out-of-process "guest" -// RenderView. Loading up a new process, creating a new RenderView, navigating -// to a given URL, and establishing a guest-to-host channel can take hundreds -// of milliseconds. Furthermore, a RenderView's associated browser-side -// WebContents, RenderViewHost, and SiteInstance must be created and accessed on -// the UI thread of the browser process. -// -// To avoid blocking the host RenderView and to avoid introducing the potential -// for deadlock, the BrowserPluginPlaceholder takes place of the guest -// RenderView until the guest has established a connection with its host -// RenderView. This permits loading the guest to happen asynchronously, while -// the host RenderView is permitted to continue to receive and process events. -class BrowserPluginPlaceholder: public webkit::WebViewPlugin::Delegate { - public: - // Creates a new WebViewPlugin with a BrowserPluginPlaceholder as a delegate. - static webkit::WebViewPlugin* Create( - content::RenderView* render_view, - WebKit::WebFrame* frame, - const WebKit::WebPluginParams& params); - - static BrowserPluginPlaceholder* FromID(int id); - - void RegisterPlaceholder(int id, BrowserPluginPlaceholder* placeholder); - void UnregisterPlaceholder(int id); - - int GetID() { return id_; } - - webkit::WebViewPlugin* plugin() { return plugin_; } - - const WebKit::WebPluginParams& plugin_params() const; - - void GuestReady(base::ProcessHandle process_handle, - const IPC::ChannelHandle& channel_handle); - - content::RenderView* render_view() { return render_view_; } - - private: - BrowserPluginPlaceholder(content::RenderView* render_view, - WebKit::WebFrame* frame, - const WebKit::WebPluginParams& params, - const std::string& html_data); - virtual ~BrowserPluginPlaceholder(); - - // Grabs the width, height, and source URL of the browser plugin - // from the element's attributes. If not found, it uses the defaults - // specified here as parameters. - void GetPluginParameters(int default_width, int default_height, - const std::string& default_src); - // Replace this placeholder with the real browser plugin. - void LoadGuest(WebKit::WebPlugin* new_plugin); - - virtual void BindWebFrame(WebKit::WebFrame* frame) OVERRIDE { } - virtual void WillDestroyPlugin() OVERRIDE; - virtual void ShowContextMenu(const WebKit::WebMouseEvent&) OVERRIDE { } - - content::RenderView* render_view_; - WebKit::WebPluginParams plugin_params_; - webkit::WebViewPlugin* plugin_; - int id_; - gfx::Size size_; - std::string src_; - - DISALLOW_COPY_AND_ASSIGN(BrowserPluginPlaceholder); -}; - -#endif // CONTNET_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_PLACEHOLDER_H_ diff --git a/content/renderer/browser_plugin/browser_plugin_registry.cc b/content/renderer/browser_plugin/browser_plugin_registry.cc new file mode 100644 index 00000000000000..14725e716ed6a2 --- /dev/null +++ b/content/renderer/browser_plugin/browser_plugin_registry.cc @@ -0,0 +1,45 @@ +// Copyright (c) 2012 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 "content/renderer/browser_plugin/browser_plugin_registry.h" + +#include "base/logging.h" + +namespace content { + +BrowserPluginRegistry::BrowserPluginRegistry() { +} + +BrowserPluginRegistry::~BrowserPluginRegistry() { +} + +webkit::ppapi::PluginModule* BrowserPluginRegistry::GetModule( + int guest_process_id) { + return modules_.Lookup(guest_process_id); +} + +void BrowserPluginRegistry::AddModule(int guest_process_id, + webkit::ppapi::PluginModule* module) { + modules_.AddWithID(module, guest_process_id); +} + +void BrowserPluginRegistry::PluginModuleDead( + webkit::ppapi::PluginModule* dead_module) { + // DANGER: Don't dereference the dead_module pointer! It may be in the + // process of being deleted. + + // Modules aren't destroyed very often and there are normally at most a + // couple of them. So for now we just do a brute-force search. + IDMap::iterator iter(&modules_); + while (!iter.IsAtEnd()) { + if (iter.GetCurrentValue() == dead_module) { + modules_.Remove(iter.GetCurrentKey()); + return; + } + iter.Advance(); + } + NOTREACHED(); // Should have always found the module above. +} + +} // namespace content diff --git a/content/renderer/browser_plugin/browser_plugin_registry.h b/content/renderer/browser_plugin/browser_plugin_registry.h new file mode 100644 index 00000000000000..7740015fd89a76 --- /dev/null +++ b/content/renderer/browser_plugin/browser_plugin_registry.h @@ -0,0 +1,37 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_REGISTRY_H_ +#define CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_REGISTRY_H_ +#pragma once + +#include "base/id_map.h" +#include "base/process.h" +#include "webkit/plugins/ppapi/plugin_delegate.h" + +namespace content { + +// This class holds references to all of the known live browser plugin +// modules. There is one browser plugin module per guest renderer process. +class BrowserPluginRegistry + : public webkit::ppapi::PluginDelegate::ModuleLifetime { + public: + BrowserPluginRegistry(); + virtual ~BrowserPluginRegistry(); + + webkit::ppapi::PluginModule* GetModule(int guest_process_id); + void AddModule(int guest_process_id, + webkit::ppapi::PluginModule* module); + + // ModuleLifetime implementation. + virtual void PluginModuleDead( + webkit::ppapi::PluginModule* dead_module) OVERRIDE; + + private: + IDMap modules_; +}; + +} // namespace content + +#endif // CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_REGISTRY_H_ diff --git a/content/renderer/browser_plugin/browser_plugin_var_serialization_rules.cc b/content/renderer/browser_plugin/browser_plugin_var_serialization_rules.cc new file mode 100644 index 00000000000000..874527c3224d90 --- /dev/null +++ b/content/renderer/browser_plugin/browser_plugin_var_serialization_rules.cc @@ -0,0 +1,51 @@ +// Copyright (c) 2012 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 "content/renderer/browser_plugin/browser_plugin_var_serialization_rules.h" + +#include "base/logging.h" + +namespace content { + +BrowserPluginVarSerializationRules::BrowserPluginVarSerializationRules() { +} + +BrowserPluginVarSerializationRules::~BrowserPluginVarSerializationRules() { +} + +PP_Var BrowserPluginVarSerializationRules::SendCallerOwned(const PP_Var& var) { + DCHECK(var.type != PP_VARTYPE_OBJECT); + return var; +} + +PP_Var BrowserPluginVarSerializationRules::BeginReceiveCallerOwned( + const PP_Var& var) { + DCHECK(var.type != PP_VARTYPE_OBJECT); + return var; +} + +void BrowserPluginVarSerializationRules::EndReceiveCallerOwned( + const PP_Var& var) { + DCHECK(var.type != PP_VARTYPE_OBJECT); +} + +PP_Var BrowserPluginVarSerializationRules::ReceivePassRef(const PP_Var& var) { + DCHECK(var.type != PP_VARTYPE_OBJECT); + return var; +} + +PP_Var BrowserPluginVarSerializationRules::BeginSendPassRef(const PP_Var& var) { + DCHECK(var.type != PP_VARTYPE_OBJECT); + return var; +} + +void BrowserPluginVarSerializationRules::EndSendPassRef(const PP_Var& var) { + DCHECK(var.type != PP_VARTYPE_OBJECT); +} + +void BrowserPluginVarSerializationRules::ReleaseObjectRef(const PP_Var& var) { + DCHECK(var.type != PP_VARTYPE_OBJECT); +} + +} // namespace content diff --git a/content/renderer/browser_plugin/browser_plugin_var_serialization_rules.h b/content/renderer/browser_plugin/browser_plugin_var_serialization_rules.h new file mode 100644 index 00000000000000..f169912d9e1e23 --- /dev/null +++ b/content/renderer/browser_plugin/browser_plugin_var_serialization_rules.h @@ -0,0 +1,35 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_VAR_SERIALIZATION_RULES_H_ +#define CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_VAR_SERIALIZATION_RULES_H_ + +#include "ppapi/proxy/var_serialization_rules.h" + +namespace content { + +// Implementation of the VarSerializationRules interface for the browser plugin. +class BrowserPluginVarSerializationRules : + public ppapi::proxy::VarSerializationRules { + public: + explicit BrowserPluginVarSerializationRules(); + + // VarSerialization implementation. + virtual PP_Var SendCallerOwned(const PP_Var& var) OVERRIDE; + virtual PP_Var BeginReceiveCallerOwned(const PP_Var& var) OVERRIDE; + virtual void EndReceiveCallerOwned(const PP_Var& var) OVERRIDE; + virtual PP_Var ReceivePassRef(const PP_Var& var) OVERRIDE; + virtual PP_Var BeginSendPassRef(const PP_Var& var) OVERRIDE; + virtual void EndSendPassRef(const PP_Var& var) OVERRIDE; + virtual void ReleaseObjectRef(const PP_Var& var) OVERRIDE; + + private: + virtual ~BrowserPluginVarSerializationRules(); + + DISALLOW_COPY_AND_ASSIGN(BrowserPluginVarSerializationRules); +}; + +} // namespace content + +#endif // CONTENT_RENDERER_BROWSER_PLUGIN_BROWSER_PLUGIN_VAR_SERIALIZATION_RULES_H_ diff --git a/content/renderer/browser_plugin/guest_to_embedder_channel.cc b/content/renderer/browser_plugin/guest_to_embedder_channel.cc new file mode 100644 index 00000000000000..1f9f6a78b5823b --- /dev/null +++ b/content/renderer/browser_plugin/guest_to_embedder_channel.cc @@ -0,0 +1,280 @@ +// Copyright (c) 2012 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 "content/renderer/browser_plugin/guest_to_embedder_channel.h" + +#include "base/process_util.h" +#include "content/common/browser_plugin_messages.h" +#include "content/common/child_process.h" +#include "content/renderer/browser_plugin/browser_plugin_channel_manager.h" +#include "content/renderer/browser_plugin/browser_plugin_var_serialization_rules.h" +#include "content/renderer/render_thread_impl.h" +#include "content/renderer/render_view_impl.h" +#include "ppapi/c/pp_bool.h" +#include "ppapi/c/pp_graphics_3d.h" +#include "ppapi/proxy/ppapi_command_buffer_proxy.h" +#include "ppapi/proxy/ppapi_messages.h" +#include "ppapi/shared_impl/api_id.h" +#include "ppapi/shared_impl/ppapi_globals.h" +#include "ppapi/shared_impl/var.h" +#include "webkit/plugins/ppapi/event_conversion.h" + +namespace content { + +GuestToEmbedderChannel::GuestToEmbedderChannel( + const std::string& embedder_channel_name) + : Dispatcher(NULL), + embedder_channel_name_(embedder_channel_name) { + SetSerializationRules(new BrowserPluginVarSerializationRules()); +} + +GuestToEmbedderChannel::~GuestToEmbedderChannel() { +} + +bool GuestToEmbedderChannel::OnMessageReceived(const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(GuestToEmbedderChannel, message) + IPC_MESSAGE_HANDLER(PpapiMsg_SupportsInterface, OnSupportsInterface) + IPC_MESSAGE_HANDLER(PpapiMsg_SetPreferences, OnSetPreferences) + IPC_MESSAGE_HANDLER(PpapiMsg_ReserveInstanceId, OnReserveInstanceId) + IPC_MESSAGE_HANDLER(PpapiMsg_PPPInstance_DidCreate, + OnDidCreate) + IPC_MESSAGE_HANDLER(PpapiMsg_PPPInstance_DidDestroy, + OnDidDestroy) + IPC_MESSAGE_HANDLER(PpapiMsg_PPPInstance_DidChangeView, + OnDidChangeView) + IPC_MESSAGE_HANDLER(PpapiMsg_PPPInstance_DidChangeFocus, + OnDidChangeFocus) + IPC_MESSAGE_HANDLER(PpapiMsg_PPPMessaging_HandleMessage, + OnHandleMessage) + IPC_MESSAGE_HANDLER(PpapiMsg_PPPInputEvent_HandleFilteredInputEvent, + OnHandleFilteredInputEvent) + IPC_MESSAGE_HANDLER(PpapiMsg_PPPGraphics3D_ContextLost, + OnContextLost) + // Have the super handle all other messages. + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + + return handled; +} + +bool GuestToEmbedderChannel::Send(IPC::Message* message) { + // We always want guest->host messages to arrive in-order. If some sync + // and some async messages are sent in response to a synchronous + // host->guest call, the sync reply will be processed before the async + // reply, and everything will be confused. + // + // Allowing all async messages to unblock the renderer means more reentrancy + // there but gives correct ordering. + message->set_unblock(true); + return ProxyChannel::Send(message); +} + +void GuestToEmbedderChannel::OnChannelError() { + // We cannot destroy the GuestToEmbedderChannel here because a + // PpapiCommandBufferProxy may still refer to this object. + // However, we should not be using this channel again once we get a + // channel error so we remove it from the channel manager. + RenderThreadImpl::current()->browser_plugin_channel_manager()-> + RemoveChannelByName(embedder_channel_name_); +} + +bool GuestToEmbedderChannel::IsPlugin() const { + return true; +} + +WebGraphicsContext3DCommandBufferImpl* + GuestToEmbedderChannel::CreateWebGraphicsContext3D( + RenderViewImpl* render_view, + const WebKit::WebGraphicsContext3D::Attributes& attributes, + bool offscreen) { + scoped_ptr context( + new WebGraphicsContext3DCommandBufferImpl( + 0, GURL(), NULL, + render_view->AsWeakPtr())); + + // Special case: RenderView initialization has not yet completed. + if (!render_view->guest_pp_instance()) + return context.release(); + + if (CreateGraphicsContext(context.get(), + attributes, + offscreen, + render_view)) + return context.release(); + + return NULL; +} + +void GuestToEmbedderChannel::IssueSwapBuffers( + const ppapi::HostResource& resource) { + Send(new PpapiHostMsg_PPBGraphics3D_SwapBuffers( + ppapi::API_ID_PPB_GRAPHICS_3D, resource)); +} + +bool GuestToEmbedderChannel::InitChannel( + const IPC::ChannelHandle& channel_handle) { + return ProxyChannel::InitWithChannel(&delegate_, channel_handle, false); +} + +void GuestToEmbedderChannel::OnSupportsInterface( + const std::string& interface_name, + bool* result) { + // TODO(fsamuel): This is a hack to avoid getting GetInstanceObject messages + // and failing a CHECK. A more correct solution is to implement + // VarSerializationRules for GuestToEmbedderChannel. + *result = interface_name.find("PPP_Instance_Private") == std::string::npos; +} + +void GuestToEmbedderChannel::OnSetPreferences(const ppapi::Preferences& prefs) { + // TODO(fsamuel): Do we care about these preferences? + // These look like some font stuff from WebPreferences. + // Perhaps this should be plumbed into our associated RenderView? + NOTIMPLEMENTED(); +} + +void GuestToEmbedderChannel::OnReserveInstanceId(PP_Instance instance, + bool* usable) { + *usable = + render_view_instances_.find(instance) == render_view_instances_.end(); +} + +void GuestToEmbedderChannel::RequestInputEvents(PP_Instance instance) { + // Request receipt of input events + Send(new PpapiHostMsg_PPBInstance_RequestInputEvents( + ppapi::API_ID_PPB_INSTANCE, instance, true, + PP_INPUTEVENT_CLASS_MOUSE | + PP_INPUTEVENT_CLASS_KEYBOARD | + PP_INPUTEVENT_CLASS_WHEEL | + PP_INPUTEVENT_CLASS_TOUCH)); +} + +bool GuestToEmbedderChannel::CreateGraphicsContext( + WebGraphicsContext3DCommandBufferImpl* context, + const WebKit::WebGraphicsContext3D::Attributes& attributes, + bool offscreen, + RenderViewImpl* render_view) { + std::vector attribs; + attribs.push_back(PP_GRAPHICS3DATTRIB_NONE); + + ppapi::HostResource resource; + DCHECK(render_view->guest_pp_instance()); + // TODO(fsamuel): Support child contexts. + bool success = Send(new PpapiHostMsg_PPBGraphics3D_Create( + ppapi::API_ID_PPB_GRAPHICS_3D, + render_view->guest_pp_instance(), + attribs, + &resource)); + if (!success || resource.is_null()) + return false; + if (!offscreen) { + PP_Bool result = PP_FALSE; + Send(new PpapiHostMsg_PPBInstance_BindGraphics( + ppapi::API_ID_PPB_INSTANCE, + render_view->guest_pp_instance(), + resource, + &result)); + if (result != PP_TRUE) + return false; + } + + CommandBufferProxy* command_buffer = + new ppapi::proxy::PpapiCommandBufferProxy(resource, this); + command_buffer->Initialize(); + context->InitializeWithCommandBuffer( + command_buffer, + attributes, + false /* bind generates resources */); + render_view->set_guest_graphics_resource(resource); + return true; +} + +void GuestToEmbedderChannel::AddGuest( + PP_Instance instance, + RenderViewImpl* render_view) { + DCHECK(instance); + DCHECK(render_view_instances_.find(instance) == render_view_instances_.end()); + render_view_instances_[instance] = render_view->AsWeakPtr(); +} + + +void GuestToEmbedderChannel::RemoveGuest(PP_Instance instance) { + DCHECK(render_view_instances_.find(instance) != render_view_instances_.end()); + render_view_instances_.erase(instance); +} + +void GuestToEmbedderChannel::OnDidCreate(PP_Instance instance, + const std::vector& argn, + const std::vector& argv, + PP_Bool* result) { + *result = PP_TRUE; +} + +void GuestToEmbedderChannel::OnDidDestroy(PP_Instance instance) { + RemoveGuest(instance); +} + +void GuestToEmbedderChannel::OnDidChangeView( + PP_Instance instance, + const ppapi::ViewData& new_data, + PP_Bool flash_fullscreen) { + // We can't do anything with this message if we don't have a render view + // yet. If we do have a RenderView then we need to tell the associated + // WebContentsObserver to resize. + if (render_view_instances_.find(instance) != render_view_instances_.end()) { + RenderViewImpl* render_view = render_view_instances_[instance]; + render_view->Send( + new BrowserPluginHostMsg_ResizeGuest( + render_view->GetRoutingID(), + new_data.rect.size.width, + new_data.rect.size.height)); + } +} + +void GuestToEmbedderChannel::OnDidChangeFocus(PP_Instance instance, + PP_Bool has_focus) { + NOTIMPLEMENTED(); +} + +void GuestToEmbedderChannel::OnHandleMessage( + PP_Instance instance, + ppapi::proxy::SerializedVarReceiveInput message_data) { + InstanceMap::iterator it = render_view_instances_.find(instance); + if (it == render_view_instances_.end()) + return; + + PP_Var received_var(message_data.Get(this)); + DCHECK(received_var.type == PP_VARTYPE_STRING); + ppapi::VarTracker* tracker = ppapi::PpapiGlobals::Get()->GetVarTracker(); + ppapi::StringVar* var = tracker->GetVar(received_var)->AsStringVar(); + DCHECK(var); + + RenderViewImpl* render_view = it->second; + render_view->Send( + new BrowserPluginHostMsg_NavigateFromGuest( + render_view->GetRoutingID(), + instance, + var->value())); +} + +void GuestToEmbedderChannel::OnHandleFilteredInputEvent( + PP_Instance instance, + const ppapi::InputEventData& data, + PP_Bool* result) { + DCHECK(render_view_instances_.find(instance) != render_view_instances_.end()); + + RenderViewImpl* render_view = render_view_instances_[instance]; + scoped_ptr web_input_event( + webkit::ppapi::CreateWebInputEvent(data)); + *result = PP_FromBool( + render_view->GetWebView()->handleInputEvent(*web_input_event)); +} + +void GuestToEmbedderChannel::OnContextLost(PP_Instance instance) { + DCHECK(render_view_instances_.find(instance) != render_view_instances_.end()); + RenderViewImpl* render_view = render_view_instances_[instance]; + render_view->GetWebView()->loseCompositorContext(1); +} + +} // namespace content diff --git a/content/renderer/browser_plugin/guest_to_embedder_channel.h b/content/renderer/browser_plugin/guest_to_embedder_channel.h new file mode 100644 index 00000000000000..7d066be334a7b6 --- /dev/null +++ b/content/renderer/browser_plugin/guest_to_embedder_channel.h @@ -0,0 +1,126 @@ +// Copyright (c) 2012 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 CONTENT_RENDERER_BROWSER_PLUGIN_GUEST_TO_EMBEDDER_CHANNEL_H_ +#define CONTENT_RENDERER_BROWSER_PLUGIN_GUEST_TO_EMBEDDER_CHANNEL_H_ +#pragma once + +#include "base/memory/ref_counted.h" +#include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h" +#include "content/renderer/pepper/pepper_proxy_channel_delegate_impl.h" +#include "content/renderer/render_view_impl.h" +#include "ipc/ipc_channel_handle.h" +#include "ppapi/c/pp_bool.h" +#include "ppapi/c/pp_instance.h" +#include "ppapi/shared_impl/ppb_view_shared.h" +#include "ppapi/proxy/plugin_dispatcher.h" +#include "ppapi/proxy/serialized_var.h" +#include "ppapi/shared_impl/host_resource.h" +#include "ppapi/shared_impl/ppapi_preferences.h" +#include "ppapi/shared_impl/ppb_input_event_shared.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebGraphicsContext3D.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" + +namespace content { + +class BrowserPluginChannelManager; + +// GuestToEmbedderChannel is a Dispatcher that sends and receives ppapi messages +// from the browser plugin embedder. It is reference counted because it is held +// by a RenderViewImpl which (indirectly) owns a PpapiCommandBufferProxy through +// a WebGraphicsContext3DCommandBufferImpl which is owned by WebKit. Since the +// lifetime of this context is less than the lifetime of the RenderViewImpl, we +// keep the GuestToEmbedderChannel alive as long as a RenderViewImpl has access +// to it. If the context is lost, then the PpapiCommandBufferProxy is destroyed +// and we can safely release the reference to this GuestToEmbedderChannel held +// by RenderViewImpl. +class GuestToEmbedderChannel + : public ppapi::proxy::Dispatcher, + public base::RefCounted { + public: + explicit GuestToEmbedderChannel(const std::string& embedder_channel_manager); + + // This must be called before anything else. Returns true on success. + bool InitChannel(const IPC::ChannelHandle& channel_handle); + + // Creates a new WebGraphicsContext3DCommandBufferImpl and returns it. + WebGraphicsContext3DCommandBufferImpl* CreateWebGraphicsContext3D( + RenderViewImpl* render_view, + const WebKit::WebGraphicsContext3D::Attributes& attributes, + bool offscreen); + + // Inform the host to invalidate its plugin container after a swap buffer. + void IssueSwapBuffers(const ppapi::HostResource& resource); + + // Request the receipt of events from the embedder renderer. + void RequestInputEvents(PP_Instance instance); + + // Request a graphics context from the embedder renderer. + bool CreateGraphicsContext( + WebGraphicsContext3DCommandBufferImpl* context, + const WebKit::WebGraphicsContext3D::Attributes& attributes, + bool offscreen, + RenderViewImpl* render_view); + + // Register the given RenderView with the given PP_Instance. + void AddGuest(PP_Instance instance, RenderViewImpl* render_view); + + // Removes the guest with the given instance identifier from the + // InstanceMap. + void RemoveGuest(PP_Instance instance); + + // ppapi::proxy::Dispatcher implementation. + virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE; + virtual bool Send(IPC::Message* message) OVERRIDE; + virtual void OnChannelError() OVERRIDE; + virtual bool IsPlugin() const OVERRIDE; + + private: + friend class base::RefCounted; + typedef std::map > InstanceMap; + typedef std::map RoutingIDToInstanceMap; + + virtual ~GuestToEmbedderChannel(); + + void OnSupportsInterface(const std::string& interface_name, bool* result); + void OnSetPreferences(const ppapi::Preferences& prefs); + void OnReserveInstanceId(PP_Instance instance, bool* usable); + void OnDidCreate(PP_Instance instance, + const std::vector& argn, + const std::vector& argv, + PP_Bool* result); + void OnDidDestroy(PP_Instance instance); + void OnDidChangeView(PP_Instance instance, + const ppapi::ViewData& new_data, + PP_Bool flash_fullscreen); + void OnDidChangeFocus(PP_Instance instance, PP_Bool has_focus); + void OnGetInstanceObject(PP_Instance instance, + ppapi::proxy::SerializedVarReturnValue result); + + void OnHandleMessage(PP_Instance instance, + ppapi::proxy::SerializedVarReceiveInput data); + + void OnHandleFilteredInputEvent(PP_Instance instance, + const ppapi::InputEventData& data, + PP_Bool* result); + + void OnSwapBuffersACK(const ppapi::HostResource& context, + int32_t pp_error); + + void OnContextLost(PP_Instance instance); + + base::WeakPtr render_view_; + BrowserPluginChannelManager* channel_manager_; + std::string embedder_channel_name_; + PepperProxyChannelDelegateImpl delegate_; + + InstanceMap render_view_instances_; + RoutingIDToInstanceMap routing_id_instance_map_; + + DISALLOW_COPY_AND_ASSIGN(GuestToEmbedderChannel); +}; + +} // namespace content + +#endif // CONTENT_RENDERER_BROWSER_PLUGIN_GUEST_TO_EMBEDDER_CHANNEL_H_ diff --git a/content/renderer/pepper/pepper_plugin_delegate_impl.cc b/content/renderer/pepper/pepper_plugin_delegate_impl.cc index 67a42c8298aa36..999bda3e037341 100644 --- a/content/renderer/pepper/pepper_plugin_delegate_impl.cc +++ b/content/renderer/pepper/pepper_plugin_delegate_impl.cc @@ -34,6 +34,8 @@ #include "content/public/common/media_stream_request.h" #include "content/public/common/referrer.h" #include "content/public/renderer/content_renderer_client.h" +#include "content/renderer/browser_plugin/browser_plugin_constants.h" +#include "content/renderer/browser_plugin/browser_plugin_registry.h" #include "content/renderer/gamepad_shared_memory_reader.h" #include "content/renderer/media/audio_hardware.h" #include "content/renderer/media/media_stream_dispatcher.h" @@ -265,6 +267,40 @@ PepperPluginDelegateImpl::CreatePepperPluginModule( return module; } +scoped_refptr + PepperPluginDelegateImpl::CreateBrowserPluginModule( + const IPC::ChannelHandle& channel_handle, + int guest_process_id) { + BrowserPluginRegistry* registry = + RenderThreadImpl::current()->browser_plugin_registry(); + scoped_refptr module = + registry->GetModule(guest_process_id); + if (module) + return module; + + scoped_refptr hung_filter( + new PepperHungPluginFilter(FilePath(kBrowserPluginPath), + render_view_->routing_id(), + guest_process_id)); + // Create a new HostDispatcher for the proxying, and hook it to a new + // PluginModule. + module = new webkit::ppapi::PluginModule(kBrowserPluginName, + FilePath(kBrowserPluginPath), + registry); + RenderThreadImpl::current()->browser_plugin_registry()->AddModule( + guest_process_id, module); + scoped_ptr dispatcher(new HostDispatcherWrapper); + if (!dispatcher->Init( + channel_handle, + module->pp_module(), + webkit::ppapi::PluginModule::GetLocalGetInterfaceFunc(), + GetPreferences(), + hung_filter.get())) + return scoped_refptr(); + module->InitAsProxied(dispatcher.release()); + return module; +} + scoped_refptr PepperPluginDelegateImpl::CreateBroker( webkit::ppapi::PluginModule* plugin_module) { DCHECK(plugin_module); diff --git a/content/renderer/pepper/pepper_plugin_delegate_impl.h b/content/renderer/pepper/pepper_plugin_delegate_impl.h index 43f20fd18d158f..54ac84c79fbe02 100644 --- a/content/renderer/pepper/pepper_plugin_delegate_impl.h +++ b/content/renderer/pepper/pepper_plugin_delegate_impl.h @@ -97,6 +97,13 @@ class PepperPluginDelegateImpl const webkit::WebPluginInfo& webplugin_info, bool* pepper_plugin_was_registered); + // Creates a browser plugin instance given the process handle, and channel + // handle to access the guest renderer. + // If the plugin fails to initialize then return NULL. + scoped_refptr CreateBrowserPluginModule( + const IPC::ChannelHandle& channel_handle, + int guest_process_id); + // Called by RenderView to tell us about painting events, these two functions // just correspond to the WillInitiatePaint, DidInitiatePaint and // DidFlushPaint hooks in RenderView. diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc index f7125d737e93dc..fe720931206036 100644 --- a/content/renderer/render_thread_impl.cc +++ b/content/renderer/render_thread_impl.cc @@ -46,6 +46,8 @@ #include "content/public/renderer/content_renderer_client.h" #include "content/public/renderer/render_process_observer.h" #include "content/public/renderer/render_view_visitor.h" +#include "content/renderer/browser_plugin/browser_plugin_channel_manager.h" +#include "content/renderer/browser_plugin/browser_plugin_registry.h" #include "content/renderer/devtools_agent_filter.h" #include "content/renderer/dom_storage/dom_storage_dispatcher.h" #include "content/renderer/dom_storage/webstoragearea_impl.h" @@ -212,6 +214,11 @@ void RenderThreadImpl::Init() { dom_storage_dispatcher_.reset(new DomStorageDispatcher()); main_thread_indexed_db_dispatcher_.reset(new IndexedDBDispatcher()); + browser_plugin_registry_.reset(new content::BrowserPluginRegistry()); + browser_plugin_channel_manager_.reset( + new content::BrowserPluginChannelManager()); + AddObserver(browser_plugin_channel_manager_.get()); + media_stream_center_ = NULL; db_message_filter_ = new DBMessageFilter(); @@ -869,22 +876,26 @@ void RenderThreadImpl::OnSetCSSColors( void RenderThreadImpl::OnCreateNewView(const ViewMsg_New_Params& params) { EnsureWebKitInitialized(); // When bringing in render_view, also bring in webkit's glue and jsbindings. - RenderViewImpl::Create( - params.parent_window, - params.opener_route_id, - params.renderer_preferences, - params.web_preferences, - new SharedRenderViewCounter(0), - params.view_id, - params.surface_id, - params.session_storage_namespace_id, - params.frame_name, - false, - params.swapped_out, - params.next_page_id, - params.screen_info, - false, - params.accessibility_mode); + if (!params.embedder_channel_name.empty()) { + browser_plugin_channel_manager()->CreateRenderView(params); + } else { + RenderViewImpl::Create( + params.parent_window, + params.opener_route_id, + params.renderer_preferences, + params.web_preferences, + new SharedRenderViewCounter(0), + params.view_id, + params.surface_id, + params.session_storage_namespace_id, + params.frame_name, + false, + params.swapped_out, + params.next_page_id, + params.screen_info, + NULL, + params.accessibility_mode); + } } GpuChannelHost* RenderThreadImpl::EstablishGpuChannelSync( diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h index 6cf6454be3d384..699a337508e726 100644 --- a/content/renderer/render_thread_impl.h +++ b/content/renderer/render_thread_impl.h @@ -53,6 +53,8 @@ class ScopedCOMInitializer; } namespace content { +class BrowserPluginChannelManager; +class BrowserPluginRegistry; class MediaStreamCenter; class RenderProcessObserver; } @@ -165,6 +167,14 @@ class CONTENT_EXPORT RenderThreadImpl : public content::RenderThread, return compositor_thread_.get(); } + content::BrowserPluginRegistry* browser_plugin_registry() const { + return browser_plugin_registry_.get(); + } + + content::BrowserPluginChannelManager* browser_plugin_channel_manager() const { + return browser_plugin_channel_manager_.get(); + } + AppCacheDispatcher* appcache_dispatcher() const { return appcache_dispatcher_.get(); } @@ -223,6 +233,8 @@ class CONTENT_EXPORT RenderThreadImpl : public content::RenderThread, scoped_ptr dom_storage_dispatcher_; scoped_ptr main_thread_indexed_db_dispatcher_; scoped_ptr webkit_platform_support_; + scoped_ptr + browser_plugin_channel_manager_; // Used on the render thread and deleted by WebKit at shutdown. content::MediaStreamCenter* media_stream_center_; @@ -274,6 +286,7 @@ class CONTENT_EXPORT RenderThreadImpl : public content::RenderThread, bool compositor_initialized_; scoped_ptr compositor_thread_; + scoped_ptr browser_plugin_registry_; ObserverList observers_; diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc index ad6fef0853fa35..1bb2373787bc25 100644 --- a/content/renderer/render_view_impl.cc +++ b/content/renderer/render_view_impl.cc @@ -55,6 +55,7 @@ #include "content/public/renderer/navigation_state.h" #include "content/public/renderer/render_view_observer.h" #include "content/public/renderer/render_view_visitor.h" +#include "content/renderer/browser_plugin/guest_to_embedder_channel.h" #include "content/renderer/device_orientation_dispatcher.h" #include "content/renderer/devtools_agent.h" #include "content/renderer/dom_automation_controller.h" @@ -78,8 +79,8 @@ #include "content/renderer/notification_provider.h" #include "content/renderer/p2p/socket_dispatcher.h" #include "content/renderer/plugin_channel_host.h" +#include "content/renderer/browser_plugin/browser_plugin.h" #include "content/renderer/browser_plugin/browser_plugin_constants.h" -#include "content/renderer/browser_plugin/browser_plugin_placeholder.h" #include "content/renderer/render_process.h" #include "content/renderer/render_thread_impl.h" #include "content/renderer/render_widget_fullscreen_pepper.h" @@ -496,7 +497,7 @@ RenderViewImpl::RenderViewImpl( bool swapped_out, int32 next_page_id, const WebKit::WebScreenInfo& screen_info, - bool guest, + content::GuestToEmbedderChannel* guest_to_embedder_channel, AccessibilityMode accessibility_mode) : RenderWidget(WebKit::WebPopupTypeNone, screen_info, swapped_out), webkit_preferences_(webkit_prefs), @@ -537,7 +538,9 @@ RenderViewImpl::RenderViewImpl( #if defined(OS_WIN) focused_plugin_id_(-1), #endif - guest_(guest), + guest_to_embedder_channel_(guest_to_embedder_channel), + guest_pp_instance_(0), + guest_uninitialized_context_(NULL), accessibility_mode_(accessibility_mode), ALLOW_THIS_IN_INITIALIZER_LIST(pepper_delegate_(this)) { routing_id_ = routing_id; @@ -575,7 +578,7 @@ RenderViewImpl::RenderViewImpl( // If this is a popup, we must wait for the CreatingNew_ACK message before // completing initialization. Otherwise, we can finish it now. - if (opener_id_ == MSG_ROUTING_NONE) { + if (!guest_to_embedder_channel && opener_id_ == MSG_ROUTING_NONE) { did_show_ = true; CompleteInit(parent_hwnd); } @@ -713,7 +716,7 @@ RenderViewImpl* RenderViewImpl::Create( bool swapped_out, int32 next_page_id, const WebKit::WebScreenInfo& screen_info, - bool guest, + content::GuestToEmbedderChannel* guest_to_embedder_channel, AccessibilityMode accessibility_mode) { DCHECK(routing_id != MSG_ROUTING_NONE); return new RenderViewImpl( @@ -730,7 +733,7 @@ RenderViewImpl* RenderViewImpl::Create( swapped_out, next_page_id, screen_info, - guest, + guest_to_embedder_channel, accessibility_mode); } @@ -1600,7 +1603,7 @@ WebView* RenderViewImpl::createView( false, 1, screen_info_, - guest_, + guest_to_embedder_channel_, accessibility_mode_); view->opened_by_user_gesture_ = params.user_gesture; @@ -1657,6 +1660,18 @@ WebGraphicsContext3D* RenderViewImpl::createGraphicsContext3D( const WebGraphicsContext3D::Attributes& attributes) { if (!webview()) return NULL; + + if (guest_to_embedder_channel()) { + WebGraphicsContext3DCommandBufferImpl* context = + guest_to_embedder_channel()->CreateWebGraphicsContext3D( + this, attributes, false); + if (!guest_pp_instance()) { + guest_uninitialized_context_ = context; + guest_attributes_ = attributes; + } + return context; + } + // The WebGraphicsContext3DInProcessImpl code path is used for // layout tests (though not through this code) as well as for // debugging and bringing up new ports. @@ -2180,14 +2195,10 @@ void RenderViewImpl::didActivateCompositor(int input_handler_identifier) { WebPlugin* RenderViewImpl::createPlugin(WebFrame* frame, const WebPluginParams& params) { - // The browser plugin is a special kind of pepper plugin - // that loads asynchronously. We first create a placeholder here. - // When a guest is ready to be displayed, we swap out the placeholder - // with the guest. const CommandLine& command_line = *CommandLine::ForCurrentProcess(); if (command_line.HasSwitch(switches::kEnableBrowserPlugin) && UTF16ToASCII(params.mimeType) == kBrowserPluginMimeType) - return BrowserPluginPlaceholder::Create(this, frame, params); + return BrowserPlugin::Create(this, frame, params); WebPlugin* plugin = NULL; if (content::GetContentClient()->renderer()->OverrideCreatePlugin( @@ -3698,6 +3709,30 @@ bool RenderViewImpl::IsEditableNode(const WebKit::WebNode& node) const { return is_editable_node; } +void RenderViewImpl::GuestReady(PP_Instance instance) { + guest_pp_instance_ = instance; + if (guest_uninitialized_context_) { + bool success = guest_to_embedder_channel()->CreateGraphicsContext( + guest_uninitialized_context_, + guest_attributes_, + false, + this); + DCHECK(success); + } + CompleteInit(host_window_); +} + +webkit::ppapi::WebPluginImpl* RenderViewImpl::CreateBrowserPlugin( + const IPC::ChannelHandle& channel_handle, + int guest_process_id, + const WebKit::WebPluginParams& params) { + scoped_refptr pepper_module( + pepper_delegate_.CreateBrowserPluginModule(channel_handle, + guest_process_id)); + return new webkit::ppapi::WebPluginImpl( + pepper_module.get(), params, pepper_delegate_.AsWeakPtr()); +} + WebKit::WebPlugin* RenderViewImpl::CreatePlugin( WebKit::WebFrame* frame, const webkit::WebPluginInfo& info, @@ -4791,6 +4826,8 @@ void RenderViewImpl::WillInitiatePaint() { void RenderViewImpl::DidInitiatePaint() { // Notify the pepper plugins that we've painted, and are waiting to flush. pepper_delegate_.ViewInitiatedPaint(); + if (guest_to_embedder_channel()) + guest_to_embedder_channel()->IssueSwapBuffers(guest_graphics_resource()); } void RenderViewImpl::DidFlushPaint() { diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h index 1e1c2bf5bc065b..bc2dc0ac940307 100644 --- a/content/renderer/render_view_impl.h +++ b/content/renderer/render_view_impl.h @@ -92,6 +92,7 @@ class WebUIBindings; namespace content { class DocumentState; +class GuestToEmbedderChannel; class NavigationState; class P2PSocketDispatcher; class RenderViewObserver; @@ -110,6 +111,7 @@ namespace webkit { namespace ppapi { class PluginInstance; +class WebPluginImpl; } // namespace ppapi } // namespace webkit @@ -200,7 +202,7 @@ class RenderViewImpl : public RenderWidget, bool swapped_out, int32 next_page_id, const WebKit::WebScreenInfo& screen_info, - bool guest, + content::GuestToEmbedderChannel* guest_to_embedder_channel, AccessibilityMode accessibility_mode); // Returns the RenderViewImpl containing the given WebView. @@ -261,7 +263,25 @@ class RenderViewImpl : public RenderWidget, // Sets whether the renderer should report load progress to the browser. void SetReportLoadProgressEnabled(bool enabled); - bool guest() const { return guest_; } + content::GuestToEmbedderChannel* + guest_to_embedder_channel() const { return guest_to_embedder_channel_; } + PP_Instance guest_pp_instance() const { return guest_pp_instance_; } + void set_guest_graphics_resource(const ppapi::HostResource& resource) { + guest_graphics_resource_ = resource; + } + const ppapi::HostResource& guest_graphics_resource() const { + return guest_graphics_resource_; + } + + // Once the browser plugin embedder has connected to this guest, and is + // ready to paint, it informs the guest through GuestReady to begin + // compositing. + void GuestReady(PP_Instance instance); + + webkit::ppapi::WebPluginImpl* CreateBrowserPlugin( + const IPC::ChannelHandle& channel_handle, + int guest_process_id, + const WebKit::WebPluginParams& params); void LoadNavigationErrorPage( WebKit::WebFrame* frame, @@ -768,7 +788,7 @@ class RenderViewImpl : public RenderWidget, bool swapped_out, int32 next_page_id, const WebKit::WebScreenInfo& screen_info, - bool guest, + content::GuestToEmbedderChannel* guest_to_embedder_channel, AccessibilityMode accessibility_mode); // Do not delete directly. This class is reference counted. @@ -1345,8 +1365,22 @@ class RenderViewImpl : public RenderWidget, // DOM automation bindings are enabled. scoped_ptr dom_automation_controller_; - // Indicates whether this RenderView is a guest of another RenderView. - bool guest_; + // Channel for communication with embedding renderer, if it exists. + scoped_refptr guest_to_embedder_channel_; + + // The pepper instance identifer for this guest RenderView. + PP_Instance guest_pp_instance_; + + // The ppapi::HostResource associated with the on-screen context for this + // guest RenderView. + ppapi::HostResource guest_graphics_resource_; + + // This graphics context is initialized once GuestReady() is called. + WebGraphicsContext3DCommandBufferImpl* guest_uninitialized_context_; + + // These are the attributes originally passed into createGraphicsContext3D + // before the guest_to_embedder_channel was ready. + WebKit::WebGraphicsContext3D::Attributes guest_attributes_; // The accessibility mode. AccessibilityMode accessibility_mode_; diff --git a/content/test/render_view_test.cc b/content/test/render_view_test.cc index 4df47e63cc1d44..59b75deaab8d41 100644 --- a/content/test/render_view_test.cc +++ b/content/test/render_view_test.cc @@ -169,7 +169,7 @@ void RenderViewTest::SetUp() { false, 1, WebKit::WebScreenInfo(), - false, + NULL, AccessibilityModeOff); view->AddRef(); view_ = view;