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#include <algorithm>
8
9#include "base/auto_reset.h"
10#include "base/basictypes.h"
11#include "base/command_line.h"
12#include "base/files/file_util.h"
13#include "base/logging.h"
14#include "base/memory/ref_counted.h"
15#include "base/memory/scoped_ptr.h"
16#include "base/process/process.h"
17#include "base/strings/string_split.h"
18#include "base/strings/string_util.h"
19#include "base/strings/utf_string_conversions.h"
20#include "base/version.h"
21#include "content/child/child_process.h"
22#include "content/child/npapi/npobject_proxy.h"
23#include "content/child/npapi/npobject_stub.h"
24#include "content/child/npapi/npobject_util.h"
25#include "content/child/npapi/webplugin_resource_client.h"
26#include "content/child/plugin_messages.h"
27#include "content/common/content_constants_internal.h"
28#include "content/common/cursors/webcursor.h"
29#include "content/common/frame_messages.h"
30#include "content/common/view_messages.h"
31#include "content/public/renderer/content_renderer_client.h"
32#include "content/renderer/npapi/plugin_channel_host.h"
33#include "content/renderer/npapi/webplugin_impl.h"
34#include "content/renderer/render_thread_impl.h"
35#include "content/renderer/render_view_impl.h"
36#include "content/renderer/sad_plugin.h"
37#include "ipc/ipc_channel_handle.h"
38#include "net/base/mime_util.h"
39#include "skia/ext/platform_canvas.h"
40#include "third_party/WebKit/public/platform/WebDragData.h"
41#include "third_party/WebKit/public/platform/WebString.h"
42#include "third_party/WebKit/public/web/WebBindings.h"
43#include "third_party/WebKit/public/web/WebDocument.h"
44#include "third_party/WebKit/public/web/WebFrame.h"
45#include "third_party/WebKit/public/web/WebView.h"
46#include "ui/gfx/blit.h"
47#include "ui/gfx/canvas.h"
48#include "ui/gfx/native_widget_types.h"
49#include "ui/gfx/size.h"
50#include "ui/gfx/skia_util.h"
51
52#if defined(OS_POSIX)
53#include "ipc/ipc_channel_posix.h"
54#endif
55
56#if defined(OS_MACOSX)
57#include "base/mac/mac_util.h"
58#endif
59
60#if defined(OS_WIN)
61#include "content/public/common/sandbox_init.h"
62#endif
63
64using blink::WebBindings;
65using blink::WebCursorInfo;
66using blink::WebDragData;
67using blink::WebInputEvent;
68using blink::WebString;
69using blink::WebView;
70
71namespace content {
72
73namespace {
74
75class ScopedLogLevel {
76 public:
77  explicit ScopedLogLevel(int level);
78  ~ScopedLogLevel();
79
80 private:
81  int old_level_;
82
83  DISALLOW_COPY_AND_ASSIGN(ScopedLogLevel);
84};
85
86ScopedLogLevel::ScopedLogLevel(int level)
87    : old_level_(logging::GetMinLogLevel()) {
88  logging::SetMinLogLevel(level);
89}
90
91ScopedLogLevel::~ScopedLogLevel() {
92  logging::SetMinLogLevel(old_level_);
93}
94
95// Proxy for WebPluginResourceClient.  The object owns itself after creation,
96// deleting itself after its callback has been called.
97class ResourceClientProxy : public WebPluginResourceClient {
98 public:
99  ResourceClientProxy(PluginChannelHost* channel, int instance_id)
100    : channel_(channel), instance_id_(instance_id), resource_id_(0),
101      multibyte_response_expected_(false) {
102  }
103
104  virtual ~ResourceClientProxy() {
105  }
106
107  void Initialize(unsigned long resource_id, const GURL& url, int notify_id) {
108    resource_id_ = resource_id;
109    channel_->Send(new PluginMsg_HandleURLRequestReply(
110        instance_id_, resource_id, url, notify_id));
111  }
112
113  void InitializeForSeekableStream(unsigned long resource_id,
114                                   int range_request_id) {
115    resource_id_ = resource_id;
116    multibyte_response_expected_ = true;
117    channel_->Send(new PluginMsg_HTTPRangeRequestReply(
118        instance_id_, resource_id, range_request_id));
119  }
120
121  // PluginResourceClient implementation:
122  virtual void WillSendRequest(const GURL& url, int http_status_code) OVERRIDE {
123    DCHECK(channel_.get() != NULL);
124    channel_->Send(new PluginMsg_WillSendRequest(
125        instance_id_, resource_id_, url, http_status_code));
126  }
127
128  virtual void DidReceiveResponse(const std::string& mime_type,
129                                  const std::string& headers,
130                                  uint32 expected_length,
131                                  uint32 last_modified,
132                                  bool request_is_seekable) OVERRIDE {
133    DCHECK(channel_.get() != NULL);
134    PluginMsg_DidReceiveResponseParams params;
135    params.id = resource_id_;
136    params.mime_type = mime_type;
137    params.headers = headers;
138    params.expected_length = expected_length;
139    params.last_modified = last_modified;
140    params.request_is_seekable = request_is_seekable;
141    // Grab a reference on the underlying channel so it does not get
142    // deleted from under us.
143    scoped_refptr<PluginChannelHost> channel_ref(channel_);
144    channel_->Send(new PluginMsg_DidReceiveResponse(instance_id_, params));
145  }
146
147  virtual void DidReceiveData(const char* buffer,
148                              int length,
149                              int data_offset) OVERRIDE {
150    DCHECK(channel_.get() != NULL);
151    DCHECK_GT(length, 0);
152    std::vector<char> data;
153    data.resize(static_cast<size_t>(length));
154    memcpy(&data.front(), buffer, length);
155    // Grab a reference on the underlying channel so it does not get
156    // deleted from under us.
157    scoped_refptr<PluginChannelHost> channel_ref(channel_);
158    channel_->Send(new PluginMsg_DidReceiveData(instance_id_, resource_id_,
159                                                data, data_offset));
160  }
161
162  virtual void DidFinishLoading(unsigned long resource_id) OVERRIDE {
163    DCHECK(channel_.get() != NULL);
164    DCHECK_EQ(resource_id, resource_id_);
165    channel_->Send(new PluginMsg_DidFinishLoading(instance_id_, resource_id_));
166    channel_ = NULL;
167    base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
168  }
169
170  virtual void DidFail(unsigned long resource_id) OVERRIDE {
171    DCHECK(channel_.get() != NULL);
172    DCHECK_EQ(resource_id, resource_id_);
173    channel_->Send(new PluginMsg_DidFail(instance_id_, resource_id_));
174    channel_ = NULL;
175    base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
176  }
177
178  virtual bool IsMultiByteResponseExpected() OVERRIDE {
179    return multibyte_response_expected_;
180  }
181
182  virtual int ResourceId() OVERRIDE {
183    return resource_id_;
184  }
185
186 private:
187  scoped_refptr<PluginChannelHost> channel_;
188  int instance_id_;
189  unsigned long resource_id_;
190  // Set to true if the response expected is a multibyte response.
191  // For e.g. response for a HTTP byte range request.
192  bool multibyte_response_expected_;
193};
194
195}  // namespace
196
197WebPluginDelegateProxy::WebPluginDelegateProxy(
198    WebPluginImpl* plugin,
199    const std::string& mime_type,
200    const base::WeakPtr<RenderViewImpl>& render_view,
201    RenderFrameImpl* render_frame)
202    : render_view_(render_view),
203      render_frame_(render_frame),
204      plugin_(plugin),
205      uses_shared_bitmaps_(false),
206#if defined(OS_MACOSX)
207      uses_compositor_(false),
208#elif defined(OS_WIN)
209      dummy_activation_window_(NULL),
210#endif
211      window_(gfx::kNullPluginWindow),
212      mime_type_(mime_type),
213      instance_id_(MSG_ROUTING_NONE),
214      npobject_(NULL),
215      npp_(new NPP_t),
216      sad_plugin_(NULL),
217      invalidate_pending_(false),
218      transparent_(false),
219      front_buffer_index_(0),
220      page_url_(render_view_->webview()->mainFrame()->document().url()) {
221}
222
223WebPluginDelegateProxy::~WebPluginDelegateProxy() {
224  if (npobject_)
225    WebBindings::releaseObject(npobject_);
226}
227
228WebPluginDelegateProxy::SharedBitmap::SharedBitmap() {}
229
230WebPluginDelegateProxy::SharedBitmap::~SharedBitmap() {}
231
232void WebPluginDelegateProxy::PluginDestroyed() {
233#if defined(OS_MACOSX) || defined(OS_WIN)
234  // Ensure that the renderer doesn't think the plugin still has focus.
235  if (render_view_)
236    render_view_->PluginFocusChanged(false, instance_id_);
237#endif
238
239#if defined(OS_WIN)
240  if (dummy_activation_window_ && render_view_) {
241    render_view_->Send(new ViewHostMsg_WindowlessPluginDummyWindowDestroyed(
242        render_view_->routing_id(), dummy_activation_window_));
243  }
244  dummy_activation_window_ = NULL;
245#endif
246
247  if (window_)
248    WillDestroyWindow();
249
250  if (render_view_.get())
251    render_view_->UnregisterPluginDelegate(this);
252
253  if (channel_host_.get()) {
254    Send(new PluginMsg_DestroyInstance(instance_id_));
255
256    // Must remove the route after sending the destroy message, rather than
257    // before, since RemoveRoute can lead to all the outstanding NPObjects
258    // being told the channel went away if this was the last instance.
259    channel_host_->RemoveRoute(instance_id_);
260
261    // Remove the mapping between our instance-Id and NPP identifiers, used by
262    // the channel to track object ownership, before releasing it.
263    channel_host_->RemoveMappingForNPObjectOwner(instance_id_);
264
265    // Release the channel host now. If we are is the last reference to the
266    // channel, this avoids a race where this renderer asks a new connection to
267    // the same plugin between now and the time 'this' is actually deleted.
268    // Destroying the channel host is what releases the channel name -> FD
269    // association on POSIX, and if we ask for a new connection before it is
270    // released, the plugin will give us a new FD, and we'll assert when trying
271    // to associate it with the channel name.
272    channel_host_ = NULL;
273  }
274
275  plugin_ = NULL;
276
277  base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
278}
279
280bool WebPluginDelegateProxy::Initialize(
281    const GURL& url,
282    const std::vector<std::string>& arg_names,
283    const std::vector<std::string>& arg_values,
284    bool load_manually) {
285  // TODO(shess): Attempt to work around http://crbug.com/97285 and
286  // http://crbug.com/141055 by retrying the connection.  Reports seem
287  // to indicate that the plugin hasn't crashed, and that the problem
288  // is not 100% persistent.
289  const size_t kAttempts = 2;
290
291  bool result = false;
292  scoped_refptr<PluginChannelHost> channel_host;
293  int instance_id = 0;
294
295  for (size_t attempt = 0; !result && attempt < kAttempts; attempt++) {
296#if defined(OS_MACOSX)
297    // TODO(shess): Debugging for http://crbug.com/97285 .  See comment
298    // in plugin_channel_host.cc.
299    scoped_ptr<base::AutoReset<bool> > track_nested_removes(
300        new base::AutoReset<bool>(PluginChannelHost::GetRemoveTrackingFlag(),
301                                  true));
302#endif
303
304    IPC::ChannelHandle channel_handle;
305    if (!RenderThreadImpl::current()->Send(new FrameHostMsg_OpenChannelToPlugin(
306            render_frame_->GetRoutingID(), url, page_url_, mime_type_,
307            &channel_handle, &info_))) {
308      continue;
309    }
310
311    if (channel_handle.name.empty()) {
312      // We got an invalid handle.  Either the plugin couldn't be found (which
313      // shouldn't happen, since if we got here the plugin should exist) or the
314      // plugin crashed on initialization.
315      if (!info_.path.empty()) {
316        render_view_->GetMainRenderFrame()->PluginCrashed(
317            info_.path, base::kNullProcessId);
318        LOG(ERROR) << "Plug-in crashed on start";
319
320        // Return true so that the plugin widget is created and we can paint the
321        // crashed plugin there.
322        return true;
323      }
324      LOG(ERROR) << "Plug-in couldn't be found";
325      return false;
326    }
327
328    channel_host =
329        PluginChannelHost::GetPluginChannelHost(
330            channel_handle, ChildProcess::current()->io_message_loop_proxy());
331    if (!channel_host.get()) {
332      LOG(ERROR) << "Couldn't get PluginChannelHost";
333      continue;
334    }
335#if defined(OS_MACOSX)
336    track_nested_removes.reset();
337#endif
338
339    {
340      // TODO(bauerb): Debugging for http://crbug.com/141055.
341      ScopedLogLevel log_level(-2);  // Equivalent to --v=2
342      result = channel_host->Send(new PluginMsg_CreateInstance(
343          mime_type_, &instance_id));
344      if (!result) {
345        LOG(ERROR) << "Couldn't send PluginMsg_CreateInstance";
346        continue;
347      }
348    }
349  }
350
351  // Failed too often, give up.
352  if (!result)
353    return false;
354
355  channel_host_ = channel_host;
356  instance_id_ = instance_id;
357
358  channel_host_->AddRoute(instance_id_, this, NULL);
359
360  // Inform the channel of the mapping between our instance-Id and dummy NPP
361  // identifier, for use in object ownership tracking.
362  channel_host_->AddMappingForNPObjectOwner(instance_id_, GetPluginNPP());
363
364  // Now tell the PluginInstance in the plugin process to initialize.
365  PluginMsg_Init_Params params;
366  params.url = url;
367  params.page_url = page_url_;
368  params.arg_names = arg_names;
369  params.arg_values = arg_values;
370  params.host_render_view_routing_id = render_view_->routing_id();
371  params.load_manually = load_manually;
372
373  result = false;
374  Send(new PluginMsg_Init(instance_id_, params, &transparent_, &result));
375
376  if (!result)
377    LOG(WARNING) << "PluginMsg_Init returned false";
378
379  render_view_->RegisterPluginDelegate(this);
380
381  return result;
382}
383
384bool WebPluginDelegateProxy::Send(IPC::Message* msg) {
385  if (!channel_host_.get()) {
386    DLOG(WARNING) << "dropping message because channel host is null";
387    delete msg;
388    return false;
389  }
390
391  return channel_host_->Send(msg);
392}
393
394void WebPluginDelegateProxy::SendJavaScriptStream(const GURL& url,
395                                                  const std::string& result,
396                                                  bool success,
397                                                  int notify_id) {
398  Send(new PluginMsg_SendJavaScriptStream(
399      instance_id_, url, result, success, notify_id));
400}
401
402void WebPluginDelegateProxy::DidReceiveManualResponse(
403    const GURL& url, const std::string& mime_type,
404    const std::string& headers, uint32 expected_length,
405    uint32 last_modified) {
406  PluginMsg_DidReceiveResponseParams params;
407  params.id = 0;
408  params.mime_type = mime_type;
409  params.headers = headers;
410  params.expected_length = expected_length;
411  params.last_modified = last_modified;
412  Send(new PluginMsg_DidReceiveManualResponse(instance_id_, url, params));
413}
414
415void WebPluginDelegateProxy::DidReceiveManualData(const char* buffer,
416                                                  int length) {
417  DCHECK_GT(length, 0);
418  std::vector<char> data;
419  data.resize(static_cast<size_t>(length));
420  memcpy(&data.front(), buffer, length);
421  Send(new PluginMsg_DidReceiveManualData(instance_id_, data));
422}
423
424void WebPluginDelegateProxy::DidFinishManualLoading() {
425  Send(new PluginMsg_DidFinishManualLoading(instance_id_));
426}
427
428void WebPluginDelegateProxy::DidManualLoadFail() {
429  Send(new PluginMsg_DidManualLoadFail(instance_id_));
430}
431
432bool WebPluginDelegateProxy::OnMessageReceived(const IPC::Message& msg) {
433  GetContentClient()->SetActiveURL(page_url_);
434
435  bool handled = true;
436  IPC_BEGIN_MESSAGE_MAP(WebPluginDelegateProxy, msg)
437    IPC_MESSAGE_HANDLER(PluginHostMsg_SetWindow, OnSetWindow)
438    IPC_MESSAGE_HANDLER(PluginHostMsg_CancelResource, OnCancelResource)
439    IPC_MESSAGE_HANDLER(PluginHostMsg_InvalidateRect, OnInvalidateRect)
440    IPC_MESSAGE_HANDLER(PluginHostMsg_GetWindowScriptNPObject,
441                        OnGetWindowScriptNPObject)
442    IPC_MESSAGE_HANDLER(PluginHostMsg_GetPluginElement, OnGetPluginElement)
443    IPC_MESSAGE_HANDLER(PluginHostMsg_ResolveProxy, OnResolveProxy)
444    IPC_MESSAGE_HANDLER(PluginHostMsg_SetCookie, OnSetCookie)
445    IPC_MESSAGE_HANDLER(PluginHostMsg_GetCookies, OnGetCookies)
446    IPC_MESSAGE_HANDLER(PluginHostMsg_URLRequest, OnHandleURLRequest)
447    IPC_MESSAGE_HANDLER(PluginHostMsg_CancelDocumentLoad, OnCancelDocumentLoad)
448    IPC_MESSAGE_HANDLER(PluginHostMsg_InitiateHTTPRangeRequest,
449                        OnInitiateHTTPRangeRequest)
450    IPC_MESSAGE_HANDLER(PluginHostMsg_DidStartLoading, OnDidStartLoading)
451    IPC_MESSAGE_HANDLER(PluginHostMsg_DidStopLoading, OnDidStopLoading)
452    IPC_MESSAGE_HANDLER(PluginHostMsg_DeferResourceLoading,
453                        OnDeferResourceLoading)
454    IPC_MESSAGE_HANDLER(PluginHostMsg_URLRedirectResponse,
455                        OnURLRedirectResponse)
456    IPC_MESSAGE_HANDLER(PluginHostMsg_CheckIfRunInsecureContent,
457                        OnCheckIfRunInsecureContent)
458#if defined(OS_WIN)
459    IPC_MESSAGE_HANDLER(PluginHostMsg_SetWindowlessData, OnSetWindowlessData)
460    IPC_MESSAGE_HANDLER(PluginHostMsg_NotifyIMEStatus, OnNotifyIMEStatus)
461#endif
462#if defined(OS_MACOSX)
463    IPC_MESSAGE_HANDLER(PluginHostMsg_FocusChanged,
464                        OnFocusChanged);
465    IPC_MESSAGE_HANDLER(PluginHostMsg_StartIme,
466                        OnStartIme);
467    IPC_MESSAGE_HANDLER(PluginHostMsg_AcceleratedPluginEnabledRendering,
468                        OnAcceleratedPluginEnabledRendering)
469    IPC_MESSAGE_HANDLER(PluginHostMsg_AcceleratedPluginAllocatedIOSurface,
470                        OnAcceleratedPluginAllocatedIOSurface)
471    IPC_MESSAGE_HANDLER(PluginHostMsg_AcceleratedPluginSwappedIOSurface,
472                        OnAcceleratedPluginSwappedIOSurface)
473#endif
474    IPC_MESSAGE_UNHANDLED(handled = false)
475  IPC_END_MESSAGE_MAP()
476  DCHECK(handled);
477  return handled;
478}
479
480void WebPluginDelegateProxy::OnChannelError() {
481  if (plugin_) {
482    if (window_) {
483      // The actual WebPluginDelegate never got a chance to tell the WebPlugin
484      // its window was going away. Do it on its behalf.
485      WillDestroyWindow();
486    }
487    plugin_->Invalidate();
488  }
489  if (channel_host_.get() && !channel_host_->expecting_shutdown()) {
490    render_view_->GetMainRenderFrame()->PluginCrashed(
491        info_.path, channel_host_->peer_pid());
492  }
493
494#if defined(OS_MACOSX) || defined(OS_WIN)
495  // Ensure that the renderer doesn't think the plugin still has focus.
496  if (render_view_)
497    render_view_->PluginFocusChanged(false, instance_id_);
498#endif
499}
500
501static void CopyTransportDIBHandleForMessage(
502    const TransportDIB::Handle& handle_in,
503    TransportDIB::Handle* handle_out,
504    base::ProcessId peer_pid) {
505#if defined(OS_MACOSX)
506  // On Mac, TransportDIB::Handle is typedef'ed to FileDescriptor, and
507  // FileDescriptor message fields needs to remain valid until the message is
508  // sent or else the sendmsg() call will fail.
509  if ((handle_out->fd = HANDLE_EINTR(dup(handle_in.fd))) < 0) {
510    PLOG(ERROR) << "dup()";
511    return;
512  }
513  handle_out->auto_close = true;
514#elif defined(OS_WIN)
515  // On Windows we need to duplicate the handle for the plugin process.
516  *handle_out = NULL;
517  BrokerDuplicateHandle(handle_in, peer_pid, handle_out,
518                        FILE_MAP_READ | FILE_MAP_WRITE, 0);
519  DCHECK(*handle_out != NULL);
520#else
521  // Don't need to do anything special for other platforms.
522  *handle_out = handle_in;
523#endif
524}
525
526void WebPluginDelegateProxy::SendUpdateGeometry(
527    bool bitmaps_changed) {
528  if (!channel_host_.get())
529    return;
530
531  PluginMsg_UpdateGeometry_Param param;
532  param.window_rect = plugin_rect_;
533  param.clip_rect = clip_rect_;
534  param.windowless_buffer0 = TransportDIB::DefaultHandleValue();
535  param.windowless_buffer1 = TransportDIB::DefaultHandleValue();
536  param.windowless_buffer_index = back_buffer_index();
537
538#if defined(OS_POSIX)
539  // If we're using POSIX mmap'd TransportDIBs, sending the handle across
540  // IPC establishes a new mapping rather than just sending a window ID,
541  // so only do so if we've actually changed the shared memory bitmaps.
542  if (bitmaps_changed)
543#endif
544  {
545    if (transport_stores_[0].dib)
546      CopyTransportDIBHandleForMessage(transport_stores_[0].dib->handle(),
547                                       &param.windowless_buffer0,
548                                       channel_host_->peer_pid());
549
550    if (transport_stores_[1].dib)
551      CopyTransportDIBHandleForMessage(transport_stores_[1].dib->handle(),
552                                       &param.windowless_buffer1,
553                                       channel_host_->peer_pid());
554  }
555
556  IPC::Message* msg;
557#if defined(OS_WIN)
558  if (UseSynchronousGeometryUpdates()) {
559    msg = new PluginMsg_UpdateGeometrySync(instance_id_, param);
560  } else  // NOLINT
561#endif
562  {
563    msg = new PluginMsg_UpdateGeometry(instance_id_, param);
564    msg->set_unblock(true);
565  }
566
567  Send(msg);
568}
569
570void WebPluginDelegateProxy::UpdateGeometry(const gfx::Rect& window_rect,
571                                            const gfx::Rect& clip_rect) {
572  // window_rect becomes either a window in native windowing system
573  // coords, or a backing buffer.  In either case things will go bad
574  // if the rectangle is very large.
575  if (window_rect.width() < 0  || window_rect.width() > kMaxPluginSideLength ||
576      window_rect.height() < 0 || window_rect.height() > kMaxPluginSideLength ||
577      // We know this won't overflow due to above checks.
578      static_cast<uint32>(window_rect.width()) *
579          static_cast<uint32>(window_rect.height()) > kMaxPluginSize) {
580    return;
581  }
582
583  plugin_rect_ = window_rect;
584  clip_rect_ = clip_rect;
585
586  bool bitmaps_changed = false;
587
588  if (uses_shared_bitmaps_) {
589    if (!front_buffer_canvas() ||
590        (window_rect.width() != front_buffer_canvas()->getDevice()->width() ||
591         window_rect.height() != front_buffer_canvas()->getDevice()->height()))
592    {
593      bitmaps_changed = true;
594
595      // Create a shared memory section that the plugin paints into
596      // asynchronously.
597      ResetWindowlessBitmaps();
598      if (!window_rect.IsEmpty()) {
599        if (!CreateSharedBitmap(&transport_stores_[0].dib,
600                                &transport_stores_[0].canvas) ||
601            !CreateSharedBitmap(&transport_stores_[1].dib,
602                                &transport_stores_[1].canvas)) {
603          DCHECK(false);
604          ResetWindowlessBitmaps();
605          return;
606        }
607      }
608    }
609  }
610
611  SendUpdateGeometry(bitmaps_changed);
612}
613
614void WebPluginDelegateProxy::ResetWindowlessBitmaps() {
615  transport_stores_[0].dib.reset();
616  transport_stores_[1].dib.reset();
617
618  transport_stores_[0].canvas.reset();
619  transport_stores_[1].canvas.reset();
620  transport_store_painted_ = gfx::Rect();
621  front_buffer_diff_ = gfx::Rect();
622}
623
624static size_t BitmapSizeForPluginRect(const gfx::Rect& plugin_rect) {
625  const size_t stride =
626      skia::PlatformCanvasStrideForWidth(plugin_rect.width());
627  return stride * plugin_rect.height();
628}
629
630#if !defined(OS_WIN)
631bool WebPluginDelegateProxy::CreateLocalBitmap(
632    std::vector<uint8>* memory,
633    scoped_ptr<skia::PlatformCanvas>* canvas) {
634  const size_t size = BitmapSizeForPluginRect(plugin_rect_);
635  memory->resize(size);
636  if (memory->size() != size)
637    return false;
638  canvas->reset(skia::CreatePlatformCanvas(
639      plugin_rect_.width(), plugin_rect_.height(), true, &((*memory)[0]),
640      skia::CRASH_ON_FAILURE));
641  return true;
642}
643#endif
644
645bool WebPluginDelegateProxy::CreateSharedBitmap(
646    scoped_ptr<TransportDIB>* memory,
647    scoped_ptr<skia::PlatformCanvas>* canvas) {
648  const size_t size = BitmapSizeForPluginRect(plugin_rect_);
649#if defined(OS_POSIX) && !defined(OS_MACOSX)
650  memory->reset(TransportDIB::Create(size, 0));
651  if (!memory->get())
652    return false;
653#endif
654#if defined(OS_POSIX) && !defined(OS_ANDROID)
655  TransportDIB::Handle handle;
656  IPC::Message* msg = new ViewHostMsg_AllocTransportDIB(size, false, &handle);
657  if (!RenderThreadImpl::current()->Send(msg))
658    return false;
659  if (handle.fd < 0)
660    return false;
661  memory->reset(TransportDIB::Map(handle));
662#else
663  static uint32 sequence_number = 0;
664  memory->reset(TransportDIB::Create(size, sequence_number++));
665#endif
666  canvas->reset((*memory)->GetPlatformCanvas(plugin_rect_.width(),
667                                             plugin_rect_.height()));
668  return !!canvas->get();
669}
670
671void WebPluginDelegateProxy::Paint(SkCanvas* canvas,
672                                   const gfx::Rect& damaged_rect) {
673  // Limit the damaged rectangle to whatever is contained inside the plugin
674  // rectangle, as that's the rectangle that we'll actually draw.
675  gfx::Rect rect = gfx::IntersectRects(damaged_rect, plugin_rect_);
676
677  // If the plugin is no longer connected (channel crashed) draw a crashed
678  // plugin bitmap
679  if (!channel_host_.get() || !channel_host_->channel_valid()) {
680    // Lazily load the sad plugin image.
681    if (!sad_plugin_)
682      sad_plugin_ = GetContentClient()->renderer()->GetSadPluginBitmap();
683    if (sad_plugin_)
684      PaintSadPlugin(canvas, plugin_rect_, *sad_plugin_);
685    return;
686  }
687
688  if (!uses_shared_bitmaps_)
689    return;
690
691  // We got a paint before the plugin's coordinates, so there's no buffer to
692  // copy from.
693  if (!front_buffer_canvas())
694    return;
695
696  gfx::Rect offset_rect = rect;
697  offset_rect.Offset(-plugin_rect_.x(), -plugin_rect_.y());
698
699  // transport_store_painted_ is really a bounding box, so in principle this
700  // check could falsely indicate that we don't need to paint offset_rect, but
701  // in practice it works fine.
702  if (!transport_store_painted_.Contains(offset_rect)) {
703    Send(new PluginMsg_Paint(instance_id_, offset_rect));
704    // Since the plugin is not blocked on the renderer in this context, there is
705    // a chance that it will begin repainting the back-buffer before we complete
706    // capturing the data. Buffer flipping would increase that risk because
707    // geometry update is asynchronous, so we don't want to use buffer flipping
708    // here.
709    UpdateFrontBuffer(offset_rect, false);
710  }
711
712  const SkBitmap& bitmap =
713      front_buffer_canvas()->getDevice()->accessBitmap(false);
714  SkPaint paint;
715  paint.setXfermodeMode(
716      transparent_ ? SkXfermode::kSrcATop_Mode : SkXfermode::kSrc_Mode);
717  SkIRect src_rect = gfx::RectToSkIRect(offset_rect);
718  canvas->drawBitmapRect(bitmap,
719                         &src_rect,
720                         gfx::RectToSkRect(rect),
721                         &paint);
722
723  if (invalidate_pending_) {
724    // Only send the PaintAck message if this paint is in response to an
725    // invalidate from the plugin, since this message acts as an access token
726    // to ensure only one process is using the transport dib at a time.
727    invalidate_pending_ = false;
728    Send(new PluginMsg_DidPaint(instance_id_));
729  }
730}
731
732NPObject* WebPluginDelegateProxy::GetPluginScriptableObject() {
733  if (npobject_)
734    return WebBindings::retainObject(npobject_);
735
736  if (!channel_host_.get())
737    return NULL;
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 = false;
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