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/child/npapi/webplugin_delegate_impl.h"
6
7#include <string>
8#include <vector>
9
10#include "base/memory/scoped_ptr.h"
11#include "base/message_loop/message_loop.h"
12#include "base/process/process_handle.h"
13#include "base/strings/string_util.h"
14#include "base/strings/utf_string_conversions.h"
15#include "content/child/npapi/plugin_instance.h"
16#include "content/child/npapi/plugin_lib.h"
17#include "content/child/npapi/plugin_stream_url.h"
18#include "content/child/npapi/plugin_url_fetcher.h"
19#include "third_party/WebKit/public/web/WebInputEvent.h"
20
21using blink::WebCursorInfo;
22using blink::WebInputEvent;
23
24namespace content {
25
26WebPluginDelegateImpl* WebPluginDelegateImpl::Create(
27    WebPlugin* plugin,
28    const base::FilePath& filename,
29    const std::string& mime_type) {
30  scoped_refptr<PluginLib> plugin_lib(PluginLib::CreatePluginLib(filename));
31  if (plugin_lib.get() == NULL)
32    return NULL;
33
34  NPError err = plugin_lib->NP_Initialize();
35  if (err != NPERR_NO_ERROR)
36    return NULL;
37
38  scoped_refptr<PluginInstance> instance(plugin_lib->CreateInstance(mime_type));
39  return new WebPluginDelegateImpl(plugin, instance.get());
40}
41
42void WebPluginDelegateImpl::PluginDestroyed() {
43  if (handle_event_depth_) {
44    base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
45  } else {
46    delete this;
47  }
48}
49
50bool WebPluginDelegateImpl::Initialize(
51    const GURL& url,
52    const std::vector<std::string>& arg_names,
53    const std::vector<std::string>& arg_values,
54    bool load_manually) {
55  if (instance_->plugin_lib()->plugin_info().name.find(
56          base::ASCIIToUTF16("QuickTime Plug-in")) != std::wstring::npos) {
57    quirks_ |= PLUGIN_QUIRK_COPY_STREAM_DATA;
58  }
59
60  instance_->set_web_plugin(plugin_);
61  if (quirks_ & PLUGIN_QUIRK_DONT_ALLOW_MULTIPLE_INSTANCES) {
62    PluginLib* plugin_lib = instance()->plugin_lib();
63    if (plugin_lib->instance_count() > 1) {
64      return false;
65    }
66  }
67
68  int argc = 0;
69  scoped_ptr<char*[]> argn(new char*[arg_names.size()]);
70  scoped_ptr<char*[]> argv(new char*[arg_names.size()]);
71  for (size_t i = 0; i < arg_names.size(); ++i) {
72    if (quirks_ & PLUGIN_QUIRK_NO_WINDOWLESS &&
73        LowerCaseEqualsASCII(arg_names[i], "windowlessvideo")) {
74      continue;
75    }
76    argn[argc] = const_cast<char*>(arg_names[i].c_str());
77    argv[argc] = const_cast<char*>(arg_values[i].c_str());
78    argc++;
79  }
80
81  creation_succeeded_ = instance_->Start(
82      url, argn.get(), argv.get(), argc, load_manually);
83  if (!creation_succeeded_) {
84    VLOG(1) << "Couldn't start plug-in instance";
85    return false;
86  }
87
88  windowless_ = instance_->windowless();
89  if (!windowless_) {
90    if (!WindowedCreatePlugin()) {
91      VLOG(1) << "Couldn't create windowed plug-in";
92      return false;
93    }
94  }
95
96  bool should_load = PlatformInitialize();
97
98  plugin_url_ = url.spec();
99
100  return should_load;
101}
102
103void WebPluginDelegateImpl::DestroyInstance() {
104  if (instance_.get() && (instance_->npp()->ndata != NULL)) {
105    // Shutdown all streams before destroying so that
106    // no streams are left "in progress".  Need to do
107    // this before calling set_web_plugin(NULL) because the
108    // instance uses the helper to do the download.
109    instance_->CloseStreams();
110
111    window_.window = NULL;
112    if (creation_succeeded_ &&
113        !(quirks_ & PLUGIN_QUIRK_DONT_SET_NULL_WINDOW_HANDLE_ON_DESTROY)) {
114      instance_->NPP_SetWindow(&window_);
115    }
116
117    instance_->NPP_Destroy();
118
119    instance_->set_web_plugin(NULL);
120
121    PlatformDestroyInstance();
122
123    instance_ = 0;
124  }
125}
126
127void WebPluginDelegateImpl::UpdateGeometry(
128    const gfx::Rect& window_rect,
129    const gfx::Rect& clip_rect) {
130
131  if (first_set_window_call_) {
132    first_set_window_call_ = false;
133    // Plugins like media player on Windows have a bug where in they handle the
134    // first geometry update and ignore the rest resulting in painting issues.
135    // This quirk basically ignores the first set window call sequence for
136    // these plugins and has been tested for Windows plugins only.
137    if (quirks_ & PLUGIN_QUIRK_IGNORE_FIRST_SETWINDOW_CALL)
138      return;
139  }
140
141  if (windowless_) {
142    WindowlessUpdateGeometry(window_rect, clip_rect);
143  } else {
144    WindowedUpdateGeometry(window_rect, clip_rect);
145  }
146}
147
148void WebPluginDelegateImpl::SetFocus(bool focused) {
149  DCHECK(windowless_);
150  // This is called when internal WebKit focus (the focused element on the page)
151  // changes, but plugins need to know about OS-level focus, so we have an extra
152  // layer of focus tracking.
153  //
154  // On Windows, historically browsers did not set focus events to windowless
155  // plugins when the toplevel window focus changes. Sending such focus events
156  // breaks full screen mode in Flash because it will come out of full screen
157  // mode when it loses focus, and its full screen window causes the browser to
158  // lose focus.
159  has_webkit_focus_ = focused;
160#if !defined(OS_WIN)
161  if (containing_view_has_focus_)
162    SetPluginHasFocus(focused);
163#else
164  SetPluginHasFocus(focused);
165#endif
166}
167
168void WebPluginDelegateImpl::SetPluginHasFocus(bool focused) {
169  if (focused == plugin_has_focus_)
170    return;
171  if (PlatformSetPluginHasFocus(focused))
172    plugin_has_focus_ = focused;
173}
174
175void WebPluginDelegateImpl::SetContentAreaHasFocus(bool has_focus) {
176  containing_view_has_focus_ = has_focus;
177  if (!windowless_)
178    return;
179#if !defined(OS_WIN)  // See SetFocus above.
180  SetPluginHasFocus(containing_view_has_focus_ && has_webkit_focus_);
181#endif
182}
183
184NPObject* WebPluginDelegateImpl::GetPluginScriptableObject() {
185  return instance_->GetPluginScriptableObject();
186}
187
188NPP WebPluginDelegateImpl::GetPluginNPP() {
189  return instance_->npp();
190}
191
192bool WebPluginDelegateImpl::GetFormValue(base::string16* value) {
193  return instance_->GetFormValue(value);
194}
195
196void WebPluginDelegateImpl::DidFinishLoadWithReason(const GURL& url,
197                                                    NPReason reason,
198                                                    int notify_id) {
199  if (quirks_ & PLUGIN_QUIRK_ALWAYS_NOTIFY_SUCCESS &&
200      reason == NPRES_NETWORK_ERR) {
201    // Flash needs this or otherwise it unloads the launching swf object.
202    reason = NPRES_DONE;
203  }
204
205  instance()->DidFinishLoadWithReason(url, reason, notify_id);
206}
207
208int WebPluginDelegateImpl::GetProcessId() {
209  // We are in process, so the plugin pid is this current process pid.
210  return base::GetCurrentProcId();
211}
212
213void WebPluginDelegateImpl::SendJavaScriptStream(const GURL& url,
214                                                 const std::string& result,
215                                                 bool success,
216                                                 int notify_id) {
217  instance()->SendJavaScriptStream(url, result, success, notify_id);
218}
219
220void WebPluginDelegateImpl::DidReceiveManualResponse(
221    const GURL& url, const std::string& mime_type,
222    const std::string& headers, uint32 expected_length, uint32 last_modified) {
223  if (!windowless_) {
224    // Calling NPP_WriteReady before NPP_SetWindow causes movies to not load in
225    // Flash.  See http://b/issue?id=892174.
226    DCHECK(windowed_did_set_window_);
227  }
228
229  instance()->DidReceiveManualResponse(url, mime_type, headers,
230                                       expected_length, last_modified);
231}
232
233void WebPluginDelegateImpl::DidReceiveManualData(const char* buffer,
234                                                 int length) {
235  instance()->DidReceiveManualData(buffer, length);
236}
237
238void WebPluginDelegateImpl::DidFinishManualLoading() {
239  instance()->DidFinishManualLoading();
240}
241
242void WebPluginDelegateImpl::DidManualLoadFail() {
243  instance()->DidManualLoadFail();
244}
245
246base::FilePath WebPluginDelegateImpl::GetPluginPath() {
247  return instance()->plugin_lib()->plugin_info().path;
248}
249
250void WebPluginDelegateImpl::WindowedUpdateGeometry(
251    const gfx::Rect& window_rect,
252    const gfx::Rect& clip_rect) {
253  if (WindowedReposition(window_rect, clip_rect) ||
254      !windowed_did_set_window_) {
255    // Let the plugin know that it has been moved
256    WindowedSetWindow();
257  }
258}
259
260bool WebPluginDelegateImpl::HandleInputEvent(
261    const WebInputEvent& event,
262    WebCursor::CursorInfo* cursor_info) {
263  DCHECK(windowless_) << "events should only be received in windowless mode";
264
265  bool pop_user_gesture = false;
266  if (IsUserGesture(event)) {
267    pop_user_gesture = true;
268    instance()->PushPopupsEnabledState(true);
269  }
270
271  bool handled = PlatformHandleInputEvent(event, cursor_info);
272
273  if (pop_user_gesture) {
274    instance()->PopPopupsEnabledState();
275  }
276
277  return handled;
278}
279
280bool WebPluginDelegateImpl::IsUserGesture(const WebInputEvent& event) {
281  switch (event.type) {
282    case WebInputEvent::MouseDown:
283    case WebInputEvent::MouseUp:
284    case WebInputEvent::KeyDown:
285    case WebInputEvent::KeyUp:
286      return true;
287    default:
288      return false;
289  }
290}
291
292WebPluginResourceClient* WebPluginDelegateImpl::CreateResourceClient(
293    unsigned long resource_id, const GURL& url, int notify_id) {
294  return instance()->CreateStream(
295      resource_id, url, std::string(), notify_id);
296}
297
298WebPluginResourceClient* WebPluginDelegateImpl::CreateSeekableResourceClient(
299    unsigned long resource_id, int range_request_id) {
300  WebPluginResourceClient* resource_client = instance()->GetRangeRequest(
301      range_request_id);
302  if (resource_client)
303    resource_client->AddRangeRequestResourceId(resource_id);
304  return resource_client;
305}
306
307void WebPluginDelegateImpl::FetchURL(unsigned long resource_id,
308                                     int notify_id,
309                                     const GURL& url,
310                                     const GURL& first_party_for_cookies,
311                                     const std::string& method,
312                                     const char* buf,
313                                     unsigned int len,
314                                     const GURL& referrer,
315                                     bool notify_redirects,
316                                     bool is_plugin_src_load,
317                                     int origin_pid,
318                                     int render_frame_id,
319                                     int render_view_id) {
320  // TODO(jam): once we switch over to resource loading always happening in this
321  // code path, remove WebPluginResourceClient abstraction.
322  PluginStreamUrl* plugin_stream = instance()->CreateStream(
323      resource_id, url, std::string(), notify_id);
324
325  bool copy_stream_data = !!(quirks_ & PLUGIN_QUIRK_COPY_STREAM_DATA);
326  plugin_stream->SetPluginURLFetcher(new PluginURLFetcher(
327      plugin_stream, url, first_party_for_cookies, method, buf, len,
328      referrer, std::string(), notify_redirects, is_plugin_src_load, origin_pid,
329      render_frame_id, render_view_id, resource_id, copy_stream_data));
330}
331
332}  // namespace content
333