summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/common/plugin_messages.h7
-rw-r--r--chrome/plugin/webplugin_delegate_stub.cc1
-rw-r--r--chrome/renderer/webplugin_delegate_proxy.cc14
-rw-r--r--webkit/glue/plugins/plugin_instance.cc2
-rw-r--r--webkit/glue/plugins/plugin_stream.cc5
-rw-r--r--webkit/glue/plugins/plugin_stream.h5
-rw-r--r--webkit/glue/plugins/plugin_stream_url.cc4
-rw-r--r--webkit/glue/plugins/plugin_stream_url.h4
-rw-r--r--webkit/glue/plugins/plugin_string_stream.cc2
-rw-r--r--webkit/glue/webframeloaderclient_impl.cc2
-rw-r--r--webkit/glue/webplugin.h4
-rw-r--r--webkit/glue/webplugin_impl.cc201
-rw-r--r--webkit/glue/webplugin_impl.h29
-rw-r--r--webkit/glue/webplugin_impl_mac.mm3
14 files changed, 234 insertions, 49 deletions
diff --git a/chrome/common/plugin_messages.h b/chrome/common/plugin_messages.h
index 6ebead6..025acc2 100644
--- a/chrome/common/plugin_messages.h
+++ b/chrome/common/plugin_messages.h
@@ -69,6 +69,7 @@ struct PluginMsg_DidReceiveResponseParams {
std::string headers;
uint32 expected_length;
uint32 last_modified;
+ bool request_is_seekable;
};
struct NPIdentifier_Param {
@@ -266,6 +267,7 @@ struct ParamTraits<PluginMsg_DidReceiveResponseParams> {
WriteParam(m, p.headers);
WriteParam(m, p.expected_length);
WriteParam(m, p.last_modified);
+ WriteParam(m, p.request_is_seekable);
}
static bool Read(const Message* m, void** iter, param_type* r) {
return
@@ -273,7 +275,8 @@ struct ParamTraits<PluginMsg_DidReceiveResponseParams> {
ReadParam(m, iter, &r->mime_type) &&
ReadParam(m, iter, &r->headers) &&
ReadParam(m, iter, &r->expected_length) &&
- ReadParam(m, iter, &r->last_modified);
+ ReadParam(m, iter, &r->last_modified) &&
+ ReadParam(m, iter, &r->request_is_seekable);
}
static void Log(const param_type& p, std::wstring* l) {
l->append(L"(");
@@ -286,6 +289,8 @@ struct ParamTraits<PluginMsg_DidReceiveResponseParams> {
LogParam(p.expected_length, l);
l->append(L", ");
LogParam(p.last_modified, l);
+ l->append(L", ");
+ LogParam(p.request_is_seekable, l);
l->append(L")");
}
};
diff --git a/chrome/plugin/webplugin_delegate_stub.cc b/chrome/plugin/webplugin_delegate_stub.cc
index 11703b6..1fc89cb 100644
--- a/chrome/plugin/webplugin_delegate_stub.cc
+++ b/chrome/plugin/webplugin_delegate_stub.cc
@@ -157,6 +157,7 @@ void WebPluginDelegateStub::OnDidReceiveResponse(
params.headers,
params.expected_length,
params.last_modified,
+ params.request_is_seekable,
cancel);
}
diff --git a/chrome/renderer/webplugin_delegate_proxy.cc b/chrome/renderer/webplugin_delegate_proxy.cc
index 3525a1f..3bad2e4f 100644
--- a/chrome/renderer/webplugin_delegate_proxy.cc
+++ b/chrome/renderer/webplugin_delegate_proxy.cc
@@ -37,7 +37,8 @@ class ResourceClientProxy : public WebPluginResourceClient {
public:
ResourceClientProxy(PluginChannelHost* channel, int instance_id)
: channel_(channel), instance_id_(instance_id), resource_id_(0),
- notify_needed_(false), notify_data_(NULL) {
+ notify_needed_(false), notify_data_(NULL),
+ multibyte_response_expected_(false) {
}
~ResourceClientProxy() {
@@ -57,6 +58,8 @@ class ResourceClientProxy : public WebPluginResourceClient {
params.notify_data = notify_data_;
params.stream = existing_stream;
+ multibyte_response_expected_ = (existing_stream != NULL);
+
channel_->Send(new PluginMsg_HandleURLRequestReply(instance_id_, params));
}
@@ -71,6 +74,7 @@ class ResourceClientProxy : public WebPluginResourceClient {
const std::string& headers,
uint32 expected_length,
uint32 last_modified,
+ bool request_is_seekable,
bool* cancel) {
DCHECK(channel_ != NULL);
PluginMsg_DidReceiveResponseParams params;
@@ -79,6 +83,7 @@ class ResourceClientProxy : public WebPluginResourceClient {
params.headers = headers;
params.expected_length = expected_length;
params.last_modified = last_modified;
+ params.request_is_seekable = request_is_seekable;
// Grab a reference on the underlying channel so it does not get
// deleted from under us.
scoped_refptr<PluginChannelHost> channel_ref(channel_);
@@ -113,6 +118,10 @@ class ResourceClientProxy : public WebPluginResourceClient {
MessageLoop::current()->DeleteSoon(FROM_HERE, this);
}
+ bool IsMultiByteResponseExpected() {
+ return multibyte_response_expected_;
+ }
+
private:
int resource_id_;
int instance_id_;
@@ -120,6 +129,9 @@ private:
std::string url_;
bool notify_needed_;
void* notify_data_;
+ // Set to true if the response expected is a multibyte response.
+ // For e.g. response for a HTTP byte range request.
+ bool multibyte_response_expected_;
};
WebPluginDelegateProxy* WebPluginDelegateProxy::Create(
diff --git a/webkit/glue/plugins/plugin_instance.cc b/webkit/glue/plugins/plugin_instance.cc
index 364274d6..47bf680 100644
--- a/webkit/glue/plugins/plugin_instance.cc
+++ b/webkit/glue/plugins/plugin_instance.cc
@@ -368,7 +368,7 @@ void PluginInstance::DidReceiveManualResponse(const std::string& url,
plugin_data_stream_ = CreateStream(-1, url, mime_type, false, NULL);
plugin_data_stream_->DidReceiveResponse(mime_type, headers, expected_length,
- last_modified, &cancel);
+ last_modified, true, &cancel);
AddStream(plugin_data_stream_.get());
}
diff --git a/webkit/glue/plugins/plugin_stream.cc b/webkit/glue/plugins/plugin_stream.cc
index bc12ace..a2b3980 100644
--- a/webkit/glue/plugins/plugin_stream.cc
+++ b/webkit/glue/plugins/plugin_stream.cc
@@ -25,7 +25,8 @@ PluginStream::~PluginStream() {
bool PluginStream::Open(const std::string &mime_type,
const std::string &headers,
uint32 length,
- uint32 last_modified) {
+ uint32 last_modified,
+ bool request_is_seekable) {
headers_ = headers;
NPP id = instance_->npp();
stream_.end = length;
@@ -35,7 +36,7 @@ bool PluginStream::Open(const std::string &mime_type,
stream_.notifyData = notify_data_;
bool seekable_stream = false;
- if (!headers_.empty()) {
+ if (request_is_seekable && !headers_.empty()) {
stream_.headers = headers_.c_str();
if (headers_.find("Accept-Ranges: bytes") != std::string::npos) {
seekable_stream = true;
diff --git a/webkit/glue/plugins/plugin_stream.h b/webkit/glue/plugins/plugin_stream.h
index 7b1250f..5cdc5b4 100644
--- a/webkit/glue/plugins/plugin_stream.h
+++ b/webkit/glue/plugins/plugin_stream.h
@@ -39,10 +39,13 @@ class PluginStream : public base::RefCounted<PluginStream> {
// mime-types table and the extension (if any) in the URL.
// If the size of the stream is known, use length to set the size. If
// not known, set length to 0.
+ // The request_is_seekable parameter indicates whether byte range requests
+ // can be issued on the stream.
bool Open(const std::string &mime_type,
const std::string &headers,
uint32 length,
- uint32 last_modified);
+ uint32 last_modified,
+ bool request_is_seekable);
// Writes to the stream.
int Write(const char *buf, const int len, int data_offset);
diff --git a/webkit/glue/plugins/plugin_stream_url.cc b/webkit/glue/plugins/plugin_stream_url.cc
index c749ff7..f7e8ec7 100644
--- a/webkit/glue/plugins/plugin_stream_url.cc
+++ b/webkit/glue/plugins/plugin_stream_url.cc
@@ -42,11 +42,13 @@ void PluginStreamUrl::DidReceiveResponse(const std::string& mime_type,
const std::string& headers,
uint32 expected_length,
uint32 last_modified,
+ bool request_is_seekable,
bool* cancel) {
bool opened = Open(mime_type,
headers,
expected_length,
- last_modified);
+ last_modified,
+ request_is_seekable);
if (!opened) {
instance()->RemoveStream(this);
*cancel = true;
diff --git a/webkit/glue/plugins/plugin_stream_url.h b/webkit/glue/plugins/plugin_stream_url.h
index 1f8c4c5..db5d4a5 100644
--- a/webkit/glue/plugins/plugin_stream_url.h
+++ b/webkit/glue/plugins/plugin_stream_url.h
@@ -48,10 +48,14 @@ class PluginStreamUrl : public PluginStream,
const std::string& headers,
uint32 expected_length,
uint32 last_modified,
+ bool request_is_seekable,
bool* cancel);
void DidReceiveData(const char* buffer, int length, int data_offset);
void DidFinishLoading();
void DidFail();
+ bool IsMultiByteResponseExpected() {
+ return seekable();
+ }
private:
diff --git a/webkit/glue/plugins/plugin_string_stream.cc b/webkit/glue/plugins/plugin_string_stream.cc
index b1ad8d1..db46d1c 100644
--- a/webkit/glue/plugins/plugin_string_stream.cc
+++ b/webkit/glue/plugins/plugin_string_stream.cc
@@ -20,7 +20,7 @@ PluginStringStream::~PluginStringStream() {
void PluginStringStream::SendToPlugin(const std::string &data,
const std::string &mime_type) {
int length = static_cast<int>(data.length());
- if (Open(mime_type, std::string(), length, 0)) {
+ if (Open(mime_type, std::string(), length, 0, false)) {
// TODO - check if it was not fully sent, and figure out a backup plan.
int written = Write(data.c_str(), length, 0);
NPReason reason = written == length ? NPRES_DONE : NPRES_NETWORK_ERR;
diff --git a/webkit/glue/webframeloaderclient_impl.cc b/webkit/glue/webframeloaderclient_impl.cc
index 21b3938..0fbb4af 100644
--- a/webkit/glue/webframeloaderclient_impl.cc
+++ b/webkit/glue/webframeloaderclient_impl.cc
@@ -1404,7 +1404,7 @@ Widget* WebFrameLoaderClient::createPlugin(const IntSize& size, // TODO(erikkay)
Widget* result = WebPluginImpl::Create(gurl, argn, argv, argc, element,
webframe_, plugin_delegate,
- load_manually);
+ load_manually, actual_mime_type);
DeleteToArray(argn);
DeleteToArray(argv);
diff --git a/webkit/glue/webplugin.h b/webkit/glue/webplugin.h
index b704153..8124857 100644
--- a/webkit/glue/webplugin.h
+++ b/webkit/glue/webplugin.h
@@ -148,15 +148,19 @@ class WebPluginResourceClient {
public:
virtual ~WebPluginResourceClient() {}
virtual void WillSendRequest(const GURL& url) = 0;
+ // The request_is_seekable parameter indicates whether byte range requests
+ // can be issued for the underlying stream.
virtual void DidReceiveResponse(const std::string& mime_type,
const std::string& headers,
uint32 expected_length,
uint32 last_modified,
+ bool request_is_seekable,
bool* cancel) = 0;
virtual void DidReceiveData(const char* buffer, int length,
int data_offset) = 0;
virtual void DidFinishLoading() = 0;
virtual void DidFail() = 0;
+ virtual bool IsMultiByteResponseExpected() = 0;
};
diff --git a/webkit/glue/webplugin_impl.cc b/webkit/glue/webplugin_impl.cc
index 221f0e2..15685e4 100644
--- a/webkit/glue/webplugin_impl.cc
+++ b/webkit/glue/webplugin_impl.cc
@@ -283,9 +283,11 @@ WebCore::Widget* WebPluginImpl::Create(const GURL& url,
WebCore::Element *element,
WebFrameImpl *frame,
WebPluginDelegate* delegate,
- bool load_manually) {
+ bool load_manually,
+ const std::string& mime_type) {
WebPluginImpl* webplugin = new WebPluginImpl(element, frame, delegate, url,
- load_manually);
+ load_manually, mime_type, argc,
+ argn, argv);
if (!delegate->Initialize(url, argn, argv, argc, webplugin, load_manually)) {
delegate->PluginDestroyed();
@@ -303,7 +305,11 @@ WebPluginImpl::WebPluginImpl(WebCore::Element* element,
WebFrameImpl* webframe,
WebPluginDelegate* delegate,
const GURL& plugin_url,
- bool load_manually)
+ bool load_manually,
+ const std::string& mime_type,
+ int arg_count,
+ char** arg_names,
+ char** arg_values)
: windowless_(false),
window_(NULL),
element_(element),
@@ -315,7 +321,11 @@ WebPluginImpl::WebPluginImpl(WebCore::Element* element,
widget_(NULL),
plugin_url_(plugin_url),
load_manually_(load_manually),
- first_geometry_update_(true) {
+ first_geometry_update_(true),
+ mime_type_(mime_type) {
+
+ ArrayToVector(arg_count, arg_names, &arg_names_);
+ ArrayToVector(arg_count, arg_values, &arg_values_);
}
WebPluginImpl::~WebPluginImpl() {
@@ -976,6 +986,7 @@ std::wstring WebPluginImpl::GetAllHeaders(
void WebPluginImpl::didReceiveResponse(WebCore::ResourceHandle* handle,
const WebCore::ResourceResponse& response) {
static const int kHttpPartialResponseStatusCode = 206;
+ static const int kHttpResponseSuccessStatusCode = 200;
WebPluginResourceClient* client = GetClientFromHandle(handle);
if (!client)
@@ -985,17 +996,55 @@ void WebPluginImpl::didReceiveResponse(WebCore::ResourceHandle* handle,
WebPluginContainer::ReadHttpResponseInfo(response, &http_response_info);
bool cancel = false;
+ bool request_is_seekable = true;
+ if (client->IsMultiByteResponseExpected()) {
+ if (response.httpStatusCode() == kHttpPartialResponseStatusCode) {
+ HandleHttpMultipartResponse(response, client);
+ return;
+ } else if (response.httpStatusCode() == kHttpResponseSuccessStatusCode) {
+ // If the client issued a byte range request and the server responds with
+ // HTTP 200 OK, it indicates that the server does not support byte range
+ // requests.
+ // We need to emulate Firefox behavior by doing the following:-
+ // 1. Destroy the plugin instance in the plugin process. Ensure that
+ // existing resource requests initiated for the plugin instance
+ // continue to remain valid.
+ // 2. Create a new plugin instance and notify it about the response
+ // received here.
+ if (!ReinitializePluginForResponse(handle)) {
+ NOTREACHED();
+ return;
+ }
- if (response.httpStatusCode() == kHttpPartialResponseStatusCode) {
- HandleHttpMultipartResponse(response, client);
- return;
+ // The server does not support byte range requests. No point in creating
+ // seekable streams.
+ request_is_seekable = false;
+
+ delete client;
+ client = NULL;
+
+ // Create a new resource client for this request.
+ for (size_t i = 0; i < clients_.size(); ++i) {
+ if (clients_[i].handle.get() == handle) {
+ WebPluginResourceClient* resource_client =
+ delegate_->CreateResourceClient(clients_[i].id,
+ plugin_url_.spec().c_str(),
+ NULL, false, NULL);
+ clients_[i].client = resource_client;
+ client = resource_client;
+ break;
+ }
+ }
+
+ DCHECK(client != NULL);
+ }
}
client->DidReceiveResponse(
base::SysWideToNativeMB(http_response_info.mime_type),
base::SysWideToNativeMB(GetAllHeaders(response)),
http_response_info.expected_length,
- http_response_info.last_modified, &cancel);
+ http_response_info.last_modified, request_is_seekable, &cancel);
if (cancel) {
handle->cancel();
@@ -1078,35 +1127,7 @@ void WebPluginImpl::RemoveClient(WebCore::ResourceHandle* handle) {
void WebPluginImpl::SetContainer(WebPluginContainer* container) {
if (container == NULL) {
- // The frame maintains a list of JSObjects which are related to this
- // plugin. Tell the frame we're gone so that it can invalidate all
- // of those sub JSObjects.
- if (frame()) {
- ASSERT(widget_ != NULL);
- frame()->script()->cleanupScriptObjectsForPlugin(widget_);
- }
-
- // Call PluginDestroyed() first to prevent the plugin from calling us back
- // in the middle of tearing down the render tree.
- delegate_->PluginDestroyed();
- delegate_ = NULL;
-
- // Cancel any pending requests because otherwise this deleted object will be
- // called by the ResourceDispatcher.
- int int_offset = 0;
- while (!clients_.empty()) {
- if (clients_[int_offset].handle)
- clients_[int_offset].handle->cancel();
- WebPluginResourceClient* resource_client = clients_[int_offset].client;
- RemoveClient(int_offset);
- if (resource_client)
- resource_client->DidFail();
- }
-
- // This needs to be called now and not in the destructor since the
- // webframe_ might not be valid anymore.
- webframe_->set_plugin_delegate(NULL);
- webframe_ = NULL;
+ TearDownPluginInstance(NULL);
}
widget_ = container;
}
@@ -1315,3 +1336,109 @@ void WebPluginImpl::HandleHttpMultipartResponse(
multipart_boundary);
multi_part_response_map_[client] = multi_part_response_handler;
}
+
+bool WebPluginImpl::ReinitializePluginForResponse(
+ WebCore::ResourceHandle* response_handle) {
+ WebFrameImpl* web_frame = WebFrameImpl::FromFrame(frame());
+ if (!web_frame)
+ return false;
+
+ WebViewImpl* web_view = web_frame->webview_impl();
+ if (!web_view)
+ return false;
+
+ WebPluginContainer* container_widget = widget_;
+
+ // Destroy the current plugin instance.
+ TearDownPluginInstance(response_handle);
+
+ widget_ = container_widget;
+ webframe_ = web_frame;
+ // Turn off the load_manually flag as we are going to hand data off to the
+ // plugin.
+ load_manually_ = false;
+
+ WebViewDelegate* webview_delegate = web_view->GetDelegate();
+ std::string actual_mime_type;
+ WebPluginDelegate* plugin_delegate =
+ webview_delegate->CreatePluginDelegate(web_view, plugin_url_,
+ mime_type_, std::string(),
+ &actual_mime_type);
+
+ char** arg_names = new char*[arg_names_.size()];
+ char** arg_values = new char*[arg_values_.size()];
+
+ for (unsigned int index = 0; index < arg_names_.size(); ++index) {
+ arg_names[index] = const_cast<char*>(arg_names_[index].c_str());
+ arg_values[index] = const_cast<char*>(arg_values_[index].c_str());
+ }
+
+ bool init_ok = plugin_delegate->Initialize(plugin_url_, arg_names,
+ arg_values, arg_names_.size(),
+ this, load_manually_);
+ delete[] arg_names;
+ delete[] arg_values;
+
+ if (!init_ok) {
+ SetContainer(NULL);
+ // TODO(iyengar) Should we delete the current plugin instance here?
+ return false;
+ }
+
+ mime_type_ = actual_mime_type;
+ delegate_ = plugin_delegate;
+ // Force a geometry update to occur to ensure that the plugin becomes
+ // visible.
+ widget_->frameRectsChanged();
+ delegate_->FlushGeometryUpdates();
+ return true;
+}
+
+void WebPluginImpl::ArrayToVector(int total_values, char** values,
+ std::vector<std::string>* value_vector) {
+ DCHECK(value_vector != NULL);
+ for (int index = 0; index < total_values; ++index) {
+ value_vector->push_back(values[index]);
+ }
+}
+
+void WebPluginImpl::TearDownPluginInstance(
+ WebCore::ResourceHandle* response_handle_to_ignore) {
+ // The frame maintains a list of JSObjects which are related to this
+ // plugin. Tell the frame we're gone so that it can invalidate all
+ // of those sub JSObjects.
+ if (frame()) {
+ ASSERT(widget_ != NULL);
+ frame()->script()->cleanupScriptObjectsForPlugin(widget_);
+ }
+
+ // Call PluginDestroyed() first to prevent the plugin from calling us back
+ // in the middle of tearing down the render tree.
+ delegate_->PluginDestroyed();
+ delegate_ = NULL;
+
+ // Cancel any pending requests because otherwise this deleted object will
+ // be called by the ResourceDispatcher.
+ std::vector<ClientInfo>::iterator client_index = clients_.begin();
+ while (client_index != clients_.end()) {
+ ClientInfo& client_info = *client_index;
+
+ if (response_handle_to_ignore == client_info.handle) {
+ client_index++;
+ continue;
+ }
+
+ if (client_info.handle)
+ client_info.handle->cancel();
+
+ WebPluginResourceClient* resource_client = client_info.client;
+ client_index = clients_.erase(client_index);
+ if (resource_client)
+ resource_client->DidFail();
+ }
+
+ // This needs to be called now and not in the destructor since the
+ // webframe_ might not be valid anymore.
+ webframe_->set_plugin_delegate(NULL);
+ webframe_ = NULL;
+}
diff --git a/webkit/glue/webplugin_impl.h b/webkit/glue/webplugin_impl.h
index 01cf7db..1d11d71 100644
--- a/webkit/glue/webplugin_impl.h
+++ b/webkit/glue/webplugin_impl.h
@@ -124,7 +124,8 @@ class WebPluginImpl : public WebPlugin,
WebCore::Element* element,
WebFrameImpl* frame,
WebPluginDelegate* delegate,
- bool load_manually);
+ bool load_manually,
+ const std::string& mime_type);
virtual ~WebPluginImpl();
virtual NPObject* GetPluginScriptableObject();
@@ -139,7 +140,8 @@ class WebPluginImpl : public WebPlugin,
WebPluginImpl(WebCore::Element *element, WebFrameImpl *frame,
WebPluginDelegate* delegate, const GURL& plugin_url,
- bool load_manually);
+ bool load_manually, const std::string& mime_type,
+ int arg_count, char** arg_names, char** arg_values);
// WebPlugin implementation:
void SetWindow(HWND window, HANDLE pump_messages_event);
@@ -231,6 +233,12 @@ class WebPluginImpl : public WebPlugin,
// Sets the actual Widget for the plugin.
void SetContainer(WebPluginContainer* container);
+ // Destroys the plugin instance.
+ // The response_handle_to_ignore parameter if not NULL indicates the
+ // resource handle to be left valid during plugin shutdown.
+ void TearDownPluginInstance(
+ WebCore::ResourceHandle* response_handle_to_ignore);
+
WebCore::ScrollView* parent() const;
// ResourceHandleClient implementation. We implement this interface in the
@@ -294,6 +302,14 @@ class WebPluginImpl : public WebPlugin,
void* notify_data, bool popups_allowed,
bool use_plugin_src_as_referrer);
+ // Tears down the existing plugin instance and creates a new plugin instance
+ // to handle the response identified by the response_handle parameter.
+ bool ReinitializePluginForResponse(WebCore::ResourceHandle* response_handle);
+
+ // Helper functions to convert an array of names/values to a vector.
+ static void ArrayToVector(int total_values, char** values,
+ std::vector<std::string>* value_vector);
+
struct ClientInfo {
int id;
WebPluginResourceClient* client;
@@ -332,6 +348,15 @@ class WebPluginImpl : public WebPlugin,
// Indicates if this is the first geometry update received by the plugin.
bool first_geometry_update_;
+ // The mime type of the plugin.
+ std::string mime_type_;
+
+ // Holds the list of argument names passed to the plugin.
+ std::vector<std::string> arg_names_;
+
+ // Holds the list of argument values passed to the plugin.
+ std::vector<std::string> arg_values_;
+
DISALLOW_COPY_AND_ASSIGN(WebPluginImpl);
};
diff --git a/webkit/glue/webplugin_impl_mac.mm b/webkit/glue/webplugin_impl_mac.mm
index 1a420ea..b88dbb6 100644
--- a/webkit/glue/webplugin_impl_mac.mm
+++ b/webkit/glue/webplugin_impl_mac.mm
@@ -50,7 +50,8 @@ WebCore::Widget* WebPluginImpl::Create(const GURL& url,
WebCore::Element* element,
WebFrameImpl* frame,
WebPluginDelegate* delegate,
- bool load_manually) {
+ bool load_manually,
+ const std::string& mime_type) {
// TODO(pinkerton): delete delegate when stubbing out?
NSLog(@"WebPluginImpl::Create");
return NULL;