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