[GStreamer] don't send transferMode HTTP header
[WebKit-https.git] / Source / WebCore / platform / graphics / gstreamer / WebKitWebSourceGStreamer.cpp
1 /*
2  *  Copyright (C) 2009, 2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
3  *  Copyright (C) 2013 Collabora Ltd.
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19
20 #include "config.h"
21 #include "WebKitWebSourceGStreamer.h"
22
23 #if ENABLE(VIDEO) && USE(GSTREAMER)
24
25 #include "CachedRawResource.h"
26 #include "CachedRawResourceClient.h"
27 #include "CachedResourceHandle.h"
28 #include "CachedResourceLoader.h"
29 #include "CachedResourceRequest.h"
30 #include "CrossOriginAccessControl.h"
31 #include "GRefPtrGStreamer.h"
32 #include "GStreamerUtilities.h"
33 #include "HTTPHeaderNames.h"
34 #include "MediaPlayer.h"
35 #include "NotImplemented.h"
36 #include "ResourceHandle.h"
37 #include "ResourceHandleClient.h"
38 #include "ResourceRequest.h"
39 #include "ResourceResponse.h"
40 #include "SecurityOrigin.h"
41 #include "SharedBuffer.h"
42 #include <gst/app/gstappsrc.h>
43 #include <gst/gst.h>
44 #include <gst/pbutils/missing-plugins.h>
45 #include <wtf/Noncopyable.h>
46 #include <wtf/gobject/GMainLoopSource.h>
47 #include <wtf/gobject/GMutexLocker.h>
48 #include <wtf/gobject/GRefPtr.h>
49 #include <wtf/gobject/GUniquePtr.h>
50 #include <wtf/text/CString.h>
51
52 using namespace WebCore;
53
54 enum CORSAccessCheckResult {
55     CORSNoCheck,
56     CORSSuccess,
57     CORSFailure
58 };
59
60 class StreamingClient {
61     public:
62         StreamingClient(WebKitWebSrc*);
63         virtual ~StreamingClient();
64
65         virtual bool loadFailed() const = 0;
66         virtual void setDefersLoading(bool) = 0;
67
68     protected:
69         char* createReadBuffer(size_t requestedSize, size_t& actualSize);
70         void handleResponseReceived(const ResourceResponse&, CORSAccessCheckResult);
71         void handleDataReceived(const char*, int);
72         void handleNotifyFinished();
73
74         GstElement* m_src;
75 };
76
77 class CachedResourceStreamingClient : public CachedRawResourceClient, public StreamingClient {
78     WTF_MAKE_NONCOPYABLE(CachedResourceStreamingClient); WTF_MAKE_FAST_ALLOCATED;
79     public:
80         CachedResourceStreamingClient(WebKitWebSrc*, CachedResourceLoader*, const ResourceRequest&, MediaPlayerClient::CORSMode);
81         virtual ~CachedResourceStreamingClient();
82
83         // StreamingClient virtual methods.
84         virtual bool loadFailed() const;
85         virtual void setDefersLoading(bool);
86
87     private:
88         // CachedResourceClient virtual methods.
89         virtual char* getOrCreateReadBuffer(CachedResource*, size_t requestedSize, size_t& actualSize);
90         virtual void responseReceived(CachedResource*, const ResourceResponse&);
91         virtual void dataReceived(CachedResource*, const char*, int);
92         virtual void notifyFinished(CachedResource*);
93
94         CachedResourceHandle<CachedRawResource> m_resource;
95         RefPtr<SecurityOrigin> m_origin;
96 };
97
98 class ResourceHandleStreamingClient : public ResourceHandleClient, public StreamingClient {
99     WTF_MAKE_NONCOPYABLE(ResourceHandleStreamingClient); WTF_MAKE_FAST_ALLOCATED;
100     public:
101         ResourceHandleStreamingClient(WebKitWebSrc*, const ResourceRequest&);
102         virtual ~ResourceHandleStreamingClient();
103
104         // StreamingClient virtual methods.
105         virtual bool loadFailed() const;
106         virtual void setDefersLoading(bool);
107
108     private:
109         // ResourceHandleClient virtual methods.
110         virtual char* getOrCreateReadBuffer(size_t requestedSize, size_t& actualSize);
111         virtual void willSendRequest(ResourceHandle*, ResourceRequest&, const ResourceResponse&);
112         virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&);
113         virtual void didReceiveData(ResourceHandle*, const char*, unsigned, int);
114         virtual void didReceiveBuffer(ResourceHandle*, PassRefPtr<SharedBuffer>, int encodedLength);
115         virtual void didFinishLoading(ResourceHandle*, double /*finishTime*/);
116         virtual void didFail(ResourceHandle*, const ResourceError&);
117         virtual void wasBlocked(ResourceHandle*);
118         virtual void cannotShowURL(ResourceHandle*);
119
120         RefPtr<ResourceHandle> m_resource;
121 };
122
123 #define WEBKIT_WEB_SRC_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_WEB_SRC, WebKitWebSrcPrivate))
124 struct _WebKitWebSrcPrivate {
125     GstAppSrc* appsrc;
126     GstPad* srcpad;
127     gchar* uri;
128
129     WebCore::MediaPlayer* player;
130
131     StreamingClient* client;
132
133     CORSAccessCheckResult corsAccessCheck;
134
135     guint64 offset;
136     guint64 size;
137     gboolean seekable;
138     gboolean paused;
139
140     guint64 requestedOffset;
141
142     GMainLoopSource startSource;
143     GMainLoopSource stopSource;
144     GMainLoopSource needDataSource;
145     GMainLoopSource enoughDataSource;
146     GMainLoopSource seekSource;
147
148     GRefPtr<GstBuffer> buffer;
149
150     // icecast stuff
151     gchar* iradioName;
152     gchar* iradioGenre;
153     gchar* iradioUrl;
154     gchar* iradioTitle;
155 };
156
157 enum {
158     PROP_IRADIO_NAME = 1,
159     PROP_IRADIO_GENRE,
160     PROP_IRADIO_URL,
161     PROP_IRADIO_TITLE,
162     PROP_LOCATION
163 };
164
165 static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE("src",
166                                                                   GST_PAD_SRC,
167                                                                   GST_PAD_ALWAYS,
168                                                                   GST_STATIC_CAPS_ANY);
169
170 GST_DEBUG_CATEGORY_STATIC(webkit_web_src_debug);
171 #define GST_CAT_DEFAULT webkit_web_src_debug
172
173 static void webKitWebSrcUriHandlerInit(gpointer gIface, gpointer ifaceData);
174
175 static void webKitWebSrcDispose(GObject*);
176 static void webKitWebSrcFinalize(GObject*);
177 static void webKitWebSrcSetProperty(GObject*, guint propertyID, const GValue*, GParamSpec*);
178 static void webKitWebSrcGetProperty(GObject*, guint propertyID, GValue*, GParamSpec*);
179 static GstStateChangeReturn webKitWebSrcChangeState(GstElement*, GstStateChange);
180
181 static gboolean webKitWebSrcQueryWithParent(GstPad*, GstObject*, GstQuery*);
182
183 static void webKitWebSrcNeedDataCb(GstAppSrc*, guint length, gpointer userData);
184 static void webKitWebSrcEnoughDataCb(GstAppSrc*, gpointer userData);
185 static gboolean webKitWebSrcSeekDataCb(GstAppSrc*, guint64 offset, gpointer userData);
186
187 static GstAppSrcCallbacks appsrcCallbacks = {
188     webKitWebSrcNeedDataCb,
189     webKitWebSrcEnoughDataCb,
190     webKitWebSrcSeekDataCb,
191     { 0 }
192 };
193
194 #define webkit_web_src_parent_class parent_class
195 // We split this out into another macro to avoid a check-webkit-style error.
196 #define WEBKIT_WEB_SRC_CATEGORY_INIT GST_DEBUG_CATEGORY_INIT(webkit_web_src_debug, "webkitwebsrc", 0, "websrc element");
197 G_DEFINE_TYPE_WITH_CODE(WebKitWebSrc, webkit_web_src, GST_TYPE_BIN,
198                          G_IMPLEMENT_INTERFACE(GST_TYPE_URI_HANDLER, webKitWebSrcUriHandlerInit);
199                          WEBKIT_WEB_SRC_CATEGORY_INIT);
200
201 static void webkit_web_src_class_init(WebKitWebSrcClass* klass)
202 {
203     GObjectClass* oklass = G_OBJECT_CLASS(klass);
204     GstElementClass* eklass = GST_ELEMENT_CLASS(klass);
205
206     oklass->dispose = webKitWebSrcDispose;
207     oklass->finalize = webKitWebSrcFinalize;
208     oklass->set_property = webKitWebSrcSetProperty;
209     oklass->get_property = webKitWebSrcGetProperty;
210
211     gst_element_class_add_pad_template(eklass,
212                                        gst_static_pad_template_get(&srcTemplate));
213     gst_element_class_set_metadata(eklass, "WebKit Web source element", "Source", "Handles HTTP/HTTPS uris",
214                                "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
215
216     // icecast stuff
217     g_object_class_install_property(oklass,
218                                     PROP_IRADIO_NAME,
219                                     g_param_spec_string("iradio-name",
220                                                         "iradio-name",
221                                                         "Name of the stream",
222                                                         0,
223                                                         (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
224
225     g_object_class_install_property(oklass,
226                                     PROP_IRADIO_GENRE,
227                                     g_param_spec_string("iradio-genre",
228                                                         "iradio-genre",
229                                                         "Genre of the stream",
230                                                         0,
231                                                         (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
232
233     g_object_class_install_property(oklass,
234                                     PROP_IRADIO_URL,
235                                     g_param_spec_string("iradio-url",
236                                                         "iradio-url",
237                                                         "Homepage URL for radio stream",
238                                                         0,
239                                                         (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
240
241     g_object_class_install_property(oklass,
242                                     PROP_IRADIO_TITLE,
243                                     g_param_spec_string("iradio-title",
244                                                         "iradio-title",
245                                                         "Name of currently playing song",
246                                                         0,
247                                                         (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
248
249
250     /* Allows setting the uri using the 'location' property, which is used
251      * for example by gst_element_make_from_uri() */
252     g_object_class_install_property(oklass,
253                                     PROP_LOCATION,
254                                     g_param_spec_string("location",
255                                                         "location",
256                                                         "Location to read from",
257                                                         0,
258                                                         (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
259     eklass->change_state = webKitWebSrcChangeState;
260
261     g_type_class_add_private(klass, sizeof(WebKitWebSrcPrivate));
262 }
263
264 static void webkit_web_src_init(WebKitWebSrc* src)
265 {
266     WebKitWebSrcPrivate* priv = WEBKIT_WEB_SRC_GET_PRIVATE(src);
267
268     src->priv = priv;
269     new (priv) WebKitWebSrcPrivate();
270
271     priv->appsrc = GST_APP_SRC(gst_element_factory_make("appsrc", 0));
272     if (!priv->appsrc) {
273         GST_ERROR_OBJECT(src, "Failed to create appsrc");
274         return;
275     }
276
277     gst_bin_add(GST_BIN(src), GST_ELEMENT(priv->appsrc));
278
279
280     GRefPtr<GstPad> targetPad = adoptGRef(gst_element_get_static_pad(GST_ELEMENT(priv->appsrc), "src"));
281     priv->srcpad = webkitGstGhostPadFromStaticTemplate(&srcTemplate, "src", targetPad.get());
282
283     gst_element_add_pad(GST_ELEMENT(src), priv->srcpad);
284
285     GST_OBJECT_FLAG_SET(priv->srcpad, GST_PAD_FLAG_NEED_PARENT);
286     gst_pad_set_query_function(priv->srcpad, webKitWebSrcQueryWithParent);
287
288     gst_app_src_set_callbacks(priv->appsrc, &appsrcCallbacks, src, 0);
289     gst_app_src_set_emit_signals(priv->appsrc, FALSE);
290     gst_app_src_set_stream_type(priv->appsrc, GST_APP_STREAM_TYPE_SEEKABLE);
291
292     // 512k is a abitrary number but we should choose a value
293     // here to not pause/unpause the SoupMessage too often and
294     // to make sure there's always some data available for
295     // GStreamer to handle.
296     gst_app_src_set_max_bytes(priv->appsrc, 512 * 1024);
297
298     // Emit the need-data signal if the queue contains less
299     // than 20% of data. Without this the need-data signal
300     // is emitted when the queue is empty, we then dispatch
301     // the soup message unpausing to the main loop and from
302     // there unpause the soup message. This already takes
303     // quite some time and libsoup even needs some more time
304     // to actually provide data again. If we do all this
305     // already if the queue is 20% empty, it's much more
306     // likely that libsoup already provides new data before
307     // the queue is really empty.
308     // This might need tweaking for ports not using libsoup.
309     g_object_set(priv->appsrc, "min-percent", 20, NULL);
310
311     gst_app_src_set_caps(priv->appsrc, 0);
312     gst_app_src_set_size(priv->appsrc, -1);
313 }
314
315 static void webKitWebSrcDispose(GObject* object)
316 {
317     WebKitWebSrc* src = WEBKIT_WEB_SRC(object);
318     WebKitWebSrcPrivate* priv = src->priv;
319
320     priv->player = 0;
321
322     GST_CALL_PARENT(G_OBJECT_CLASS, dispose, (object));
323 }
324
325 static void webKitWebSrcFinalize(GObject* object)
326 {
327     WebKitWebSrc* src = WEBKIT_WEB_SRC(object);
328     WebKitWebSrcPrivate* priv = src->priv;
329
330     g_free(priv->uri);
331     priv->~WebKitWebSrcPrivate();
332
333     GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object));
334 }
335
336 static void webKitWebSrcSetProperty(GObject* object, guint propID, const GValue* value, GParamSpec* pspec)
337 {
338     WebKitWebSrc* src = WEBKIT_WEB_SRC(object);
339
340     switch (propID) {
341     case PROP_LOCATION:
342         gst_uri_handler_set_uri(reinterpret_cast<GstURIHandler*>(src), g_value_get_string(value), 0);
343         break;
344     default:
345         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, pspec);
346         break;
347     }
348 }
349
350 static void webKitWebSrcGetProperty(GObject* object, guint propID, GValue* value, GParamSpec* pspec)
351 {
352     WebKitWebSrc* src = WEBKIT_WEB_SRC(object);
353     WebKitWebSrcPrivate* priv = src->priv;
354
355     GMutexLocker locker(*GST_OBJECT_GET_LOCK(src));
356     switch (propID) {
357     case PROP_IRADIO_NAME:
358         g_value_set_string(value, priv->iradioName);
359         break;
360     case PROP_IRADIO_GENRE:
361         g_value_set_string(value, priv->iradioGenre);
362         break;
363     case PROP_IRADIO_URL:
364         g_value_set_string(value, priv->iradioUrl);
365         break;
366     case PROP_IRADIO_TITLE:
367         g_value_set_string(value, priv->iradioTitle);
368         break;
369     case PROP_LOCATION:
370         g_value_set_string(value, priv->uri);
371         break;
372     default:
373         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, pspec);
374         break;
375     }
376 }
377
378 static void removeTimeoutSources(WebKitWebSrc* src)
379 {
380     WebKitWebSrcPrivate* priv = src->priv;
381
382     priv->startSource.cancel();
383     priv->needDataSource.cancel();
384     priv->enoughDataSource.cancel();
385     priv->seekSource.cancel();
386 }
387
388 static void webKitWebSrcStop(WebKitWebSrc* src)
389 {
390     WebKitWebSrcPrivate* priv = src->priv;
391
392     ASSERT(isMainThread());
393
394     GMutexLocker locker(*GST_OBJECT_GET_LOCK(src));
395
396     bool seeking = priv->seekSource.isActive();
397
398     removeTimeoutSources(src);
399
400     if (priv->client) {
401         delete priv->client;
402         priv->client = 0;
403     }
404
405     if (priv->buffer) {
406         unmapGstBuffer(priv->buffer.get());
407         priv->buffer.clear();
408     }
409
410     priv->paused = FALSE;
411
412     g_free(priv->iradioName);
413     priv->iradioName = 0;
414
415     g_free(priv->iradioGenre);
416     priv->iradioGenre = 0;
417
418     g_free(priv->iradioUrl);
419     priv->iradioUrl = 0;
420
421     g_free(priv->iradioTitle);
422     priv->iradioTitle = 0;
423
424     priv->offset = 0;
425     priv->seekable = FALSE;
426
427     if (!seeking) {
428         priv->size = 0;
429         priv->requestedOffset = 0;
430         priv->player = 0;
431     }
432
433     locker.unlock();
434
435     if (priv->appsrc) {
436         gst_app_src_set_caps(priv->appsrc, 0);
437         if (!seeking)
438             gst_app_src_set_size(priv->appsrc, -1);
439     }
440
441     GST_DEBUG_OBJECT(src, "Stopped request");
442 }
443
444 static void webKitWebSrcStart(WebKitWebSrc* src)
445 {
446     WebKitWebSrcPrivate* priv = src->priv;
447
448     ASSERT(isMainThread());
449
450     GMutexLocker locker(*GST_OBJECT_GET_LOCK(src));
451
452     priv->corsAccessCheck = CORSNoCheck;
453
454     if (!priv->uri) {
455         GST_ERROR_OBJECT(src, "No URI provided");
456         locker.unlock();
457         webKitWebSrcStop(src);
458         return;
459     }
460
461     ASSERT(!priv->client);
462
463     URL url = URL(URL(), priv->uri);
464
465     ResourceRequest request(url);
466     request.setAllowCookies(true);
467
468     if (priv->player)
469         request.setHTTPReferrer(priv->player->referrer());
470
471 #if USE(SOUP)
472     // Let's disable HTTP Accept-Encoding here as we don't want the received response to be
473     // encoded in any way as we need to rely on the proper size of the returned data on
474     // didReceiveResponse.
475     // If Accept-Encoding is used, the server may send the data in encoded format and
476     // request.expectedContentLength() will have the "wrong" size (the size of the
477     // compressed data), even though the data received in didReceiveData is uncompressed.
478     request.setAcceptEncoding(false);
479 #endif
480
481     // Let Apple web servers know we want to access their nice movie trailers.
482     if (!g_ascii_strcasecmp("movies.apple.com", url.host().utf8().data())
483         || !g_ascii_strcasecmp("trailers.apple.com", url.host().utf8().data()))
484         request.setHTTPUserAgent("Quicktime/7.6.6");
485
486     if (priv->requestedOffset) {
487         GUniquePtr<gchar> val(g_strdup_printf("bytes=%" G_GUINT64_FORMAT "-", priv->requestedOffset));
488         request.setHTTPHeaderField(HTTPHeaderName::Range, val.get());
489     }
490     priv->offset = priv->requestedOffset;
491
492     // We always request Icecast/Shoutcast metadata, just in case ...
493     request.setHTTPHeaderField(HTTPHeaderName::IcyMetadata, "1");
494
495     if (priv->player) {
496         if (CachedResourceLoader* loader = priv->player->cachedResourceLoader())
497             priv->client = new CachedResourceStreamingClient(src, loader, request, priv->player->mediaPlayerClient()->mediaPlayerCORSMode());
498     }
499
500     if (!priv->client)
501         priv->client = new ResourceHandleStreamingClient(src, request);
502
503     if (!priv->client || priv->client->loadFailed()) {
504         GST_ERROR_OBJECT(src, "Failed to setup streaming client");
505         if (priv->client) {
506             delete priv->client;
507             priv->client = 0;
508         }
509         locker.unlock();
510         webKitWebSrcStop(src);
511         return;
512     }
513     GST_DEBUG_OBJECT(src, "Started request");
514 }
515
516 static GstStateChangeReturn webKitWebSrcChangeState(GstElement* element, GstStateChange transition)
517 {
518     GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
519     WebKitWebSrc* src = WEBKIT_WEB_SRC(element);
520     WebKitWebSrcPrivate* priv = src->priv;
521
522     switch (transition) {
523     case GST_STATE_CHANGE_NULL_TO_READY:
524         if (!priv->appsrc) {
525             gst_element_post_message(element,
526                                      gst_missing_element_message_new(element, "appsrc"));
527             GST_ELEMENT_ERROR(src, CORE, MISSING_PLUGIN, (0), ("no appsrc"));
528             return GST_STATE_CHANGE_FAILURE;
529         }
530         break;
531     default:
532         break;
533     }
534
535     ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
536     if (G_UNLIKELY(ret == GST_STATE_CHANGE_FAILURE)) {
537         GST_DEBUG_OBJECT(src, "State change failed");
538         return ret;
539     }
540
541     GMutexLocker locker(*GST_OBJECT_GET_LOCK(src));
542     switch (transition) {
543     case GST_STATE_CHANGE_READY_TO_PAUSED:
544         GST_DEBUG_OBJECT(src, "READY->PAUSED");
545         gst_object_ref(src);
546         priv->startSource.schedule("[WebKit] webKitWebSrcStart", std::function<void()>(std::bind(webKitWebSrcStart, src)), G_PRIORITY_DEFAULT,
547             [src] { gst_object_unref(src); });
548         break;
549     case GST_STATE_CHANGE_PAUSED_TO_READY:
550         GST_DEBUG_OBJECT(src, "PAUSED->READY");
551         // cancel pending sources
552         removeTimeoutSources(src);
553         gst_object_ref(src);
554         priv->stopSource.schedule("[WebKit] webKitWebSrcStop", std::function<void()>(std::bind(webKitWebSrcStop, src)), G_PRIORITY_DEFAULT,
555             [src] { gst_object_unref(src); });
556         break;
557     default:
558         break;
559     }
560
561     return ret;
562 }
563
564 static gboolean webKitWebSrcQueryWithParent(GstPad* pad, GstObject* parent, GstQuery* query)
565 {
566     WebKitWebSrc* src = WEBKIT_WEB_SRC(GST_ELEMENT(parent));
567     gboolean result = FALSE;
568
569     switch (GST_QUERY_TYPE(query)) {
570     case GST_QUERY_DURATION: {
571         GstFormat format;
572
573         gst_query_parse_duration(query, &format, NULL);
574
575         GST_DEBUG_OBJECT(src, "duration query in format %s", gst_format_get_name(format));
576         GMutexLocker locker(*GST_OBJECT_GET_LOCK(src));
577         if (format == GST_FORMAT_BYTES && src->priv->size > 0) {
578             gst_query_set_duration(query, format, src->priv->size);
579             result = TRUE;
580         }
581         break;
582     }
583     case GST_QUERY_URI: {
584         GMutexLocker locker(*GST_OBJECT_GET_LOCK(src));
585         gst_query_set_uri(query, src->priv->uri);
586         result = TRUE;
587         break;
588     }
589     default: {
590         GRefPtr<GstPad> target = adoptGRef(gst_ghost_pad_get_target(GST_GHOST_PAD_CAST(pad)));
591
592         // Forward the query to the proxy target pad.
593         if (target)
594             result = gst_pad_query(target.get(), query);
595         break;
596     }
597     }
598
599     return result;
600 }
601
602 static bool urlHasSupportedProtocol(const URL& url)
603 {
604     return url.isValid() && (url.protocolIsInHTTPFamily() || url.protocolIs("blob"));
605 }
606
607 // uri handler interface
608
609 static GstURIType webKitWebSrcUriGetType(GType)
610 {
611     return GST_URI_SRC;
612 }
613
614 const gchar* const* webKitWebSrcGetProtocols(GType)
615 {
616     static const char* protocols[] = {"http", "https", "blob", 0 };
617     return protocols;
618 }
619
620 static gchar* webKitWebSrcGetUri(GstURIHandler* handler)
621 {
622     WebKitWebSrc* src = WEBKIT_WEB_SRC(handler);
623     gchar* ret;
624
625     GMutexLocker locker(*GST_OBJECT_GET_LOCK(src));
626     ret = g_strdup(src->priv->uri);
627     return ret;
628 }
629
630 static gboolean webKitWebSrcSetUri(GstURIHandler* handler, const gchar* uri, GError** error)
631 {
632     WebKitWebSrc* src = WEBKIT_WEB_SRC(handler);
633     WebKitWebSrcPrivate* priv = src->priv;
634
635     if (GST_STATE(src) >= GST_STATE_PAUSED) {
636         GST_ERROR_OBJECT(src, "URI can only be set in states < PAUSED");
637         return FALSE;
638     }
639
640     GMutexLocker locker(*GST_OBJECT_GET_LOCK(src));
641
642     g_free(priv->uri);
643     priv->uri = 0;
644
645     if (!uri)
646         return TRUE;
647
648     URL url(URL(), uri);
649     if (!urlHasSupportedProtocol(url)) {
650         g_set_error(error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI, "Invalid URI '%s'", uri);
651         return FALSE;
652     }
653
654     priv->uri = g_strdup(url.string().utf8().data());
655     return TRUE;
656 }
657
658 static void webKitWebSrcUriHandlerInit(gpointer gIface, gpointer)
659 {
660     GstURIHandlerInterface* iface = (GstURIHandlerInterface *) gIface;
661
662     iface->get_type = webKitWebSrcUriGetType;
663     iface->get_protocols = webKitWebSrcGetProtocols;
664     iface->get_uri = webKitWebSrcGetUri;
665     iface->set_uri = webKitWebSrcSetUri;
666 }
667
668 // appsrc callbacks
669
670 static void webKitWebSrcNeedDataMainCb(WebKitWebSrc* src)
671 {
672     WebKitWebSrcPrivate* priv = src->priv;
673
674     ASSERT(isMainThread());
675
676     GMutexLocker locker(*GST_OBJECT_GET_LOCK(src));
677     priv->paused = FALSE;
678     locker.unlock();
679
680     if (priv->client)
681         priv->client->setDefersLoading(false);
682 }
683
684 static void webKitWebSrcNeedDataCb(GstAppSrc*, guint length, gpointer userData)
685 {
686     WebKitWebSrc* src = WEBKIT_WEB_SRC(userData);
687     WebKitWebSrcPrivate* priv = src->priv;
688
689     GST_DEBUG_OBJECT(src, "Need more data: %u", length);
690
691     GMutexLocker locker(*GST_OBJECT_GET_LOCK(src));
692     if (priv->needDataSource.isScheduled() || !priv->paused)
693         return;
694
695     gst_object_ref(src);
696     priv->needDataSource.schedule("[WebKit] webKitWebSrcNeedDataMainCb", std::function<void()>(std::bind(webKitWebSrcNeedDataMainCb, src)), G_PRIORITY_DEFAULT,
697         [src] { gst_object_unref(src); });
698 }
699
700 static void webKitWebSrcEnoughDataMainCb(WebKitWebSrc* src)
701 {
702     WebKitWebSrcPrivate* priv = src->priv;
703
704     ASSERT(isMainThread());
705
706     GMutexLocker locker(*GST_OBJECT_GET_LOCK(src));
707     priv->paused = TRUE;
708     locker.unlock();
709
710     if (priv->client)
711         priv->client->setDefersLoading(true);
712 }
713
714 static void webKitWebSrcEnoughDataCb(GstAppSrc*, gpointer userData)
715 {
716     WebKitWebSrc* src = WEBKIT_WEB_SRC(userData);
717     WebKitWebSrcPrivate* priv = src->priv;
718
719     GST_DEBUG_OBJECT(src, "Have enough data");
720
721     GMutexLocker locker(*GST_OBJECT_GET_LOCK(src));
722     if (priv->enoughDataSource.isScheduled() || priv->paused)
723         return;
724
725     gst_object_ref(src);
726     priv->enoughDataSource.schedule("[WebKit] webKitWebSrcEnoughDataMainCb", std::function<void()>(std::bind(webKitWebSrcEnoughDataMainCb, src)), G_PRIORITY_DEFAULT,
727         [src] { gst_object_unref(src); });
728 }
729
730 static void webKitWebSrcSeekMainCb(WebKitWebSrc* src)
731 {
732     ASSERT(isMainThread());
733
734     webKitWebSrcStop(src);
735     webKitWebSrcStart(src);
736 }
737
738 static gboolean webKitWebSrcSeekDataCb(GstAppSrc*, guint64 offset, gpointer userData)
739 {
740     WebKitWebSrc* src = WEBKIT_WEB_SRC(userData);
741     WebKitWebSrcPrivate* priv = src->priv;
742
743     GST_DEBUG_OBJECT(src, "Seeking to offset: %" G_GUINT64_FORMAT, offset);
744     GMutexLocker locker(*GST_OBJECT_GET_LOCK(src));
745     if (offset == priv->offset && priv->requestedOffset == priv->offset)
746         return TRUE;
747
748     if (!priv->seekable)
749         return FALSE;
750
751     GST_DEBUG_OBJECT(src, "Doing range-request seek");
752     priv->requestedOffset = offset;
753
754     gst_object_ref(src);
755     priv->seekSource.schedule("[WebKit] webKitWebSrcSeekMainCb", std::function<void()>(std::bind(webKitWebSrcSeekMainCb, src)), G_PRIORITY_DEFAULT,
756         [src] { gst_object_unref(src); });
757     return TRUE;
758 }
759
760 void webKitWebSrcSetMediaPlayer(WebKitWebSrc* src, WebCore::MediaPlayer* player)
761 {
762     ASSERT(player);
763     GMutexLocker locker(*GST_OBJECT_GET_LOCK(src));
764     src->priv->player = player;
765 }
766
767 bool webKitSrcPassedCORSAccessCheck(WebKitWebSrc* src)
768 {
769     return src->priv->corsAccessCheck == CORSSuccess;
770 }
771
772 StreamingClient::StreamingClient(WebKitWebSrc* src)
773     : m_src(static_cast<GstElement*>(gst_object_ref(src)))
774 {
775 }
776
777 StreamingClient::~StreamingClient()
778 {
779     gst_object_unref(m_src);
780 }
781
782 char* StreamingClient::createReadBuffer(size_t requestedSize, size_t& actualSize)
783 {
784     WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
785     WebKitWebSrcPrivate* priv = src->priv;
786
787     ASSERT(!priv->buffer);
788
789     GstBuffer* buffer = gst_buffer_new_and_alloc(requestedSize);
790
791     mapGstBuffer(buffer);
792
793     GMutexLocker locker(*GST_OBJECT_GET_LOCK(src));
794     priv->buffer = adoptGRef(buffer);
795     locker.unlock();
796
797     actualSize = gst_buffer_get_size(buffer);
798     return getGstBufferDataPointer(buffer);
799 }
800
801 void StreamingClient::handleResponseReceived(const ResourceResponse& response, CORSAccessCheckResult corsAccessCheck)
802 {
803     WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
804     WebKitWebSrcPrivate* priv = src->priv;
805
806     GST_DEBUG_OBJECT(src, "Received response: %d", response.httpStatusCode());
807
808     if (response.httpStatusCode() >= 400 || corsAccessCheck == CORSFailure) {
809         // Received error code or CORS check failed
810         if (corsAccessCheck == CORSFailure)
811             GST_ELEMENT_ERROR(src, RESOURCE, READ, ("Cross-origin stream load denied by Cross-Origin Resource Sharing policy."), (nullptr));
812         else
813             GST_ELEMENT_ERROR(src, RESOURCE, READ, ("Received %d HTTP error code", response.httpStatusCode()), (nullptr));
814         gst_app_src_end_of_stream(priv->appsrc);
815         webKitWebSrcStop(src);
816         return;
817     }
818
819     GMutexLocker locker(*GST_OBJECT_GET_LOCK(src));
820
821     priv->corsAccessCheck = corsAccessCheck;
822
823     if (priv->seekSource.isActive()) {
824         GST_DEBUG_OBJECT(src, "Seek in progress, ignoring response");
825         return;
826     }
827
828     if (priv->requestedOffset) {
829         // Seeking ... we expect a 206 == PARTIAL_CONTENT
830         if (response.httpStatusCode() == 200) {
831             // Range request didn't have a ranged response; resetting offset.
832             priv->offset = 0;
833         } else if (response.httpStatusCode() != 206) {
834             // Range request completely failed.
835             locker.unlock();
836             GST_ELEMENT_ERROR(src, RESOURCE, READ, ("Received unexpected %d HTTP status code", response.httpStatusCode()), (nullptr));
837             gst_app_src_end_of_stream(priv->appsrc);
838             webKitWebSrcStop(src);
839             return;
840         }
841     }
842
843     long long length = response.expectedContentLength();
844     if (length > 0 && priv->requestedOffset && response.httpStatusCode() == 206)
845         length += priv->requestedOffset;
846
847     priv->size = length >= 0 ? length : 0;
848     priv->seekable = length > 0 && g_ascii_strcasecmp("none", response.httpHeaderField(HTTPHeaderName::AcceptRanges).utf8().data());
849
850     // Wait until we unlock to send notifications
851     g_object_freeze_notify(G_OBJECT(src));
852
853     GstTagList* tags = gst_tag_list_new_empty();
854     String value = response.httpHeaderField(HTTPHeaderName::IcyName);
855     if (!value.isEmpty()) {
856         g_free(priv->iradioName);
857         priv->iradioName = g_strdup(value.utf8().data());
858         g_object_notify(G_OBJECT(src), "iradio-name");
859         gst_tag_list_add(tags, GST_TAG_MERGE_REPLACE, GST_TAG_ORGANIZATION, priv->iradioName, NULL);
860     }
861     value = response.httpHeaderField(HTTPHeaderName::IcyGenre);
862     if (!value.isEmpty()) {
863         g_free(priv->iradioGenre);
864         priv->iradioGenre = g_strdup(value.utf8().data());
865         g_object_notify(G_OBJECT(src), "iradio-genre");
866         gst_tag_list_add(tags, GST_TAG_MERGE_REPLACE, GST_TAG_GENRE, priv->iradioGenre, NULL);
867     }
868     value = response.httpHeaderField(HTTPHeaderName::IcyURL);
869     if (!value.isEmpty()) {
870         g_free(priv->iradioUrl);
871         priv->iradioUrl = g_strdup(value.utf8().data());
872         g_object_notify(G_OBJECT(src), "iradio-url");
873         gst_tag_list_add(tags, GST_TAG_MERGE_REPLACE, GST_TAG_LOCATION, priv->iradioUrl, NULL);
874     }
875     value = response.httpHeaderField(HTTPHeaderName::IcyTitle);
876     if (!value.isEmpty()) {
877         g_free(priv->iradioTitle);
878         priv->iradioTitle = g_strdup(value.utf8().data());
879         g_object_notify(G_OBJECT(src), "iradio-title");
880         gst_tag_list_add(tags, GST_TAG_MERGE_REPLACE, GST_TAG_TITLE, priv->iradioTitle, NULL);
881     }
882
883     locker.unlock();
884     g_object_thaw_notify(G_OBJECT(src));
885
886     // notify size/duration
887     if (length > 0) {
888         gst_app_src_set_size(priv->appsrc, length);
889     } else
890         gst_app_src_set_size(priv->appsrc, -1);
891
892     // icecast stuff
893     value = response.httpHeaderField(HTTPHeaderName::IcyMetaInt);
894     if (!value.isEmpty()) {
895         gchar* endptr = 0;
896         gint64 icyMetaInt = g_ascii_strtoll(value.utf8().data(), &endptr, 10);
897
898         if (endptr && *endptr == '\0' && icyMetaInt > 0) {
899             GRefPtr<GstCaps> caps = adoptGRef(gst_caps_new_simple("application/x-icy", "metadata-interval", G_TYPE_INT, (gint) icyMetaInt, NULL));
900
901             gst_app_src_set_caps(priv->appsrc, caps.get());
902         }
903     } else
904         gst_app_src_set_caps(priv->appsrc, 0);
905
906     // notify tags
907     if (gst_tag_list_is_empty(tags))
908         gst_tag_list_unref(tags);
909     else
910         gst_pad_push_event(priv->srcpad, gst_event_new_tag(tags));
911 }
912
913 void StreamingClient::handleDataReceived(const char* data, int length)
914 {
915     WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
916     WebKitWebSrcPrivate* priv = src->priv;
917
918     GMutexLocker locker(*GST_OBJECT_GET_LOCK(src));
919
920     GST_LOG_OBJECT(src, "Have %lld bytes of data", priv->buffer ? static_cast<long long>(gst_buffer_get_size(priv->buffer.get())) : length);
921
922     ASSERT(!priv->buffer || data == getGstBufferDataPointer(priv->buffer.get()));
923
924     if (priv->buffer)
925         unmapGstBuffer(priv->buffer.get());
926
927     if (priv->seekSource.isActive()) {
928         GST_DEBUG_OBJECT(src, "Seek in progress, ignoring data");
929         priv->buffer.clear();
930         return;
931     }
932
933     if (priv->offset < priv->requestedOffset) {
934         // Range request failed; seeking manually.
935         if (priv->offset + length <= priv->requestedOffset) {
936             // Discard all the buffers coming before the requested seek position.
937             priv->offset += length;
938             priv->buffer.clear();
939             return;
940         }
941
942         if (priv->offset + length > priv->requestedOffset) {
943             guint64 offset = priv->requestedOffset - priv->offset;
944             data += offset;
945             length -= offset;
946             if (priv->buffer)
947                 gst_buffer_resize(priv->buffer.get(), offset, -1);
948             priv->offset = priv->requestedOffset;
949         }
950
951         priv->requestedOffset = 0;
952     }
953
954     // Ports using the GStreamer backend but not the soup implementation of ResourceHandle
955     // won't be using buffers provided by this client, the buffer is created here in that case.
956     if (!priv->buffer)
957         priv->buffer = adoptGRef(createGstBufferForData(data, length));
958     else
959         gst_buffer_set_size(priv->buffer.get(), static_cast<gssize>(length));
960
961     GST_BUFFER_OFFSET(priv->buffer.get()) = priv->offset;
962     if (priv->requestedOffset == priv->offset)
963         priv->requestedOffset += length;
964     priv->offset += length;
965     // priv->size == 0 if received length on didReceiveResponse < 0.
966     if (priv->size > 0 && priv->offset > priv->size) {
967         GST_DEBUG_OBJECT(src, "Updating internal size from %" G_GUINT64_FORMAT " to %" G_GUINT64_FORMAT, priv->size, priv->offset);
968         gst_app_src_set_size(priv->appsrc, priv->offset);
969         priv->size = priv->offset;
970     }
971     GST_BUFFER_OFFSET_END(priv->buffer.get()) = priv->offset;
972
973     locker.unlock();
974
975     GstFlowReturn ret = gst_app_src_push_buffer(priv->appsrc, priv->buffer.leakRef());
976     if (ret != GST_FLOW_OK && ret != GST_FLOW_EOS)
977         GST_ELEMENT_ERROR(src, CORE, FAILED, (0), (0));
978 }
979
980 void StreamingClient::handleNotifyFinished()
981 {
982     WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
983     WebKitWebSrcPrivate* priv = src->priv;
984
985     GST_DEBUG_OBJECT(src, "Have EOS");
986
987     GMutexLocker locker(*GST_OBJECT_GET_LOCK(src));
988     if (!priv->seekSource.isActive()) {
989         locker.unlock();
990         gst_app_src_end_of_stream(priv->appsrc);
991     }
992 }
993
994 CachedResourceStreamingClient::CachedResourceStreamingClient(WebKitWebSrc* src, CachedResourceLoader* resourceLoader, const ResourceRequest& request, MediaPlayerClient::CORSMode corsMode)
995     : StreamingClient(src)
996 {
997     DataBufferingPolicy bufferingPolicy = request.url().protocolIs("blob") ? BufferData : DoNotBufferData;
998     RequestOriginPolicy corsPolicy = corsMode != MediaPlayerClient::Unspecified ? PotentiallyCrossOriginEnabled : UseDefaultOriginRestrictionsForType;
999     StoredCredentials allowCredentials = corsMode == MediaPlayerClient::UseCredentials ? AllowStoredCredentials : DoNotAllowStoredCredentials;
1000     ResourceLoaderOptions options(SendCallbacks, DoNotSniffContent, bufferingPolicy, allowCredentials, DoNotAskClientForCrossOriginCredentials, DoSecurityCheck, corsPolicy, DoNotIncludeCertificateInfo);
1001
1002     CachedResourceRequest cacheRequest(request, options);
1003
1004     if (corsMode != MediaPlayerClient::Unspecified) {
1005         m_origin = resourceLoader->document() ? resourceLoader->document()->securityOrigin() : nullptr;
1006         updateRequestForAccessControl(cacheRequest.mutableResourceRequest(), m_origin.get(), allowCredentials);
1007     }
1008
1009     // TODO: Decide whether to use preflight mode for cross-origin requests (see http://wkbug.com/131484).
1010     m_resource = resourceLoader->requestRawResource(cacheRequest);
1011     if (m_resource)
1012         m_resource->addClient(this);
1013 }
1014
1015 CachedResourceStreamingClient::~CachedResourceStreamingClient()
1016 {
1017     if (m_resource) {
1018         m_resource->removeClient(this);
1019         m_resource = 0;
1020     }
1021 }
1022
1023 bool CachedResourceStreamingClient::loadFailed() const
1024 {
1025     return !m_resource;
1026 }
1027
1028 void CachedResourceStreamingClient::setDefersLoading(bool defers)
1029 {
1030     if (m_resource)
1031         m_resource->setDefersLoading(defers);
1032 }
1033
1034 char* CachedResourceStreamingClient::getOrCreateReadBuffer(CachedResource*, size_t requestedSize, size_t& actualSize)
1035 {
1036     return createReadBuffer(requestedSize, actualSize);
1037 }
1038
1039 void CachedResourceStreamingClient::responseReceived(CachedResource* resource, const ResourceResponse& response)
1040 {
1041     CORSAccessCheckResult corsAccessCheck = CORSNoCheck;
1042     if (m_origin)
1043         corsAccessCheck = (m_origin->canRequest(response.url()) || resource->passesAccessControlCheck(m_origin.get())) ? CORSSuccess : CORSFailure;
1044     handleResponseReceived(response, corsAccessCheck);
1045 }
1046
1047 void CachedResourceStreamingClient::dataReceived(CachedResource*, const char* data, int length)
1048 {
1049     handleDataReceived(data, length);
1050 }
1051
1052 void CachedResourceStreamingClient::notifyFinished(CachedResource* resource)
1053 {
1054     if (resource->loadFailedOrCanceled()) {
1055         WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
1056
1057         if (!resource->wasCanceled()) {
1058             const ResourceError& error = resource->resourceError();
1059             GST_ERROR_OBJECT(src, "Have failure: %s", error.localizedDescription().utf8().data());
1060             GST_ELEMENT_ERROR(src, RESOURCE, FAILED, ("%s", error.localizedDescription().utf8().data()), (0));
1061         }
1062         gst_app_src_end_of_stream(src->priv->appsrc);
1063         return;
1064     }
1065
1066     handleNotifyFinished();
1067 }
1068
1069 ResourceHandleStreamingClient::ResourceHandleStreamingClient(WebKitWebSrc* src, const ResourceRequest& request)
1070     : StreamingClient(src)
1071 {
1072     m_resource = ResourceHandle::create(0 /*context*/, request, this, false, false);
1073 }
1074
1075 ResourceHandleStreamingClient::~ResourceHandleStreamingClient()
1076 {
1077     if (m_resource) {
1078         m_resource->cancel();
1079         m_resource.release();
1080         m_resource = 0;
1081     }
1082 }
1083
1084 bool ResourceHandleStreamingClient::loadFailed() const
1085 {
1086     return !m_resource;
1087 }
1088
1089 void ResourceHandleStreamingClient::setDefersLoading(bool defers)
1090 {
1091     if (m_resource)
1092         m_resource->setDefersLoading(defers);
1093 }
1094
1095 char* ResourceHandleStreamingClient::getOrCreateReadBuffer(size_t requestedSize, size_t& actualSize)
1096 {
1097     return createReadBuffer(requestedSize, actualSize);
1098 }
1099
1100 void ResourceHandleStreamingClient::willSendRequest(ResourceHandle*, ResourceRequest&, const ResourceResponse&)
1101 {
1102 }
1103
1104 void ResourceHandleStreamingClient::didReceiveResponse(ResourceHandle*, const ResourceResponse& response)
1105 {
1106     handleResponseReceived(response, CORSNoCheck);
1107 }
1108
1109 void ResourceHandleStreamingClient::didReceiveData(ResourceHandle*, const char* /* data */, unsigned /* length */, int)
1110 {
1111     ASSERT_NOT_REACHED();
1112 }
1113
1114 void ResourceHandleStreamingClient::didReceiveBuffer(ResourceHandle*, PassRefPtr<SharedBuffer> buffer, int /* encodedLength */)
1115 {
1116     // This pattern is suggested by SharedBuffer.h.
1117     const char* segment;
1118     unsigned position = 0;
1119     while (unsigned length = buffer->getSomeData(segment, position)) {
1120         handleDataReceived(segment, length);
1121         position += length;
1122     }
1123 }
1124
1125 void ResourceHandleStreamingClient::didFinishLoading(ResourceHandle*, double)
1126 {
1127     handleNotifyFinished();
1128 }
1129
1130 void ResourceHandleStreamingClient::didFail(ResourceHandle*, const ResourceError& error)
1131 {
1132     WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
1133
1134     GST_ERROR_OBJECT(src, "Have failure: %s", error.localizedDescription().utf8().data());
1135     GST_ELEMENT_ERROR(src, RESOURCE, FAILED, ("%s", error.localizedDescription().utf8().data()), (0));
1136     gst_app_src_end_of_stream(src->priv->appsrc);
1137 }
1138
1139 void ResourceHandleStreamingClient::wasBlocked(ResourceHandle*)
1140 {
1141     WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
1142     GUniquePtr<gchar> uri;
1143
1144     GST_ERROR_OBJECT(src, "Request was blocked");
1145
1146     GMutexLocker locker(*GST_OBJECT_GET_LOCK(src));
1147     uri.reset(g_strdup(src->priv->uri));
1148     locker.unlock();
1149
1150     GST_ELEMENT_ERROR(src, RESOURCE, OPEN_READ, ("Access to \"%s\" was blocked", uri.get()), (0));
1151 }
1152
1153 void ResourceHandleStreamingClient::cannotShowURL(ResourceHandle*)
1154 {
1155     WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
1156     GUniquePtr<gchar> uri;
1157
1158     GST_ERROR_OBJECT(src, "Cannot show URL");
1159
1160     GMutexLocker locker(*GST_OBJECT_GET_LOCK(src));
1161     uri.reset(g_strdup(src->priv->uri));
1162     locker.unlock();
1163
1164     GST_ELEMENT_ERROR(src, RESOURCE, OPEN_READ, ("Can't show \"%s\"", uri.get()), (0));
1165 }
1166
1167 #endif // USE(GSTREAMER)
1168