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/plugin/webplugin_delegate_stub.h"
6
7#include "build/build_config.h"
8
9#include "base/bind.h"
10#include "base/command_line.h"
11#include "base/strings/string_number_conversions.h"
12#include "content/child/npapi/plugin_instance.h"
13#include "content/child/npapi/webplugin_delegate_impl.h"
14#include "content/child/npapi/webplugin_resource_client.h"
15#include "content/child/plugin_messages.h"
16#include "content/common/cursors/webcursor.h"
17#include "content/plugin/plugin_channel.h"
18#include "content/plugin/plugin_thread.h"
19#include "content/plugin/webplugin_proxy.h"
20#include "content/public/common/content_client.h"
21#include "content/public/common/content_constants.h"
22#include "content/public/common/content_switches.h"
23#include "skia/ext/platform_device.h"
24#include "third_party/WebKit/public/platform/WebCursorInfo.h"
25#include "third_party/WebKit/public/web/WebBindings.h"
26#include "third_party/npapi/bindings/npapi.h"
27#include "third_party/npapi/bindings/npruntime.h"
28
29using blink::WebBindings;
30using blink::WebCursorInfo;
31
32namespace content {
33
34static void DestroyWebPluginAndDelegate(
35    base::WeakPtr<NPObjectStub> scriptable_object,
36    WebPluginDelegateImpl* delegate,
37    WebPlugin* webplugin) {
38  // The plugin may not expect us to try to release the scriptable object
39  // after calling NPP_Destroy on the instance, so delete the stub now.
40  if (scriptable_object.get())
41    scriptable_object->DeleteSoon();
42
43  if (delegate) {
44    // Save the object owner Id so we can unregister it as a valid owner
45    // after the instance has been destroyed.
46    NPP owner = delegate->GetPluginNPP();
47
48    // WebPlugin must outlive WebPluginDelegate.
49    delegate->PluginDestroyed();
50
51    // PluginDestroyed can call into script, so only unregister as an object
52    // owner after that has completed.
53    WebBindings::unregisterObjectOwner(owner);
54  }
55
56  delete webplugin;
57}
58
59WebPluginDelegateStub::WebPluginDelegateStub(
60    const std::string& mime_type, int instance_id, PluginChannel* channel) :
61    mime_type_(mime_type),
62    instance_id_(instance_id),
63    channel_(channel),
64    delegate_(NULL),
65    webplugin_(NULL),
66    in_destructor_(false) {
67  DCHECK(channel);
68}
69
70WebPluginDelegateStub::~WebPluginDelegateStub() {
71  in_destructor_ = true;
72  GetContentClient()->SetActiveURL(page_url_);
73
74  if (channel_->in_send()) {
75    // The delegate or an npobject is in the callstack, so don't delete it
76    // right away.
77    base::MessageLoop::current()->PostNonNestableTask(
78        FROM_HERE,
79        base::Bind(&DestroyWebPluginAndDelegate,
80                   plugin_scriptable_object_,
81                   delegate_,
82                   webplugin_));
83  } else {
84    // Safe to delete right away.
85    DestroyWebPluginAndDelegate(
86        plugin_scriptable_object_, delegate_, webplugin_);
87  }
88
89  // Remove the NPObject owner mapping for this instance.
90  if (delegate_)
91    channel_->RemoveMappingForNPObjectOwner(instance_id_);
92}
93
94bool WebPluginDelegateStub::OnMessageReceived(const IPC::Message& msg) {
95  GetContentClient()->SetActiveURL(page_url_);
96
97  // A plugin can execute a script to delete itself in any of its NPP methods.
98  // Hold an extra reference to ourself so that if this does occur and we're
99  // handling a sync message, we don't crash when attempting to send a reply.
100  // The exception to this is when we're already in the destructor.
101  if (!in_destructor_)
102    AddRef();
103
104  bool handled = true;
105  IPC_BEGIN_MESSAGE_MAP(WebPluginDelegateStub, msg)
106    IPC_MESSAGE_HANDLER(PluginMsg_Init, OnInit)
107    IPC_MESSAGE_HANDLER(PluginMsg_WillSendRequest, OnWillSendRequest)
108    IPC_MESSAGE_HANDLER(PluginMsg_DidReceiveResponse, OnDidReceiveResponse)
109    IPC_MESSAGE_HANDLER(PluginMsg_DidReceiveData, OnDidReceiveData)
110    IPC_MESSAGE_HANDLER(PluginMsg_DidFinishLoading, OnDidFinishLoading)
111    IPC_MESSAGE_HANDLER(PluginMsg_DidFail, OnDidFail)
112    IPC_MESSAGE_HANDLER(PluginMsg_DidFinishLoadWithReason,
113                        OnDidFinishLoadWithReason)
114    IPC_MESSAGE_HANDLER(PluginMsg_SetFocus, OnSetFocus)
115    IPC_MESSAGE_HANDLER(PluginMsg_HandleInputEvent, OnHandleInputEvent)
116    IPC_MESSAGE_HANDLER(PluginMsg_Paint, OnPaint)
117    IPC_MESSAGE_HANDLER(PluginMsg_DidPaint, OnDidPaint)
118    IPC_MESSAGE_HANDLER(PluginMsg_GetPluginScriptableObject,
119                        OnGetPluginScriptableObject)
120    IPC_MESSAGE_HANDLER(PluginMsg_GetFormValue, OnGetFormValue)
121    IPC_MESSAGE_HANDLER(PluginMsg_UpdateGeometry, OnUpdateGeometry)
122    IPC_MESSAGE_HANDLER(PluginMsg_UpdateGeometrySync, OnUpdateGeometry)
123    IPC_MESSAGE_HANDLER(PluginMsg_SendJavaScriptStream,
124                        OnSendJavaScriptStream)
125    IPC_MESSAGE_HANDLER(PluginMsg_SetContentAreaFocus, OnSetContentAreaFocus)
126#if defined(OS_WIN) && !defined(USE_AURA)
127    IPC_MESSAGE_HANDLER(PluginMsg_ImeCompositionUpdated,
128                        OnImeCompositionUpdated)
129    IPC_MESSAGE_HANDLER(PluginMsg_ImeCompositionCompleted,
130                        OnImeCompositionCompleted)
131#endif
132#if defined(OS_MACOSX)
133    IPC_MESSAGE_HANDLER(PluginMsg_SetWindowFocus, OnSetWindowFocus)
134    IPC_MESSAGE_HANDLER(PluginMsg_ContainerHidden, OnContainerHidden)
135    IPC_MESSAGE_HANDLER(PluginMsg_ContainerShown, OnContainerShown)
136    IPC_MESSAGE_HANDLER(PluginMsg_WindowFrameChanged, OnWindowFrameChanged)
137    IPC_MESSAGE_HANDLER(PluginMsg_ImeCompositionCompleted,
138                        OnImeCompositionCompleted)
139#endif
140    IPC_MESSAGE_HANDLER(PluginMsg_DidReceiveManualResponse,
141                        OnDidReceiveManualResponse)
142    IPC_MESSAGE_HANDLER(PluginMsg_DidReceiveManualData, OnDidReceiveManualData)
143    IPC_MESSAGE_HANDLER(PluginMsg_DidFinishManualLoading,
144                        OnDidFinishManualLoading)
145    IPC_MESSAGE_HANDLER(PluginMsg_DidManualLoadFail, OnDidManualLoadFail)
146    IPC_MESSAGE_HANDLER(PluginMsg_HandleURLRequestReply,
147                        OnHandleURLRequestReply)
148    IPC_MESSAGE_HANDLER(PluginMsg_HTTPRangeRequestReply,
149                        OnHTTPRangeRequestReply)
150    IPC_MESSAGE_HANDLER(PluginMsg_FetchURL, OnFetchURL)
151    IPC_MESSAGE_UNHANDLED(handled = false)
152  IPC_END_MESSAGE_MAP()
153
154  if (!in_destructor_)
155    Release();
156
157  DCHECK(handled);
158  return handled;
159}
160
161bool WebPluginDelegateStub::Send(IPC::Message* msg) {
162  return channel_->Send(msg);
163}
164
165void WebPluginDelegateStub::OnInit(const PluginMsg_Init_Params& params,
166                                   bool* transparent,
167                                   bool* result) {
168  page_url_ = params.page_url;
169  GetContentClient()->SetActiveURL(page_url_);
170
171  *transparent = false;
172  *result = false;
173  if (params.arg_names.size() != params.arg_values.size()) {
174    NOTREACHED();
175    return;
176  }
177
178  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
179  base::FilePath path =
180      command_line.GetSwitchValuePath(switches::kPluginPath);
181
182  webplugin_ = new WebPluginProxy(channel_.get(),
183                                  instance_id_,
184                                  page_url_,
185                                  params.host_render_view_routing_id);
186  delegate_ = WebPluginDelegateImpl::Create(webplugin_, path, mime_type_);
187  if (delegate_) {
188    if (delegate_->GetQuirks() &
189        WebPluginDelegateImpl::PLUGIN_QUIRK_DIE_AFTER_UNLOAD) {
190      PluginThread::current()->SetForcefullyTerminatePluginProcess();
191    }
192
193    webplugin_->set_delegate(delegate_);
194    std::vector<std::string> arg_names = params.arg_names;
195    std::vector<std::string> arg_values = params.arg_values;
196
197    // Register the plugin as a valid object owner.
198    WebBindings::registerObjectOwner(delegate_->GetPluginNPP());
199
200    // Add an NPObject owner mapping for this instance, to support ownership
201    // tracking in the renderer.
202    channel_->AddMappingForNPObjectOwner(instance_id_,
203                                         delegate_->GetPluginNPP());
204
205    *result = delegate_->Initialize(params.url,
206                                    arg_names,
207                                    arg_values,
208                                    params.load_manually);
209    *transparent = delegate_->instance()->transparent();
210  }
211}
212
213void WebPluginDelegateStub::OnWillSendRequest(int id, const GURL& url,
214                                              int http_status_code) {
215  WebPluginResourceClient* client = webplugin_->GetResourceClient(id);
216  if (!client)
217    return;
218
219  client->WillSendRequest(url, http_status_code);
220}
221
222void WebPluginDelegateStub::OnDidReceiveResponse(
223    const PluginMsg_DidReceiveResponseParams& params) {
224  WebPluginResourceClient* client = webplugin_->GetResourceClient(params.id);
225  if (!client)
226    return;
227
228  client->DidReceiveResponse(params.mime_type,
229                             params.headers,
230                             params.expected_length,
231                             params.last_modified,
232                             params.request_is_seekable);
233}
234
235void WebPluginDelegateStub::OnDidReceiveData(int id,
236                                             const std::vector<char>& buffer,
237                                             int data_offset) {
238  WebPluginResourceClient* client = webplugin_->GetResourceClient(id);
239  if (!client)
240    return;
241
242  client->DidReceiveData(&buffer.front(), static_cast<int>(buffer.size()),
243                         data_offset);
244}
245
246void WebPluginDelegateStub::OnDidFinishLoading(int id) {
247  WebPluginResourceClient* client = webplugin_->GetResourceClient(id);
248  if (!client)
249    return;
250
251  client->DidFinishLoading(id);
252}
253
254void WebPluginDelegateStub::OnDidFail(int id) {
255  WebPluginResourceClient* client = webplugin_->GetResourceClient(id);
256  if (!client)
257    return;
258
259  client->DidFail(id);
260}
261
262void WebPluginDelegateStub::OnDidFinishLoadWithReason(
263    const GURL& url, int reason, int notify_id) {
264  delegate_->DidFinishLoadWithReason(url, reason, notify_id);
265}
266
267void WebPluginDelegateStub::OnSetFocus(bool focused) {
268  delegate_->SetFocus(focused);
269#if defined(OS_WIN) && !defined(USE_AURA)
270  if (focused)
271    webplugin_->UpdateIMEStatus();
272#endif
273}
274
275void WebPluginDelegateStub::OnHandleInputEvent(
276    const blink::WebInputEvent *event,
277    bool* handled,
278    WebCursor* cursor) {
279  WebCursor::CursorInfo cursor_info;
280  *handled = delegate_->HandleInputEvent(*event, &cursor_info);
281  cursor->InitFromCursorInfo(cursor_info);
282}
283
284void WebPluginDelegateStub::OnPaint(const gfx::Rect& damaged_rect) {
285  webplugin_->Paint(damaged_rect);
286}
287
288void WebPluginDelegateStub::OnDidPaint() {
289  webplugin_->DidPaint();
290}
291
292void WebPluginDelegateStub::OnUpdateGeometry(
293    const PluginMsg_UpdateGeometry_Param& param) {
294  webplugin_->UpdateGeometry(
295      param.window_rect, param.clip_rect,
296      param.windowless_buffer0, param.windowless_buffer1,
297      param.windowless_buffer_index);
298}
299
300void WebPluginDelegateStub::OnGetPluginScriptableObject(int* route_id) {
301  NPObject* object = delegate_->GetPluginScriptableObject();
302  if (!object) {
303    *route_id = MSG_ROUTING_NONE;
304    return;
305  }
306
307  *route_id = channel_->GenerateRouteID();
308  // We will delete the stub immediately before calling PluginDestroyed on the
309  // delegate. It will delete itself sooner if the proxy tells it that it has
310  // been released, or if the channel to the proxy is closed.
311  NPObjectStub* scriptable_stub = new NPObjectStub(
312      object, channel_.get(), *route_id,
313      webplugin_->host_render_view_routing_id(), page_url_);
314  plugin_scriptable_object_ = scriptable_stub->AsWeakPtr();
315
316  // Release ref added by GetPluginScriptableObject (our stub holds its own).
317  WebBindings::releaseObject(object);
318}
319
320void WebPluginDelegateStub::OnGetFormValue(base::string16* value,
321                                           bool* success) {
322  *success = false;
323  if (!delegate_)
324    return;
325  *success = delegate_->GetFormValue(value);
326}
327
328void WebPluginDelegateStub::OnSendJavaScriptStream(const GURL& url,
329                                                   const std::string& result,
330                                                   bool success,
331                                                   int notify_id) {
332  delegate_->SendJavaScriptStream(url, result, success, notify_id);
333}
334
335void WebPluginDelegateStub::OnSetContentAreaFocus(bool has_focus) {
336  if (delegate_)
337    delegate_->SetContentAreaHasFocus(has_focus);
338}
339
340#if defined(OS_WIN) && !defined(USE_AURA)
341void WebPluginDelegateStub::OnImeCompositionUpdated(
342    const base::string16& text,
343    const std::vector<int>& clauses,
344    const std::vector<int>& target,
345    int cursor_position) {
346  if (delegate_)
347    delegate_->ImeCompositionUpdated(text, clauses, target, cursor_position);
348  webplugin_->UpdateIMEStatus();
349}
350
351void WebPluginDelegateStub::OnImeCompositionCompleted(
352    const base::string16& text) {
353  if (delegate_)
354    delegate_->ImeCompositionCompleted(text);
355}
356#endif
357
358#if defined(OS_MACOSX)
359void WebPluginDelegateStub::OnSetWindowFocus(bool has_focus) {
360  if (delegate_)
361    delegate_->SetWindowHasFocus(has_focus);
362}
363
364void WebPluginDelegateStub::OnContainerHidden() {
365  if (delegate_)
366    delegate_->SetContainerVisibility(false);
367}
368
369void WebPluginDelegateStub::OnContainerShown(gfx::Rect window_frame,
370                                             gfx::Rect view_frame,
371                                             bool has_focus) {
372  if (delegate_) {
373    delegate_->WindowFrameChanged(window_frame, view_frame);
374    delegate_->SetContainerVisibility(true);
375    delegate_->SetWindowHasFocus(has_focus);
376  }
377}
378
379void WebPluginDelegateStub::OnWindowFrameChanged(const gfx::Rect& window_frame,
380                                                 const gfx::Rect& view_frame) {
381  if (delegate_)
382    delegate_->WindowFrameChanged(window_frame, view_frame);
383}
384
385void WebPluginDelegateStub::OnImeCompositionCompleted(
386    const base::string16& text) {
387  if (delegate_)
388    delegate_->ImeCompositionCompleted(text);
389}
390#endif  // OS_MACOSX
391
392void WebPluginDelegateStub::OnDidReceiveManualResponse(
393    const GURL& url,
394    const PluginMsg_DidReceiveResponseParams& params) {
395  delegate_->DidReceiveManualResponse(url, params.mime_type, params.headers,
396                                      params.expected_length,
397                                      params.last_modified);
398}
399
400void WebPluginDelegateStub::OnDidReceiveManualData(
401    const std::vector<char>& buffer) {
402  delegate_->DidReceiveManualData(&buffer.front(),
403                                  static_cast<int>(buffer.size()));
404}
405
406void WebPluginDelegateStub::OnDidFinishManualLoading() {
407  delegate_->DidFinishManualLoading();
408}
409
410void WebPluginDelegateStub::OnDidManualLoadFail() {
411  delegate_->DidManualLoadFail();
412}
413
414void WebPluginDelegateStub::OnHandleURLRequestReply(
415    unsigned long resource_id, const GURL& url, int notify_id) {
416  WebPluginResourceClient* resource_client =
417      delegate_->CreateResourceClient(resource_id, url, notify_id);
418  webplugin_->OnResourceCreated(resource_id, resource_client);
419}
420
421void WebPluginDelegateStub::OnHTTPRangeRequestReply(
422    unsigned long resource_id, int range_request_id) {
423  WebPluginResourceClient* resource_client =
424      delegate_->CreateSeekableResourceClient(resource_id, range_request_id);
425  webplugin_->OnResourceCreated(resource_id, resource_client);
426}
427
428void WebPluginDelegateStub::OnFetchURL(
429    const PluginMsg_FetchURL_Params& params) {
430  const char* data = NULL;
431  if (params.post_data.size())
432    data = &params.post_data[0];
433
434  delegate_->FetchURL(params.resource_id,
435                      params.notify_id,
436                      params.url,
437                      params.first_party_for_cookies,
438                      params.method,
439                      data,
440                      static_cast<unsigned int>(params.post_data.size()),
441                      params.referrer,
442                      params.notify_redirect,
443                      params.is_plugin_src_load,
444                      channel_->renderer_id(),
445                      params.render_frame_id,
446                      webplugin_->host_render_view_routing_id());
447}
448
449}  // namespace content
450