Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / content / child / npapi / plugin_url_fetcher.cc
1 // Copyright (c) 2013 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/plugin_url_fetcher.h"
6
7 #include "base/memory/scoped_ptr.h"
8 #include "content/child/child_thread.h"
9 #include "content/child/multipart_response_delegate.h"
10 #include "content/child/npapi/plugin_host.h"
11 #include "content/child/npapi/plugin_instance.h"
12 #include "content/child/npapi/plugin_stream_url.h"
13 #include "content/child/npapi/webplugin.h"
14 #include "content/child/npapi/webplugin_resource_client.h"
15 #include "content/child/plugin_messages.h"
16 #include "content/child/request_extra_data.h"
17 #include "content/child/request_info.h"
18 #include "content/child/resource_dispatcher.h"
19 #include "content/child/resource_loader_bridge.h"
20 #include "content/child/web_url_loader_impl.h"
21 #include "content/common/resource_request_body.h"
22 #include "content/common/service_worker/service_worker_types.h"
23 #include "content/public/common/resource_response_info.h"
24 #include "net/base/load_flags.h"
25 #include "net/base/net_errors.h"
26 #include "net/http/http_response_headers.h"
27 #include "net/url_request/redirect_info.h"
28 #include "third_party/WebKit/public/platform/WebURLLoaderClient.h"
29 #include "third_party/WebKit/public/platform/WebURLResponse.h"
30
31 namespace content {
32 namespace {
33
34 // This class handles individual multipart responses. It is instantiated when
35 // we receive HTTP status code 206 in the HTTP response. This indicates
36 // that the response could have multiple parts each separated by a boundary
37 // specified in the response header.
38 // TODO(jam): this is similar to MultiPartResponseClient in webplugin_impl.cc,
39 // we should remove that other class once we switch to loading from the plugin
40 // process by default.
41 class MultiPartResponseClient : public blink::WebURLLoaderClient {
42  public:
43   explicit MultiPartResponseClient(PluginStreamUrl* plugin_stream)
44       : byte_range_lower_bound_(0), plugin_stream_(plugin_stream) {}
45
46   // blink::WebURLLoaderClient implementation:
47   virtual void didReceiveResponse(
48       blink::WebURLLoader* loader,
49       const blink::WebURLResponse& response) override {
50     int64 byte_range_upper_bound, instance_size;
51     if (!MultipartResponseDelegate::ReadContentRanges(response,
52                                                       &byte_range_lower_bound_,
53                                                       &byte_range_upper_bound,
54                                                       &instance_size)) {
55       NOTREACHED();
56     }
57   }
58   virtual void didReceiveData(blink::WebURLLoader* loader,
59                               const char* data,
60                               int data_length,
61                               int encoded_data_length) override {
62     // TODO(ananta)
63     // We should defer further loads on multipart resources on the same lines
64     // as regular resources requested by plugins to prevent reentrancy.
65     int64 data_offset = byte_range_lower_bound_;
66     byte_range_lower_bound_ += data_length;
67     plugin_stream_->DidReceiveData(data, data_length, data_offset);
68     // DANGER: this instance may be deleted at this point.
69   }
70
71  private:
72   // The lower bound of the byte range.
73   int64 byte_range_lower_bound_;
74   // The handler for the data.
75   PluginStreamUrl* plugin_stream_;
76 };
77
78 }  // namespace
79
80 PluginURLFetcher::PluginURLFetcher(PluginStreamUrl* plugin_stream,
81                                    const GURL& url,
82                                    const GURL& first_party_for_cookies,
83                                    const std::string& method,
84                                    const char* buf,
85                                    unsigned int len,
86                                    const GURL& referrer,
87                                    const std::string& range,
88                                    bool notify_redirects,
89                                    bool is_plugin_src_load,
90                                    int origin_pid,
91                                    int render_frame_id,
92                                    int render_view_id,
93                                    unsigned long resource_id,
94                                    bool copy_stream_data)
95     : plugin_stream_(plugin_stream),
96       url_(url),
97       first_party_for_cookies_(first_party_for_cookies),
98       referrer_(referrer),
99       notify_redirects_(notify_redirects),
100       is_plugin_src_load_(is_plugin_src_load),
101       origin_pid_(origin_pid),
102       render_frame_id_(render_frame_id),
103       render_view_id_(render_view_id),
104       resource_id_(resource_id),
105       copy_stream_data_(copy_stream_data),
106       data_offset_(0),
107       pending_failure_notification_(false) {
108   RequestInfo request_info;
109   request_info.method = method;
110   request_info.url = url;
111   request_info.first_party_for_cookies = first_party_for_cookies;
112   request_info.referrer = referrer;
113   request_info.load_flags = net::LOAD_NORMAL;
114   request_info.requestor_pid = origin_pid;
115   request_info.request_type = RESOURCE_TYPE_OBJECT;
116   request_info.routing_id = render_view_id;
117   // ServiceWorker is disabled for NPAPI.
118   request_info.skip_service_worker = true;
119
120   RequestExtraData extra_data;
121   extra_data.set_render_frame_id(render_frame_id);
122   extra_data.set_is_main_frame(false);
123   request_info.extra_data = &extra_data;
124
125   std::vector<char> body;
126   if (method == "POST") {
127     bool content_type_found = false;
128     std::vector<std::string> names;
129     std::vector<std::string> values;
130     PluginHost::SetPostData(buf, len, &names, &values, &body);
131     for (size_t i = 0; i < names.size(); ++i) {
132       if (!request_info.headers.empty())
133         request_info.headers += "\r\n";
134       request_info.headers += names[i] + ": " + values[i];
135       if (LowerCaseEqualsASCII(names[i], "content-type"))
136         content_type_found = true;
137     }
138
139     if (!content_type_found) {
140       if (!request_info.headers.empty())
141         request_info.headers += "\r\n";
142       request_info.headers += "Content-Type: application/x-www-form-urlencoded";
143     }
144   } else {
145     if (!range.empty())
146       request_info.headers = std::string("Range: ") + range;
147   }
148
149   bridge_.reset(ChildThread::current()->resource_dispatcher()->CreateBridge(
150       request_info));
151   if (!body.empty()) {
152     scoped_refptr<ResourceRequestBody> request_body =
153         new ResourceRequestBody;
154     request_body->AppendBytes(&body[0], body.size());
155     bridge_->SetRequestBody(request_body.get());
156   }
157
158   bridge_->Start(this);
159
160   // TODO(jam): range requests
161 }
162
163 PluginURLFetcher::~PluginURLFetcher() {
164 }
165
166 void PluginURLFetcher::Cancel() {
167   bridge_->Cancel();
168
169   // Due to races and nested event loops, PluginURLFetcher may still receive
170   // events from the bridge before being destroyed. Do not forward additional
171   // events back to the plugin, via either |plugin_stream_| or
172   // |multipart_delegate_| which has its own pointer via
173   // MultiPartResponseClient.
174   if (multipart_delegate_)
175     multipart_delegate_->Cancel();
176   plugin_stream_ = NULL;
177 }
178
179 void PluginURLFetcher::URLRedirectResponse(bool allow) {
180   if (!plugin_stream_)
181     return;
182
183   if (allow) {
184     bridge_->SetDefersLoading(false);
185   } else {
186     bridge_->Cancel();
187     plugin_stream_->DidFail(resource_id_);  // That will delete |this|.
188   }
189 }
190
191 void PluginURLFetcher::OnUploadProgress(uint64 position, uint64 size) {
192 }
193
194 bool PluginURLFetcher::OnReceivedRedirect(
195     const net::RedirectInfo& redirect_info,
196     const ResourceResponseInfo& info) {
197   if (!plugin_stream_)
198     return false;
199
200   // TODO(jam): THIS LOGIC IS COPIED FROM WebPluginImpl::willSendRequest until
201   // kDirectNPAPIRequests is the default and we can remove the old path there.
202
203   // Currently this check is just to catch an https -> http redirect when
204   // loading the main plugin src URL. Longer term, we could investigate
205   // firing mixed diplay or scripting issues for subresource loads
206   // initiated by plug-ins.
207   if (is_plugin_src_load_ &&
208       !plugin_stream_->instance()->webplugin()->CheckIfRunInsecureContent(
209           redirect_info.new_url)) {
210     plugin_stream_->DidFail(resource_id_);  // That will delete |this|.
211     return false;
212   }
213
214   GURL old_url = url_;
215   url_ = redirect_info.new_url;
216   first_party_for_cookies_ = redirect_info.new_first_party_for_cookies;
217
218   // If the plugin does not participate in url redirect notifications then just
219   // block cross origin 307 POST redirects.
220   if (!notify_redirects_) {
221     if (redirect_info.status_code == 307 &&
222         redirect_info.new_method == "POST" &&
223         old_url.GetOrigin() != url_.GetOrigin()) {
224       plugin_stream_->DidFail(resource_id_);  // That will delete |this|.
225       return false;
226     }
227   } else {
228     // Pause the request while we ask the plugin what to do about the redirect.
229     bridge_->SetDefersLoading(true);
230     plugin_stream_->WillSendRequest(url_, redirect_info.status_code);
231   }
232
233   return true;
234 }
235
236 void PluginURLFetcher::OnReceivedResponse(const ResourceResponseInfo& info) {
237   if (!plugin_stream_)
238     return;
239
240   // TODO(jam): THIS LOGIC IS COPIED FROM WebPluginImpl::didReceiveResponse
241   // GetAllHeaders, and GetResponseInfo until kDirectNPAPIRequests is the
242   // default and we can remove the old path there.
243
244   bool request_is_seekable = true;
245   DCHECK(!multipart_delegate_.get());
246   if (plugin_stream_->seekable()) {
247     int response_code = info.headers->response_code();
248     if (response_code == 206) {
249       blink::WebURLResponse response;
250       response.initialize();
251       WebURLLoaderImpl::PopulateURLResponse(url_, info, &response);
252
253       std::string multipart_boundary;
254       if (MultipartResponseDelegate::ReadMultipartBoundary(
255               response, &multipart_boundary)) {
256         plugin_stream_->instance()->webplugin()->DidStartLoading();
257
258         MultiPartResponseClient* multi_part_response_client =
259             new MultiPartResponseClient(plugin_stream_);
260
261         multipart_delegate_.reset(new MultipartResponseDelegate(
262             multi_part_response_client, NULL, response, multipart_boundary));
263
264         // Multiple ranges requested, data will be delivered by
265         // MultipartResponseDelegate.
266         data_offset_ = 0;
267         return;
268       }
269
270       int64 upper_bound = 0, instance_size = 0;
271       // Single range requested - go through original processing for
272       // non-multipart requests, but update data offset.
273       MultipartResponseDelegate::ReadContentRanges(
274           response, &data_offset_, &upper_bound, &instance_size);
275     } else if (response_code == 200) {
276       // TODO: should we handle this case? We used to but it's not clear that we
277       // still need to. This was bug 5403, fixed in r7139.
278     }
279   }
280
281   // If the length comes in as -1, then it indicates that it was not
282   // read off the HTTP headers. We replicate Safari webkit behavior here,
283   // which is to set it to 0.
284   int expected_length = std::max(static_cast<int>(info.content_length), 0);
285
286   base::Time temp;
287   uint32 last_modified = 0;
288   std::string headers;
289   if (info.headers.get()) {  // NULL for data: urls.
290     if (info.headers->GetLastModifiedValue(&temp))
291       last_modified = static_cast<uint32>(temp.ToDoubleT());
292
293      // TODO(darin): Shouldn't we also report HTTP version numbers?
294     int response_code = info.headers->response_code();
295     headers = base::StringPrintf("HTTP %d ", response_code);
296     headers += info.headers->GetStatusText();
297     headers += "\n";
298
299     void* iter = NULL;
300     std::string name, value;
301     while (info.headers->EnumerateHeaderLines(&iter, &name, &value)) {
302       // TODO(darin): Should we really exclude headers with an empty value?
303       if (!name.empty() && !value.empty())
304         headers += name + ": " + value + "\n";
305     }
306
307     // Bug http://b/issue?id=925559. The flash plugin would not handle the HTTP
308     // error codes in the stream header and as a result, was unaware of the fate
309     // of the HTTP requests issued via NPN_GetURLNotify. Webkit and FF destroy
310     // the stream and invoke the NPP_DestroyStream function on the plugin if the
311     // HTTPrequest fails.
312     if ((url_.SchemeIs(url::kHttpScheme) || url_.SchemeIs(url::kHttpsScheme)) &&
313         (response_code < 100 || response_code >= 400)) {
314       pending_failure_notification_ = true;
315     }
316   }
317
318   plugin_stream_->DidReceiveResponse(info.mime_type,
319                                      headers,
320                                      expected_length,
321                                      last_modified,
322                                      request_is_seekable);
323 }
324
325 void PluginURLFetcher::OnDownloadedData(int len,
326                                         int encoded_data_length) {
327 }
328
329 void PluginURLFetcher::OnReceivedData(const char* data,
330                                       int data_length,
331                                       int encoded_data_length) {
332   if (!plugin_stream_)
333     return;
334
335   if (multipart_delegate_) {
336     multipart_delegate_->OnReceivedData(data, data_length, encoded_data_length);
337   } else {
338     int64 offset = data_offset_;
339     data_offset_ += data_length;
340
341     if (copy_stream_data_) {
342       // QuickTime writes to this memory, and since we got it from
343       // ResourceDispatcher it's not mapped for write access in this process.
344       // http://crbug.com/308466.
345       scoped_ptr<char[]> data_copy(new char[data_length]);
346       memcpy(data_copy.get(), data, data_length);
347       plugin_stream_->DidReceiveData(data_copy.get(), data_length, offset);
348     } else {
349       plugin_stream_->DidReceiveData(data, data_length, offset);
350     }
351     // DANGER: this instance may be deleted at this point.
352   }
353 }
354
355 void PluginURLFetcher::OnCompletedRequest(
356     int error_code,
357     bool was_ignored_by_handler,
358     bool stale_copy_in_cache,
359     const std::string& security_info,
360     const base::TimeTicks& completion_time,
361     int64 total_transfer_size) {
362   if (!plugin_stream_)
363     return;
364
365   if (multipart_delegate_) {
366     multipart_delegate_->OnCompletedRequest();
367     multipart_delegate_.reset();
368   }
369
370   if (error_code == net::OK) {
371     plugin_stream_->DidFinishLoading(resource_id_);
372   } else {
373     plugin_stream_->DidFail(resource_id_);
374   }
375 }
376
377 }  // namespace content