webplugin_delegate_proxy.cc revision 23730a6e56a168d1879203e4b3819bb36e3d8f1f
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/renderer/npapi/webplugin_delegate_proxy.h"
6
7#if defined(TOOLKIT_GTK)
8#include <gtk/gtk.h>
9#elif defined(USE_X11)
10#include <cairo/cairo.h>
11#endif
12
13#include <algorithm>
14
15#include "base/auto_reset.h"
16#include "base/basictypes.h"
17#include "base/command_line.h"
18#include "base/file_util.h"
19#include "base/logging.h"
20#include "base/memory/ref_counted.h"
21#include "base/memory/scoped_ptr.h"
22#include "base/process/process.h"
23#include "base/strings/string_split.h"
24#include "base/strings/string_util.h"
25#include "base/strings/utf_string_conversions.h"
26#include "base/version.h"
27#include "content/child/child_process.h"
28#include "content/child/npapi/npobject_proxy.h"
29#include "content/child/npapi/npobject_stub.h"
30#include "content/child/npapi/npobject_util.h"
31#include "content/child/npapi/webplugin_resource_client.h"
32#include "content/child/plugin_messages.h"
33#include "content/common/content_constants_internal.h"
34#include "content/common/cursors/webcursor.h"
35#include "content/common/frame_messages.h"
36#include "content/common/view_messages.h"
37#include "content/public/renderer/content_renderer_client.h"
38#include "content/renderer/npapi/plugin_channel_host.h"
39#include "content/renderer/npapi/webplugin_impl.h"
40#include "content/renderer/render_thread_impl.h"
41#include "content/renderer/render_view_impl.h"
42#include "content/renderer/sad_plugin.h"
43#include "ipc/ipc_channel_handle.h"
44#include "net/base/mime_util.h"
45#include "skia/ext/platform_canvas.h"
46#include "third_party/WebKit/public/platform/WebDragData.h"
47#include "third_party/WebKit/public/platform/WebString.h"
48#include "third_party/WebKit/public/web/WebBindings.h"
49#include "third_party/WebKit/public/web/WebDocument.h"
50#include "third_party/WebKit/public/web/WebFrame.h"
51#include "third_party/WebKit/public/web/WebView.h"
52#include "ui/gfx/blit.h"
53#include "ui/gfx/canvas.h"
54#include "ui/gfx/native_widget_types.h"
55#include "ui/gfx/size.h"
56#include "ui/gfx/skia_util.h"
57
58#if defined(OS_POSIX)
59#include "ipc/ipc_channel_posix.h"
60#endif
61
62#if defined(OS_MACOSX)
63#include "base/mac/mac_util.h"
64#endif
65
66#if defined(OS_WIN)
67#include "content/public/common/sandbox_init.h"
68#endif
69
70using blink::WebBindings;
71using blink::WebCursorInfo;
72using blink::WebDragData;
73using blink::WebInputEvent;
74using blink::WebString;
75using blink::WebView;
76
77namespace content {
78
79namespace {
80
81class ScopedLogLevel {
82 public:
83  explicit ScopedLogLevel(int level);
84  ~ScopedLogLevel();
85
86 private:
87  int old_level_;
88
89  DISALLOW_COPY_AND_ASSIGN(ScopedLogLevel);
90};
91
92ScopedLogLevel::ScopedLogLevel(int level)
93    : old_level_(logging::GetMinLogLevel()) {
94  logging::SetMinLogLevel(level);
95}
96
97ScopedLogLevel::~ScopedLogLevel() {
98  logging::SetMinLogLevel(old_level_);
99}
100
101// Proxy for WebPluginResourceClient.  The object owns itself after creation,
102// deleting itself after its callback has been called.
103class ResourceClientProxy : public WebPluginResourceClient {
104 public:
105  ResourceClientProxy(PluginChannelHost* channel, int instance_id)
106    : channel_(channel), instance_id_(instance_id), resource_id_(0),
107      multibyte_response_expected_(false) {
108  }
109
110  virtual ~ResourceClientProxy() {
111  }
112
113  void Initialize(unsigned long resource_id, const GURL& url, int notify_id) {
114    resource_id_ = resource_id;
115    channel_->Send(new PluginMsg_HandleURLRequestReply(
116        instance_id_, resource_id, url, notify_id));
117  }
118
119  void InitializeForSeekableStream(unsigned long resource_id,
120                                   int range_request_id) {
121    resource_id_ = resource_id;
122    multibyte_response_expected_ = true;
123    channel_->Send(new PluginMsg_HTTPRangeRequestReply(
124        instance_id_, resource_id, range_request_id));
125  }
126
127  // PluginResourceClient implementation:
128  virtual void WillSendRequest(const GURL& url, int http_status_code) OVERRIDE {
129    DCHECK(channel_.get() != NULL);
130    channel_->Send(new PluginMsg_WillSendRequest(
131        instance_id_, resource_id_, url, http_status_code));
132  }
133
134  virtual void DidReceiveResponse(const std::string& mime_type,
135                                  const std::string& headers,
136                                  uint32 expected_length,
137                                  uint32 last_modified,
138                                  bool request_is_seekable) OVERRIDE {
139    DCHECK(channel_.get() != NULL);
140    PluginMsg_DidReceiveResponseParams params;
141    params.id = resource_id_;
142    params.mime_type = mime_type;
143    params.headers = headers;
144    params.expected_length = expected_length;
145    params.last_modified = last_modified;
146    params.request_is_seekable = request_is_seekable;
147    // Grab a reference on the underlying channel so it does not get
148    // deleted from under us.
149    scoped_refptr<PluginChannelHost> channel_ref(channel_);
150    channel_->Send(new PluginMsg_DidReceiveResponse(instance_id_, params));
151  }
152
153  virtual void DidReceiveData(const char* buffer,
154                              int length,
155                              int data_offset) OVERRIDE {
156    DCHECK(channel_.get() != NULL);
157    DCHECK_GT(length, 0);
158    std::vector<char> data;
159    data.resize(static_cast<size_t>(length));
160    memcpy(&data.front(), buffer, length);
161    // Grab a reference on the underlying channel so it does not get
162    // deleted from under us.
163    scoped_refptr<PluginChannelHost> channel_ref(channel_);
164    channel_->Send(new PluginMsg_DidReceiveData(instance_id_, resource_id_,
165                                                data, data_offset));
166  }
167
168  virtual void DidFinishLoading(unsigned long resource_id) OVERRIDE {
169    DCHECK(channel_.get() != NULL);
170    DCHECK_EQ(resource_id, resource_id_);
171    channel_->Send(new PluginMsg_DidFinishLoading(instance_id_, resource_id_));
172    channel_ = NULL;
173    base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
174  }
175
176  virtual void DidFail(unsigned long resource_id) OVERRIDE {
177    DCHECK(channel_.get() != NULL);
178    DCHECK_EQ(resource_id, resource_id_);
179    channel_->Send(new PluginMsg_DidFail(instance_id_, resource_id_));
180    channel_ = NULL;
181    base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
182  }
183
184  virtual bool IsMultiByteResponseExpected() OVERRIDE {
185    return multibyte_response_expected_;
186  }
187
188  virtual int ResourceId() OVERRIDE {
189    return resource_id_;
190  }
191
192 private:
193  scoped_refptr<PluginChannelHost> channel_;
194  int instance_id_;
195  unsigned long resource_id_;
196  // Set to true if the response expected is a multibyte response.
197  // For e.g. response for a HTTP byte range request.
198  bool multibyte_response_expected_;
199};
200
201}  // namespace
202
203WebPluginDelegateProxy::WebPluginDelegateProxy(
204    WebPluginImpl* plugin,
205    const std::string& mime_type,
206    const base::WeakPtr<RenderViewImpl>& render_view,
207    RenderFrameImpl* render_frame)
208    : render_view_(render_view),
209      render_frame_(render_frame),
210      plugin_(plugin),
211      uses_shared_bitmaps_(false),
212#if defined(OS_MACOSX)
213      uses_compositor_(false),
214#elif defined(OS_WIN)
215      dummy_activation_window_(NULL),
216#endif
217      window_(gfx::kNullPluginWindow),
218      mime_type_(mime_type),
219      instance_id_(MSG_ROUTING_NONE),
220      npobject_(NULL),
221      npp_(new NPP_t),
222      sad_plugin_(NULL),
223      invalidate_pending_(false),
224      transparent_(false),
225      front_buffer_index_(0),
226      page_url_(render_view_->webview()->mainFrame()->document().url()) {
227}
228
229WebPluginDelegateProxy::~WebPluginDelegateProxy() {
230  if (npobject_)
231    WebBindings::releaseObject(npobject_);
232}
233
234WebPluginDelegateProxy::SharedBitmap::SharedBitmap() {}
235
236WebPluginDelegateProxy::SharedBitmap::~SharedBitmap() {}
237
238void WebPluginDelegateProxy::PluginDestroyed() {
239#if defined(OS_MACOSX) || defined(OS_WIN)
240  // Ensure that the renderer doesn't think the plugin still has focus.
241  if (render_view_)
242    render_view_->PluginFocusChanged(false, instance_id_);
243#endif
244
245#if defined(OS_WIN)
246  if (dummy_activation_window_ && render_view_) {
247    render_view_->Send(new ViewHostMsg_WindowlessPluginDummyWindowDestroyed(
248        render_view_->routing_id(), dummy_activation_window_));
249  }
250  dummy_activation_window_ = NULL;
251#endif
252
253  if (window_)
254    WillDestroyWindow();
255
256  if (render_view_.get())
257    render_view_->UnregisterPluginDelegate(this);
258
259  if (channel_host_.get()) {
260    Send(new PluginMsg_DestroyInstance(instance_id_));
261
262    // Must remove the route after sending the destroy message, rather than
263    // before, since RemoveRoute can lead to all the outstanding NPObjects
264    // being told the channel went away if this was the last instance.
265    channel_host_->RemoveRoute(instance_id_);
266
267    // Remove the mapping between our instance-Id and NPP identifiers, used by
268    // the channel to track object ownership, before releasing it.
269    channel_host_->RemoveMappingForNPObjectOwner(instance_id_);
270
271    // Release the channel host now. If we are is the last reference to the
272    // channel, this avoids a race where this renderer asks a new connection to
273    // the same plugin between now and the time 'this' is actually deleted.
274    // Destroying the channel host is what releases the channel name -> FD
275    // association on POSIX, and if we ask for a new connection before it is
276    // released, the plugin will give us a new FD, and we'll assert when trying
277    // to associate it with the channel name.
278    channel_host_ = NULL;
279  }
280
281  plugin_ = NULL;
282
283  base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
284}
285
286bool WebPluginDelegateProxy::Initialize(
287    const GURL& url,
288    const std::vector<std::string>& arg_names,
289    const std::vector<std::string>& arg_values,
290    bool load_manually) {
291  // TODO(shess): Attempt to work around http://crbug.com/97285 and
292  // http://crbug.com/141055 by retrying the connection.  Reports seem
293  // to indicate that the plugin hasn't crashed, and that the problem
294  // is not 100% persistent.
295  const size_t kAttempts = 2;
296
297  bool result = false;
298  scoped_refptr<PluginChannelHost> channel_host;
299  int instance_id = 0;
300
301  for (size_t attempt = 0; !result && attempt < kAttempts; attempt++) {
302#if defined(OS_MACOSX)
303    // TODO(shess): Debugging for http://crbug.com/97285 .  See comment
304    // in plugin_channel_host.cc.
305    scoped_ptr<base::AutoReset<bool> > track_nested_removes(
306        new base::AutoReset<bool>(PluginChannelHost::GetRemoveTrackingFlag(),
307                                  true));
308#endif
309
310    IPC::ChannelHandle channel_handle;
311    if (!RenderThreadImpl::current()->Send(new FrameHostMsg_OpenChannelToPlugin(
312            render_frame_->GetRoutingID(), url, page_url_, mime_type_,
313            &channel_handle, &info_))) {
314      continue;
315    }
316
317    if (channel_handle.name.empty()) {
318      // We got an invalid handle.  Either the plugin couldn't be found (which
319      // shouldn't happen, since if we got here the plugin should exist) or the
320      // plugin crashed on initialization.
321      if (!info_.path.empty()) {
322        render_view_->main_render_frame()->PluginCrashed(
323            info_.path, base::kNullProcessId);
324        LOG(ERROR) << "Plug-in crashed on start";
325
326        // Return true so that the plugin widget is created and we can paint the
327        // crashed plugin there.
328        return true;
329      }
330      LOG(ERROR) << "Plug-in couldn't be found";
331      return false;
332    }
333
334    channel_host =
335        PluginChannelHost::GetPluginChannelHost(
336            channel_handle, ChildProcess::current()->io_message_loop_proxy());
337    if (!channel_host.get()) {
338      LOG(ERROR) << "Couldn't get PluginChannelHost";
339      continue;
340    }
341#if defined(OS_MACOSX)
342    track_nested_removes.reset();
343#endif
344
345    {
346      // TODO(bauerb): Debugging for http://crbug.com/141055.
347      ScopedLogLevel log_level(-2);  // Equivalent to --v=2
348      result = channel_host->Send(new PluginMsg_CreateInstance(
349          mime_type_, &instance_id));
350      if (!result) {
351        LOG(ERROR) << "Couldn't send PluginMsg_CreateInstance";
352        continue;
353      }
354    }
355  }
356
357  // Failed too often, give up.
358  if (!result)
359    return false;
360
361  channel_host_ = channel_host;
362  instance_id_ = instance_id;
363
364  channel_host_->AddRoute(instance_id_, this, NULL);
365
366  // Inform the channel of the mapping between our instance-Id and dummy NPP
367  // identifier, for use in object ownership tracking.
368  channel_host_->AddMappingForNPObjectOwner(instance_id_, GetPluginNPP());
369
370  // Now tell the PluginInstance in the plugin process to initialize.
371  PluginMsg_Init_Params params;
372  params.url = url;
373  params.page_url = page_url_;
374  params.arg_names = arg_names;
375  params.arg_values = arg_values;
376  params.host_render_view_routing_id = render_view_->routing_id();
377  params.load_manually = load_manually;
378
379  result = false;
380  Send(new PluginMsg_Init(instance_id_, params, &transparent_, &result));
381
382  if (!result)
383    LOG(WARNING) << "PluginMsg_Init returned false";
384
385  render_view_->RegisterPluginDelegate(this);
386
387  return result;
388}
389
390bool WebPluginDelegateProxy::Send(IPC::Message* msg) {
391  if (!channel_host_.get()) {
392    DLOG(WARNING) << "dropping message because channel host is null";
393    delete msg;
394    return false;
395  }
396
397  return channel_host_->Send(msg);
398}
399
400void WebPluginDelegateProxy::SendJavaScriptStream(const GURL& url,
401                                                  const std::string& result,
402                                                  bool success,
403                                                  int notify_id) {
404  Send(new PluginMsg_SendJavaScriptStream(
405      instance_id_, url, result, success, notify_id));
406}
407
408void WebPluginDelegateProxy::DidReceiveManualResponse(
409    const GURL& url, const std::string& mime_type,
410    const std::string& headers, uint32 expected_length,
411    uint32 last_modified) {
412  PluginMsg_DidReceiveResponseParams params;
413  params.id = 0;
414  params.mime_type = mime_type;
415  params.headers = headers;
416  params.expected_length = expected_length;
417  params.last_modified = last_modified;
418  Send(new PluginMsg_DidReceiveManualResponse(instance_id_, url, params));
419}
420
421void WebPluginDelegateProxy::DidReceiveManualData(const char* buffer,
422                                                  int length) {
423  DCHECK_GT(length, 0);
424  std::vector<char> data;
425  data.resize(static_cast<size_t>(length));
426  memcpy(&data.front(), buffer, length);
427  Send(new PluginMsg_DidReceiveManualData(instance_id_, data));
428}
429
430void WebPluginDelegateProxy::DidFinishManualLoading() {
431  Send(new PluginMsg_DidFinishManualLoading(instance_id_));
432}
433
434void WebPluginDelegateProxy::DidManualLoadFail() {
435  Send(new PluginMsg_DidManualLoadFail(instance_id_));
436}
437
438bool WebPluginDelegateProxy::OnMessageReceived(const IPC::Message& msg) {
439  GetContentClient()->SetActiveURL(page_url_);
440
441  bool handled = true;
442  IPC_BEGIN_MESSAGE_MAP(WebPluginDelegateProxy, msg)
443    IPC_MESSAGE_HANDLER(PluginHostMsg_SetWindow, OnSetWindow)
444    IPC_MESSAGE_HANDLER(PluginHostMsg_CancelResource, OnCancelResource)
445    IPC_MESSAGE_HANDLER(PluginHostMsg_InvalidateRect, OnInvalidateRect)
446    IPC_MESSAGE_HANDLER(PluginHostMsg_GetWindowScriptNPObject,
447                        OnGetWindowScriptNPObject)
448    IPC_MESSAGE_HANDLER(PluginHostMsg_GetPluginElement, OnGetPluginElement)
449    IPC_MESSAGE_HANDLER(PluginHostMsg_ResolveProxy, OnResolveProxy)
450    IPC_MESSAGE_HANDLER(PluginHostMsg_SetCookie, OnSetCookie)
451    IPC_MESSAGE_HANDLER(PluginHostMsg_GetCookies, OnGetCookies)
452    IPC_MESSAGE_HANDLER(PluginHostMsg_URLRequest, OnHandleURLRequest)
453    IPC_MESSAGE_HANDLER(PluginHostMsg_CancelDocumentLoad, OnCancelDocumentLoad)
454    IPC_MESSAGE_HANDLER(PluginHostMsg_InitiateHTTPRangeRequest,
455                        OnInitiateHTTPRangeRequest)
456    IPC_MESSAGE_HANDLER(PluginHostMsg_DidStartLoading, OnDidStartLoading)
457    IPC_MESSAGE_HANDLER(PluginHostMsg_DidStopLoading, OnDidStopLoading)
458    IPC_MESSAGE_HANDLER(PluginHostMsg_DeferResourceLoading,
459                        OnDeferResourceLoading)
460    IPC_MESSAGE_HANDLER(PluginHostMsg_URLRedirectResponse,
461                        OnURLRedirectResponse)
462    IPC_MESSAGE_HANDLER(PluginHostMsg_CheckIfRunInsecureContent,
463                        OnCheckIfRunInsecureContent)
464#if defined(OS_WIN)
465    IPC_MESSAGE_HANDLER(PluginHostMsg_SetWindowlessData, OnSetWindowlessData)
466    IPC_MESSAGE_HANDLER(PluginHostMsg_NotifyIMEStatus, OnNotifyIMEStatus)
467#endif
468#if defined(OS_MACOSX)
469    IPC_MESSAGE_HANDLER(PluginHostMsg_FocusChanged,
470                        OnFocusChanged);
471    IPC_MESSAGE_HANDLER(PluginHostMsg_StartIme,
472                        OnStartIme);
473    IPC_MESSAGE_HANDLER(PluginHostMsg_AcceleratedPluginEnabledRendering,
474                        OnAcceleratedPluginEnabledRendering)
475    IPC_MESSAGE_HANDLER(PluginHostMsg_AcceleratedPluginAllocatedIOSurface,
476                        OnAcceleratedPluginAllocatedIOSurface)
477    IPC_MESSAGE_HANDLER(PluginHostMsg_AcceleratedPluginSwappedIOSurface,
478                        OnAcceleratedPluginSwappedIOSurface)
479#endif
480    IPC_MESSAGE_UNHANDLED(handled = false)
481  IPC_END_MESSAGE_MAP()
482  DCHECK(handled);
483  return handled;
484}
485
486void WebPluginDelegateProxy::OnChannelError() {
487  if (plugin_) {
488    if (window_) {
489      // The actual WebPluginDelegate never got a chance to tell the WebPlugin
490      // its window was going away. Do it on its behalf.
491      WillDestroyWindow();
492    }
493    plugin_->Invalidate();
494  }
495  if (!channel_host_->expecting_shutdown()) {
496    render_view_->main_render_frame()->PluginCrashed(
497        info_.path, channel_host_->peer_pid());
498  }
499
500#if defined(OS_MACOSX) || defined(OS_WIN)
501  // Ensure that the renderer doesn't think the plugin still has focus.
502  if (render_view_)
503    render_view_->PluginFocusChanged(false, instance_id_);
504#endif
505}
506
507static void CopyTransportDIBHandleForMessage(
508    const TransportDIB::Handle& handle_in,
509    TransportDIB::Handle* handle_out,
510    base::ProcessId peer_pid) {
511#if defined(OS_MACOSX)
512  // On Mac, TransportDIB::Handle is typedef'ed to FileDescriptor, and
513  // FileDescriptor message fields needs to remain valid until the message is
514  // sent or else the sendmsg() call will fail.
515  if ((handle_out->fd = HANDLE_EINTR(dup(handle_in.fd))) < 0) {
516    PLOG(ERROR) << "dup()";
517    return;
518  }
519  handle_out->auto_close = true;
520#elif defined(OS_WIN)
521  // On Windows we need to duplicate the handle for the plugin process.
522  *handle_out = NULL;
523  BrokerDuplicateHandle(handle_in, peer_pid, handle_out,
524                        FILE_MAP_READ | FILE_MAP_WRITE, 0);
525  DCHECK(*handle_out != NULL);
526#else
527  // Don't need to do anything special for other platforms.
528  *handle_out = handle_in;
529#endif
530}
531
532void WebPluginDelegateProxy::SendUpdateGeometry(
533    bool bitmaps_changed) {
534  PluginMsg_UpdateGeometry_Param param;
535  param.window_rect = plugin_rect_;
536  param.clip_rect = clip_rect_;
537  param.windowless_buffer0 = TransportDIB::DefaultHandleValue();
538  param.windowless_buffer1 = TransportDIB::DefaultHandleValue();
539  param.windowless_buffer_index = back_buffer_index();
540
541#if defined(OS_POSIX)
542  // If we're using POSIX mmap'd TransportDIBs, sending the handle across
543  // IPC establishes a new mapping rather than just sending a window ID,
544  // so only do so if we've actually changed the shared memory bitmaps.
545  if (bitmaps_changed)
546#endif
547  {
548    if (transport_stores_[0].dib)
549      CopyTransportDIBHandleForMessage(transport_stores_[0].dib->handle(),
550                                       &param.windowless_buffer0,
551                                       channel_host_->peer_pid());
552
553    if (transport_stores_[1].dib)
554      CopyTransportDIBHandleForMessage(transport_stores_[1].dib->handle(),
555                                       &param.windowless_buffer1,
556                                       channel_host_->peer_pid());
557  }
558
559  IPC::Message* msg;
560#if defined(OS_WIN)
561  if (UseSynchronousGeometryUpdates()) {
562    msg = new PluginMsg_UpdateGeometrySync(instance_id_, param);
563  } else  // NOLINT
564#endif
565  {
566    msg = new PluginMsg_UpdateGeometry(instance_id_, param);
567    msg->set_unblock(true);
568  }
569
570  Send(msg);
571}
572
573void WebPluginDelegateProxy::UpdateGeometry(const gfx::Rect& window_rect,
574                                            const gfx::Rect& clip_rect) {
575  // window_rect becomes either a window in native windowing system
576  // coords, or a backing buffer.  In either case things will go bad
577  // if the rectangle is very large.
578  if (window_rect.width() < 0  || window_rect.width() > kMaxPluginSideLength ||
579      window_rect.height() < 0 || window_rect.height() > kMaxPluginSideLength ||
580      // We know this won't overflow due to above checks.
581      static_cast<uint32>(window_rect.width()) *
582          static_cast<uint32>(window_rect.height()) > kMaxPluginSize) {
583    return;
584  }
585
586  plugin_rect_ = window_rect;
587  clip_rect_ = clip_rect;
588
589  bool bitmaps_changed = false;
590
591  if (uses_shared_bitmaps_) {
592    if (!front_buffer_canvas() ||
593        (window_rect.width() != front_buffer_canvas()->getDevice()->width() ||
594         window_rect.height() != front_buffer_canvas()->getDevice()->height()))
595    {
596      bitmaps_changed = true;
597
598      // Create a shared memory section that the plugin paints into
599      // asynchronously.
600      ResetWindowlessBitmaps();
601      if (!window_rect.IsEmpty()) {
602        if (!CreateSharedBitmap(&transport_stores_[0].dib,
603                                &transport_stores_[0].canvas) ||
604            !CreateSharedBitmap(&transport_stores_[1].dib,
605                                &transport_stores_[1].canvas)) {
606          DCHECK(false);
607          ResetWindowlessBitmaps();
608          return;
609        }
610      }
611    }
612  }
613
614  SendUpdateGeometry(bitmaps_changed);
615}
616
617void WebPluginDelegateProxy::ResetWindowlessBitmaps() {
618  transport_stores_[0].dib.reset();
619  transport_stores_[1].dib.reset();
620
621  transport_stores_[0].canvas.reset();
622  transport_stores_[1].canvas.reset();
623  transport_store_painted_ = gfx::Rect();
624  front_buffer_diff_ = gfx::Rect();
625}
626
627static size_t BitmapSizeForPluginRect(const gfx::Rect& plugin_rect) {
628  const size_t stride =
629      skia::PlatformCanvasStrideForWidth(plugin_rect.width());
630  return stride * plugin_rect.height();
631}
632
633#if !defined(OS_WIN)
634bool WebPluginDelegateProxy::CreateLocalBitmap(
635    std::vector<uint8>* memory,
636    scoped_ptr<skia::PlatformCanvas>* canvas) {
637  const size_t size = BitmapSizeForPluginRect(plugin_rect_);
638  memory->resize(size);
639  if (memory->size() != size)
640    return false;
641  canvas->reset(skia::CreatePlatformCanvas(
642      plugin_rect_.width(), plugin_rect_.height(), true, &((*memory)[0]),
643      skia::CRASH_ON_FAILURE));
644  return true;
645}
646#endif
647
648bool WebPluginDelegateProxy::CreateSharedBitmap(
649    scoped_ptr<TransportDIB>* memory,
650    scoped_ptr<skia::PlatformCanvas>* canvas) {
651  const size_t size = BitmapSizeForPluginRect(plugin_rect_);
652#if defined(OS_POSIX) && !defined(OS_MACOSX)
653  memory->reset(TransportDIB::Create(size, 0));
654  if (!memory->get())
655    return false;
656#endif
657#if defined(OS_POSIX) && !defined(TOOLKIT_GTK) && !defined(OS_ANDROID)
658  TransportDIB::Handle handle;
659  IPC::Message* msg = new ViewHostMsg_AllocTransportDIB(size, false, &handle);
660  if (!RenderThreadImpl::current()->Send(msg))
661    return false;
662  if (handle.fd < 0)
663    return false;
664  memory->reset(TransportDIB::Map(handle));
665#else
666  static uint32 sequence_number = 0;
667  memory->reset(TransportDIB::Create(size, sequence_number++));
668#endif
669  canvas->reset((*memory)->GetPlatformCanvas(plugin_rect_.width(),
670                                             plugin_rect_.height()));
671  return !!canvas->get();
672}
673
674void WebPluginDelegateProxy::Paint(SkCanvas* canvas,
675                                   const gfx::Rect& damaged_rect) {
676  // Limit the damaged rectangle to whatever is contained inside the plugin
677  // rectangle, as that's the rectangle that we'll actually draw.
678  gfx::Rect rect = gfx::IntersectRects(damaged_rect, plugin_rect_);
679
680  // If the plugin is no longer connected (channel crashed) draw a crashed
681  // plugin bitmap
682  if (!channel_host_.get() || !channel_host_->channel_valid()) {
683    // Lazily load the sad plugin image.
684    if (!sad_plugin_)
685      sad_plugin_ = GetContentClient()->renderer()->GetSadPluginBitmap();
686    if (sad_plugin_)
687      PaintSadPlugin(canvas, plugin_rect_, *sad_plugin_);
688    return;
689  }
690
691  if (!uses_shared_bitmaps_)
692    return;
693
694  // We got a paint before the plugin's coordinates, so there's no buffer to
695  // copy from.
696  if (!front_buffer_canvas())
697    return;
698
699  gfx::Rect offset_rect = rect;
700  offset_rect.Offset(-plugin_rect_.x(), -plugin_rect_.y());
701
702  // transport_store_painted_ is really a bounding box, so in principle this
703  // check could falsely indicate that we don't need to paint offset_rect, but
704  // in practice it works fine.
705  if (!transport_store_painted_.Contains(offset_rect)) {
706    Send(new PluginMsg_Paint(instance_id_, offset_rect));
707    // Since the plugin is not blocked on the renderer in this context, there is
708    // a chance that it will begin repainting the back-buffer before we complete
709    // capturing the data. Buffer flipping would increase that risk because
710    // geometry update is asynchronous, so we don't want to use buffer flipping
711    // here.
712    UpdateFrontBuffer(offset_rect, false);
713  }
714
715  const SkBitmap& bitmap =
716      front_buffer_canvas()->getDevice()->accessBitmap(false);
717  SkPaint paint;
718  paint.setXfermodeMode(
719      transparent_ ? SkXfermode::kSrcATop_Mode : SkXfermode::kSrc_Mode);
720  SkIRect src_rect = gfx::RectToSkIRect(offset_rect);
721  canvas->drawBitmapRect(bitmap,
722                         &src_rect,
723                         gfx::RectToSkRect(rect),
724                         &paint);
725
726  if (invalidate_pending_) {
727    // Only send the PaintAck message if this paint is in response to an
728    // invalidate from the plugin, since this message acts as an access token
729    // to ensure only one process is using the transport dib at a time.
730    invalidate_pending_ = false;
731    Send(new PluginMsg_DidPaint(instance_id_));
732  }
733}
734
735NPObject* WebPluginDelegateProxy::GetPluginScriptableObject() {
736  if (npobject_)
737    return WebBindings::retainObject(npobject_);
738
739  int route_id = MSG_ROUTING_NONE;
740  Send(new PluginMsg_GetPluginScriptableObject(instance_id_, &route_id));
741  if (route_id == MSG_ROUTING_NONE)
742    return NULL;
743
744  npobject_ = NPObjectProxy::Create(
745      channel_host_.get(), route_id, 0, page_url_, GetPluginNPP());
746
747  return WebBindings::retainObject(npobject_);
748}
749
750NPP WebPluginDelegateProxy::GetPluginNPP() {
751  // Return a dummy NPP for WebKit to use to identify this plugin.
752  return npp_.get();
753}
754
755bool WebPluginDelegateProxy::GetFormValue(base::string16* value) {
756  bool success = false;
757  Send(new PluginMsg_GetFormValue(instance_id_, value, &success));
758  return success;
759}
760
761void WebPluginDelegateProxy::DidFinishLoadWithReason(
762    const GURL& url, NPReason reason, int notify_id) {
763  Send(new PluginMsg_DidFinishLoadWithReason(
764      instance_id_, url, reason, notify_id));
765}
766
767void WebPluginDelegateProxy::SetFocus(bool focused) {
768  Send(new PluginMsg_SetFocus(instance_id_, focused));
769#if defined(OS_WIN)
770  if (render_view_)
771    render_view_->PluginFocusChanged(focused, instance_id_);
772#endif
773}
774
775bool WebPluginDelegateProxy::HandleInputEvent(
776    const WebInputEvent& event,
777    WebCursor::CursorInfo* cursor_info) {
778  bool handled;
779  WebCursor cursor;
780  // A windowless plugin can enter a modal loop in the context of a
781  // NPP_HandleEvent call, in which case we need to pump messages to
782  // the plugin. We pass of the corresponding event handle to the
783  // plugin process, which is set if the plugin does enter a modal loop.
784  IPC::SyncMessage* message = new PluginMsg_HandleInputEvent(
785      instance_id_, &event, &handled, &cursor);
786  message->set_pump_messages_event(modal_loop_pump_messages_event_.get());
787  Send(message);
788  return handled;
789}
790
791int WebPluginDelegateProxy::GetProcessId() {
792  return channel_host_->peer_pid();
793}
794
795void WebPluginDelegateProxy::SetContentAreaFocus(bool has_focus) {
796  IPC::Message* msg = new PluginMsg_SetContentAreaFocus(instance_id_,
797                                                        has_focus);
798  // Make sure focus events are delivered in the right order relative to
799  // sync messages they might interact with (Paint, HandleEvent, etc.).
800  msg->set_unblock(true);
801  Send(msg);
802}
803
804#if defined(OS_WIN)
805void WebPluginDelegateProxy::ImeCompositionUpdated(
806    const base::string16& text,
807    const std::vector<int>& clauses,
808    const std::vector<int>& target,
809    int cursor_position,
810    int plugin_id) {
811  // Dispatch the raw IME data if this plug-in is the focused one.
812  if (instance_id_ != plugin_id)
813    return;
814
815  IPC::Message* msg = new PluginMsg_ImeCompositionUpdated(instance_id_,
816      text, clauses, target, cursor_position);
817  msg->set_unblock(true);
818  Send(msg);
819}
820
821void WebPluginDelegateProxy::ImeCompositionCompleted(const base::string16& text,
822                                                     int plugin_id) {
823  // Dispatch the IME text if this plug-in is the focused one.
824  if (instance_id_ != plugin_id)
825    return;
826
827  IPC::Message* msg = new PluginMsg_ImeCompositionCompleted(instance_id_, text);
828  msg->set_unblock(true);
829  Send(msg);
830}
831#endif
832
833#if defined(OS_MACOSX)
834void WebPluginDelegateProxy::SetWindowFocus(bool window_has_focus) {
835  IPC::Message* msg = new PluginMsg_SetWindowFocus(instance_id_,
836                                                   window_has_focus);
837  // Make sure focus events are delivered in the right order relative to
838  // sync messages they might interact with (Paint, HandleEvent, etc.).
839  msg->set_unblock(true);
840  Send(msg);
841}
842
843void WebPluginDelegateProxy::SetContainerVisibility(bool is_visible) {
844  IPC::Message* msg;
845  if (is_visible) {
846    gfx::Rect window_frame = render_view_->rootWindowRect();
847    gfx::Rect view_frame = render_view_->windowRect();
848    blink::WebView* webview = render_view_->webview();
849    msg = new PluginMsg_ContainerShown(instance_id_, window_frame, view_frame,
850                                       webview && webview->isActive());
851  } else {
852    msg = new PluginMsg_ContainerHidden(instance_id_);
853  }
854  // Make sure visibility events are delivered in the right order relative to
855  // sync messages they might interact with (Paint, HandleEvent, etc.).
856  msg->set_unblock(true);
857  Send(msg);
858}
859
860void WebPluginDelegateProxy::WindowFrameChanged(gfx::Rect window_frame,
861                                                gfx::Rect view_frame) {
862  IPC::Message* msg = new PluginMsg_WindowFrameChanged(instance_id_,
863                                                       window_frame,
864                                                       view_frame);
865  // Make sure frame events are delivered in the right order relative to
866  // sync messages they might interact with (e.g., HandleEvent).
867  msg->set_unblock(true);
868  Send(msg);
869}
870void WebPluginDelegateProxy::ImeCompositionCompleted(const base::string16& text,
871                                                     int plugin_id) {
872  // If the message isn't intended for this plugin, there's nothing to do.
873  if (instance_id_ != plugin_id)
874    return;
875
876  IPC::Message* msg = new PluginMsg_ImeCompositionCompleted(instance_id_,
877                                                            text);
878  // Order relative to other key events is important.
879  msg->set_unblock(true);
880  Send(msg);
881}
882#endif  // OS_MACOSX
883
884void WebPluginDelegateProxy::OnSetWindow(gfx::PluginWindowHandle window) {
885#if defined(OS_MACOSX)
886  uses_shared_bitmaps_ = !window && !uses_compositor_;
887#else
888  uses_shared_bitmaps_ = !window;
889#endif
890  window_ = window;
891  if (plugin_)
892    plugin_->SetWindow(window);
893}
894
895void WebPluginDelegateProxy::WillDestroyWindow() {
896  DCHECK(window_);
897  plugin_->WillDestroyWindow(window_);
898  window_ = gfx::kNullPluginWindow;
899}
900
901#if defined(OS_WIN)
902void WebPluginDelegateProxy::OnSetWindowlessData(
903      HANDLE modal_loop_pump_messages_event,
904      gfx::NativeViewId dummy_activation_window) {
905  DCHECK(modal_loop_pump_messages_event_ == NULL);
906  DCHECK(dummy_activation_window_ == NULL);
907
908  dummy_activation_window_ = dummy_activation_window;
909  render_view_->Send(new ViewHostMsg_WindowlessPluginDummyWindowCreated(
910      render_view_->routing_id(), dummy_activation_window_));
911
912  // Bug 25583: this can be null because some "virus scanners" block the
913  // DuplicateHandle call in the plugin process.
914  if (!modal_loop_pump_messages_event)
915    return;
916
917  modal_loop_pump_messages_event_.reset(
918      new base::WaitableEvent(modal_loop_pump_messages_event));
919}
920
921void WebPluginDelegateProxy::OnNotifyIMEStatus(int input_type,
922                                               const gfx::Rect& caret_rect) {
923  if (!render_view_)
924    return;
925
926  render_view_->Send(new ViewHostMsg_TextInputTypeChanged(
927      render_view_->routing_id(),
928      static_cast<ui::TextInputType>(input_type),
929      ui::TEXT_INPUT_MODE_DEFAULT,
930      true));
931
932  ViewHostMsg_SelectionBounds_Params bounds_params;
933  bounds_params.anchor_rect = bounds_params.focus_rect = caret_rect;
934  bounds_params.anchor_dir = bounds_params.focus_dir =
935      blink::WebTextDirectionLeftToRight;
936  bounds_params.is_anchor_first = true;
937  render_view_->Send(new ViewHostMsg_SelectionBoundsChanged(
938      render_view_->routing_id(),
939      bounds_params));
940}
941#endif
942
943void WebPluginDelegateProxy::OnCancelResource(int id) {
944  if (plugin_)
945    plugin_->CancelResource(id);
946}
947
948void WebPluginDelegateProxy::OnInvalidateRect(const gfx::Rect& rect) {
949  if (!plugin_)
950    return;
951
952  // Clip the invalidation rect to the plugin bounds; the plugin may have been
953  // resized since the invalidate message was sent.
954  gfx::Rect clipped_rect =
955      gfx::IntersectRects(rect, gfx::Rect(plugin_rect_.size()));
956
957  invalidate_pending_ = true;
958  // The plugin is blocked on the renderer because the invalidate message it has
959  // sent us is synchronous, so we can use buffer flipping here if the caller
960  // allows it.
961  UpdateFrontBuffer(clipped_rect, true);
962  plugin_->InvalidateRect(clipped_rect);
963}
964
965void WebPluginDelegateProxy::OnGetWindowScriptNPObject(
966    int route_id, bool* success) {
967  *success = false;
968  NPObject* npobject = NULL;
969  if (plugin_)
970    npobject = plugin_->GetWindowScriptNPObject();
971
972  if (!npobject)
973    return;
974
975  // The stub will delete itself when the proxy tells it that it's released, or
976  // otherwise when the channel is closed.
977  new NPObjectStub(npobject, channel_host_.get(), route_id, 0, page_url_);
978  *success = true;
979}
980
981void WebPluginDelegateProxy::OnResolveProxy(const GURL& url,
982                                            bool* result,
983                                            std::string* proxy_list) {
984  *result = RenderThreadImpl::current()->ResolveProxy(url, proxy_list);
985}
986
987void WebPluginDelegateProxy::OnGetPluginElement(int route_id, bool* success) {
988  *success = false;
989  NPObject* npobject = NULL;
990  if (plugin_)
991    npobject = plugin_->GetPluginElement();
992  if (!npobject)
993    return;
994
995  // The stub will delete itself when the proxy tells it that it's released, or
996  // otherwise when the channel is closed.
997  new NPObjectStub(
998      npobject, channel_host_.get(), route_id, 0, page_url_);
999  *success = true;
1000}
1001
1002void WebPluginDelegateProxy::OnSetCookie(const GURL& url,
1003                                         const GURL& first_party_for_cookies,
1004                                         const std::string& cookie) {
1005  if (plugin_)
1006    plugin_->SetCookie(url, first_party_for_cookies, cookie);
1007}
1008
1009void WebPluginDelegateProxy::OnGetCookies(const GURL& url,
1010                                          const GURL& first_party_for_cookies,
1011                                          std::string* cookies) {
1012  DCHECK(cookies);
1013  if (plugin_)
1014    *cookies = plugin_->GetCookies(url, first_party_for_cookies);
1015}
1016
1017void WebPluginDelegateProxy::CopyFromBackBufferToFrontBuffer(
1018    const gfx::Rect& rect) {
1019#if defined(OS_MACOSX)
1020  // Blitting the bits directly is much faster than going through CG, and since
1021  // the goal is just to move the raw pixels between two bitmaps with the same
1022  // pixel format (no compositing, color correction, etc.), it's safe.
1023  const size_t stride =
1024      skia::PlatformCanvasStrideForWidth(plugin_rect_.width());
1025  const size_t chunk_size = 4 * rect.width();
1026  DCHECK(back_buffer_dib() != NULL);
1027  uint8* source_data = static_cast<uint8*>(back_buffer_dib()->memory()) +
1028                       rect.y() * stride + 4 * rect.x();
1029  DCHECK(front_buffer_dib() != NULL);
1030  uint8* target_data = static_cast<uint8*>(front_buffer_dib()->memory()) +
1031                       rect.y() * stride + 4 * rect.x();
1032  for (int row = 0; row < rect.height(); ++row) {
1033    memcpy(target_data, source_data, chunk_size);
1034    source_data += stride;
1035    target_data += stride;
1036  }
1037#else
1038  BlitCanvasToCanvas(front_buffer_canvas(),
1039                     rect,
1040                     back_buffer_canvas(),
1041                     rect.origin());
1042#endif
1043}
1044
1045void WebPluginDelegateProxy::UpdateFrontBuffer(
1046    const gfx::Rect& rect,
1047    bool allow_buffer_flipping) {
1048  if (!front_buffer_canvas()) {
1049    return;
1050  }
1051
1052#if defined(OS_WIN)
1053  // If SendUpdateGeometry() would block on the plugin process then we don't
1054  // want to use buffer flipping at all since it would add extra locking.
1055  // (Alternatively we could probably safely use async updates for buffer
1056  // flipping all the time since the size is not changing.)
1057  if (UseSynchronousGeometryUpdates()) {
1058    allow_buffer_flipping = false;
1059  }
1060#endif
1061
1062  // Plugin has just painted "rect" into the back-buffer, so the front-buffer
1063  // no longer holds the latest content for that rectangle.
1064  front_buffer_diff_.Subtract(rect);
1065  if (allow_buffer_flipping && front_buffer_diff_.IsEmpty()) {
1066    // Back-buffer contains the latest content for all areas; simply flip
1067    // the buffers.
1068    front_buffer_index_ = back_buffer_index();
1069    SendUpdateGeometry(false);
1070    // The front-buffer now holds newer content for this region than the
1071    // back-buffer.
1072    front_buffer_diff_ = rect;
1073  } else {
1074    // Back-buffer contains the latest content for "rect" but the front-buffer
1075    // contains the latest content for some other areas (or buffer flipping not
1076    // allowed); fall back to copying the data.
1077    CopyFromBackBufferToFrontBuffer(rect);
1078  }
1079  transport_store_painted_.Union(rect);
1080}
1081
1082void WebPluginDelegateProxy::OnHandleURLRequest(
1083    const PluginHostMsg_URLRequest_Params& params) {
1084  const char* data = NULL;
1085  if (params.buffer.size())
1086    data = &params.buffer[0];
1087
1088  const char* target = NULL;
1089  if (params.target.length())
1090    target = params.target.c_str();
1091
1092  plugin_->HandleURLRequest(
1093      params.url.c_str(), params.method.c_str(), target, data,
1094      static_cast<unsigned int>(params.buffer.size()), params.notify_id,
1095      params.popups_allowed, params.notify_redirects);
1096}
1097
1098WebPluginResourceClient* WebPluginDelegateProxy::CreateResourceClient(
1099    unsigned long resource_id, const GURL& url, int notify_id) {
1100  if (!channel_host_.get())
1101    return NULL;
1102
1103  ResourceClientProxy* proxy =
1104      new ResourceClientProxy(channel_host_.get(), instance_id_);
1105  proxy->Initialize(resource_id, url, notify_id);
1106  return proxy;
1107}
1108
1109WebPluginResourceClient* WebPluginDelegateProxy::CreateSeekableResourceClient(
1110    unsigned long resource_id, int range_request_id) {
1111  if (!channel_host_.get())
1112    return NULL;
1113
1114  ResourceClientProxy* proxy =
1115      new ResourceClientProxy(channel_host_.get(), instance_id_);
1116  proxy->InitializeForSeekableStream(resource_id, range_request_id);
1117  return proxy;
1118}
1119
1120void WebPluginDelegateProxy::FetchURL(unsigned long resource_id,
1121                                      int notify_id,
1122                                      const GURL& url,
1123                                      const GURL& first_party_for_cookies,
1124                                      const std::string& method,
1125                                      const char* buf,
1126                                      unsigned int len,
1127                                      const GURL& referrer,
1128                                      bool notify_redirects,
1129                                      bool is_plugin_src_load,
1130                                      int origin_pid,
1131                                      int render_frame_id,
1132                                      int render_view_id) {
1133  PluginMsg_FetchURL_Params params;
1134  params.resource_id = resource_id;
1135  params.notify_id = notify_id;
1136  params.url = url;
1137  params.first_party_for_cookies = first_party_for_cookies;
1138  params.method = method;
1139  if (len) {
1140    params.post_data.resize(len);
1141    memcpy(&params.post_data.front(), buf, len);
1142  }
1143  params.referrer = referrer;
1144  params.notify_redirect = notify_redirects;
1145  params.is_plugin_src_load = is_plugin_src_load;
1146  params.render_frame_id = render_frame_id;
1147  Send(new PluginMsg_FetchURL(instance_id_, params));
1148}
1149
1150#if defined(OS_MACOSX)
1151void WebPluginDelegateProxy::OnFocusChanged(bool focused) {
1152  if (render_view_)
1153    render_view_->PluginFocusChanged(focused, instance_id_);
1154}
1155
1156void WebPluginDelegateProxy::OnStartIme() {
1157  if (render_view_)
1158    render_view_->StartPluginIme();
1159}
1160#endif
1161
1162gfx::PluginWindowHandle WebPluginDelegateProxy::GetPluginWindowHandle() {
1163  return window_;
1164}
1165
1166void WebPluginDelegateProxy::OnCancelDocumentLoad() {
1167  plugin_->CancelDocumentLoad();
1168}
1169
1170void WebPluginDelegateProxy::OnInitiateHTTPRangeRequest(
1171    const std::string& url,
1172    const std::string& range_info,
1173    int range_request_id) {
1174  plugin_->InitiateHTTPRangeRequest(
1175      url.c_str(), range_info.c_str(), range_request_id);
1176}
1177
1178void WebPluginDelegateProxy::OnDidStartLoading() {
1179  plugin_->DidStartLoading();
1180}
1181
1182void WebPluginDelegateProxy::OnDidStopLoading() {
1183  plugin_->DidStopLoading();
1184}
1185
1186void WebPluginDelegateProxy::OnDeferResourceLoading(unsigned long resource_id,
1187                                                    bool defer) {
1188  plugin_->SetDeferResourceLoading(resource_id, defer);
1189}
1190
1191#if defined(OS_MACOSX)
1192void WebPluginDelegateProxy::OnAcceleratedPluginEnabledRendering() {
1193  uses_compositor_ = true;
1194  OnSetWindow(gfx::kNullPluginWindow);
1195}
1196
1197void WebPluginDelegateProxy::OnAcceleratedPluginAllocatedIOSurface(
1198    int32 width,
1199    int32 height,
1200    uint32 surface_id) {
1201  if (plugin_)
1202    plugin_->AcceleratedPluginAllocatedIOSurface(width, height, surface_id);
1203}
1204
1205void WebPluginDelegateProxy::OnAcceleratedPluginSwappedIOSurface() {
1206  if (plugin_)
1207    plugin_->AcceleratedPluginSwappedIOSurface();
1208}
1209#endif
1210
1211#if defined(OS_WIN)
1212bool WebPluginDelegateProxy::UseSynchronousGeometryUpdates() {
1213  // Need to update geometry synchronously with WMP, otherwise if a site
1214  // scripts the plugin to start playing while it's in the middle of handling
1215  // an update geometry message, videos don't play.  See urls in bug 20260.
1216  if (info_.name.find(base::ASCIIToUTF16("Windows Media Player")) !=
1217      base::string16::npos)
1218    return true;
1219
1220  // The move networks plugin needs to be informed of geometry updates
1221  // synchronously.
1222  std::vector<WebPluginMimeType>::iterator index;
1223  for (index = info_.mime_types.begin(); index != info_.mime_types.end();
1224       index++) {
1225    if (index->mime_type == "application/x-vnd.moveplayer.qm" ||
1226        index->mime_type == "application/x-vnd.moveplay2.qm" ||
1227        index->mime_type == "application/x-vnd.movenetworks.qm" ||
1228        index->mime_type == "application/x-vnd.mnplayer.qm") {
1229      return true;
1230    }
1231  }
1232  return false;
1233}
1234#endif
1235
1236void WebPluginDelegateProxy::OnURLRedirectResponse(bool allow,
1237                                                   int resource_id) {
1238  if (!plugin_)
1239    return;
1240
1241  plugin_->URLRedirectResponse(allow, resource_id);
1242}
1243
1244void WebPluginDelegateProxy::OnCheckIfRunInsecureContent(const GURL& url,
1245                                                         bool* result) {
1246  *result = plugin_->CheckIfRunInsecureContent(url);
1247}
1248
1249}  // namespace content
1250