Stop using setDefersLoading from WebCore
[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 "GStreamerCommon.h"
26 #include "HTTPHeaderNames.h"
27 #include "MainThreadNotifier.h"
28 #include "MediaPlayer.h"
29 #include "PlatformMediaResourceLoader.h"
30 #include "ResourceError.h"
31 #include "ResourceRequest.h"
32 #include "ResourceResponse.h"
33 #include "SecurityOrigin.h"
34 #include <cstdint>
35 #include <gst/app/gstappsrc.h>
36 #include <gst/pbutils/missing-plugins.h>
37 #include <wtf/text/CString.h>
38
39 using namespace WebCore;
40
41 class CachedResourceStreamingClient final : public PlatformMediaResourceClient {
42     WTF_MAKE_NONCOPYABLE(CachedResourceStreamingClient);
43 public:
44     CachedResourceStreamingClient(WebKitWebSrc*, ResourceRequest&&);
45     virtual ~CachedResourceStreamingClient();
46
47     const HashSet<RefPtr<WebCore::SecurityOrigin>>& securityOrigins() const { return m_origins; }
48
49 private:
50     void checkUpdateBlocksize(uint64_t bytesRead);
51
52     // PlatformMediaResourceClient virtual methods.
53     void responseReceived(PlatformMediaResource&, const ResourceResponse&, CompletionHandler<void(ShouldContinue)>&&) override;
54     void dataReceived(PlatformMediaResource&, const char*, int) override;
55     void accessControlCheckFailed(PlatformMediaResource&, const ResourceError&) override;
56     void loadFailed(PlatformMediaResource&, const ResourceError&) override;
57     void loadFinished(PlatformMediaResource&) override;
58
59     static constexpr int s_growBlocksizeLimit { 1 };
60     static constexpr int s_growBlocksizeCount { 1 };
61     static constexpr int s_growBlocksizeFactor { 2 };
62     static constexpr float s_reduceBlocksizeLimit { 0.20 };
63     static constexpr int s_reduceBlocksizeCount { 2 };
64     static constexpr float s_reduceBlocksizeFactor { 0.5 };
65     int m_reduceBlocksizeCount { 0 };
66     int m_increaseBlocksizeCount { 0 };
67
68     GRefPtr<GstElement> m_src;
69     ResourceRequest m_request;
70     HashSet<RefPtr<WebCore::SecurityOrigin>> m_origins;
71 };
72
73 enum MainThreadSourceNotification {
74     Start = 1 << 0,
75     Stop = 1 << 1,
76     NeedData = 1 << 2,
77     EnoughData = 1 << 3,
78     Seek = 1 << 4
79 };
80
81 #define WEBKIT_WEB_SRC_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_WEB_SRC, WebKitWebSrcPrivate))
82 struct _WebKitWebSrcPrivate {
83     GstAppSrc* appsrc;
84     GstPad* srcpad;
85     CString originalURI;
86     CString redirectedURI;
87     bool keepAlive;
88     GUniquePtr<GstStructure> extraHeaders;
89     bool compress;
90     GUniquePtr<gchar> httpMethod;
91
92     WebCore::MediaPlayer* player;
93
94     RefPtr<PlatformMediaResourceLoader> loader;
95     RefPtr<PlatformMediaResource> resource;
96
97     bool didPassAccessControlCheck;
98
99     guint64 offset;
100     bool haveSize;
101     guint64 size;
102     gboolean seekable;
103     bool paused;
104     bool isSeeking;
105
106     guint64 requestedOffset;
107
108     uint64_t minimumBlocksize;
109
110     RefPtr<MainThreadNotifier<MainThreadSourceNotification>> notifier;
111 };
112
113 enum {
114     PROP_0,
115     PROP_LOCATION,
116     PROP_RESOLVED_LOCATION,
117     PROP_KEEP_ALIVE,
118     PROP_EXTRA_HEADERS,
119     PROP_COMPRESS,
120     PROP_METHOD
121 };
122
123 static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE("src",
124                                                                   GST_PAD_SRC,
125                                                                   GST_PAD_ALWAYS,
126                                                                   GST_STATIC_CAPS_ANY);
127
128 GST_DEBUG_CATEGORY_STATIC(webkit_web_src_debug);
129 #define GST_CAT_DEFAULT webkit_web_src_debug
130
131 static void webKitWebSrcUriHandlerInit(gpointer gIface, gpointer ifaceData);
132
133 static void webKitWebSrcDispose(GObject*);
134 static void webKitWebSrcFinalize(GObject*);
135 static void webKitWebSrcSetProperty(GObject*, guint propertyID, const GValue*, GParamSpec*);
136 static void webKitWebSrcGetProperty(GObject*, guint propertyID, GValue*, GParamSpec*);
137 static GstStateChangeReturn webKitWebSrcChangeState(GstElement*, GstStateChange);
138
139 static gboolean webKitWebSrcQueryWithParent(GstPad*, GstObject*, GstQuery*);
140
141 static void webKitWebSrcNeedData(WebKitWebSrc*);
142 static void webKitWebSrcEnoughData(WebKitWebSrc*);
143 static gboolean webKitWebSrcSeek(WebKitWebSrc*, guint64);
144
145 static GstAppSrcCallbacks appsrcCallbacks = {
146     // need_data
147     [](GstAppSrc*, guint, gpointer userData) {
148         webKitWebSrcNeedData(WEBKIT_WEB_SRC(userData));
149     },
150     // enough_data
151     [](GstAppSrc*, gpointer userData) {
152         webKitWebSrcEnoughData(WEBKIT_WEB_SRC(userData));
153     },
154     // seek_data
155     [](GstAppSrc*, guint64 offset, gpointer userData) -> gboolean {
156         return webKitWebSrcSeek(WEBKIT_WEB_SRC(userData), offset);
157     },
158     { nullptr }
159 };
160
161 #define webkit_web_src_parent_class parent_class
162 // We split this out into another macro to avoid a check-webkit-style error.
163 #define WEBKIT_WEB_SRC_CATEGORY_INIT GST_DEBUG_CATEGORY_INIT(webkit_web_src_debug, "webkitwebsrc", 0, "websrc element");
164 G_DEFINE_TYPE_WITH_CODE(WebKitWebSrc, webkit_web_src, GST_TYPE_BIN,
165                          G_IMPLEMENT_INTERFACE(GST_TYPE_URI_HANDLER, webKitWebSrcUriHandlerInit);
166                          WEBKIT_WEB_SRC_CATEGORY_INIT);
167
168 static void webkit_web_src_class_init(WebKitWebSrcClass* klass)
169 {
170     GObjectClass* oklass = G_OBJECT_CLASS(klass);
171     GstElementClass* eklass = GST_ELEMENT_CLASS(klass);
172
173     oklass->dispose = webKitWebSrcDispose;
174     oklass->finalize = webKitWebSrcFinalize;
175     oklass->set_property = webKitWebSrcSetProperty;
176     oklass->get_property = webKitWebSrcGetProperty;
177
178     gst_element_class_add_pad_template(eklass,
179                                        gst_static_pad_template_get(&srcTemplate));
180     gst_element_class_set_metadata(eklass, "WebKit Web source element", "Source", "Handles HTTP/HTTPS uris",
181                                "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
182
183     /* Allows setting the uri using the 'location' property, which is used
184      * for example by gst_element_make_from_uri() */
185     g_object_class_install_property(oklass, PROP_LOCATION,
186         g_param_spec_string("location", "location", "Location to read from",
187             nullptr, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
188
189     g_object_class_install_property(oklass, PROP_RESOLVED_LOCATION,
190         g_param_spec_string("resolved-location", "Resolved location", "The location resolved by the server",
191             nullptr, static_cast<GParamFlags>(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
192
193     g_object_class_install_property(oklass, PROP_KEEP_ALIVE,
194         g_param_spec_boolean("keep-alive", "keep-alive", "Use HTTP persistent connections",
195             FALSE, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
196
197     g_object_class_install_property(oklass, PROP_EXTRA_HEADERS,
198         g_param_spec_boxed("extra-headers", "Extra Headers", "Extra headers to append to the HTTP request",
199             GST_TYPE_STRUCTURE, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
200
201     g_object_class_install_property(oklass, PROP_COMPRESS,
202         g_param_spec_boolean("compress", "Compress", "Allow compressed content encodings",
203             FALSE, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
204
205     g_object_class_install_property(oklass, PROP_METHOD,
206         g_param_spec_string("method", "method", "The HTTP method to use (default: GET)",
207             nullptr, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
208
209     eklass->change_state = webKitWebSrcChangeState;
210
211     g_type_class_add_private(klass, sizeof(WebKitWebSrcPrivate));
212 }
213
214 static void webkit_web_src_init(WebKitWebSrc* src)
215 {
216     WebKitWebSrcPrivate* priv = WEBKIT_WEB_SRC_GET_PRIVATE(src);
217
218     src->priv = priv;
219     new (priv) WebKitWebSrcPrivate();
220
221     priv->notifier = MainThreadNotifier<MainThreadSourceNotification>::create();
222
223     priv->haveSize = FALSE;
224     priv->size = 0;
225
226     priv->appsrc = GST_APP_SRC(gst_element_factory_make("appsrc", nullptr));
227     if (!priv->appsrc) {
228         GST_ERROR_OBJECT(src, "Failed to create appsrc");
229         return;
230     }
231
232     gst_bin_add(GST_BIN(src), GST_ELEMENT(priv->appsrc));
233
234     GRefPtr<GstPad> targetPad = adoptGRef(gst_element_get_static_pad(GST_ELEMENT(priv->appsrc), "src"));
235     priv->srcpad = webkitGstGhostPadFromStaticTemplate(&srcTemplate, "src", targetPad.get());
236
237     gst_element_add_pad(GST_ELEMENT(src), priv->srcpad);
238
239     GST_OBJECT_FLAG_SET(priv->srcpad, GST_PAD_FLAG_NEED_PARENT);
240     gst_pad_set_query_function(priv->srcpad, webKitWebSrcQueryWithParent);
241
242     gst_app_src_set_callbacks(priv->appsrc, &appsrcCallbacks, src, nullptr);
243     gst_app_src_set_emit_signals(priv->appsrc, FALSE);
244     gst_app_src_set_stream_type(priv->appsrc, GST_APP_STREAM_TYPE_SEEKABLE);
245
246     // 512k is a abitrary number but we should choose a value
247     // here to not pause/unpause the SoupMessage too often and
248     // to make sure there's always some data available for
249     // GStreamer to handle.
250     gst_app_src_set_max_bytes(priv->appsrc, 512 * 1024);
251
252     // Emit the need-data signal if the queue contains less
253     // than 20% of data. Without this the need-data signal
254     // is emitted when the queue is empty, we then dispatch
255     // the soup message unpausing to the main loop and from
256     // there unpause the soup message. This already takes
257     // quite some time and libsoup even needs some more time
258     // to actually provide data again. If we do all this
259     // already if the queue is 20% empty, it's much more
260     // likely that libsoup already provides new data before
261     // the queue is really empty.
262     // This might need tweaking for ports not using libsoup.
263     g_object_set(priv->appsrc, "min-percent", 20, nullptr);
264
265     gst_base_src_set_automatic_eos(GST_BASE_SRC(priv->appsrc), FALSE);
266
267     gst_app_src_set_caps(priv->appsrc, nullptr);
268
269     priv->minimumBlocksize = gst_base_src_get_blocksize(GST_BASE_SRC_CAST(priv->appsrc));
270 }
271
272 static void webKitWebSrcDispose(GObject* object)
273 {
274     WebKitWebSrcPrivate* priv = WEBKIT_WEB_SRC(object)->priv;
275     if (priv->notifier) {
276         priv->notifier->invalidate();
277         priv->notifier = nullptr;
278     }
279
280     GST_CALL_PARENT(G_OBJECT_CLASS, dispose, (object));
281 }
282
283 static void webKitWebSrcFinalize(GObject* object)
284 {
285     WebKitWebSrcPrivate* priv = WEBKIT_WEB_SRC(object)->priv;
286
287     priv->~WebKitWebSrcPrivate();
288
289     GST_CALL_PARENT(G_OBJECT_CLASS, finalize, (object));
290 }
291
292 static void webKitWebSrcSetProperty(GObject* object, guint propID, const GValue* value, GParamSpec* pspec)
293 {
294     WebKitWebSrc* src = WEBKIT_WEB_SRC(object);
295
296     switch (propID) {
297     case PROP_LOCATION:
298         gst_uri_handler_set_uri(reinterpret_cast<GstURIHandler*>(src), g_value_get_string(value), nullptr);
299         break;
300     case PROP_KEEP_ALIVE:
301         src->priv->keepAlive = g_value_get_boolean(value);
302         break;
303     case PROP_EXTRA_HEADERS: {
304         const GstStructure* s = gst_value_get_structure(value);
305         src->priv->extraHeaders.reset(s ? gst_structure_copy(s) : nullptr);
306         break;
307     }
308     case PROP_COMPRESS:
309         src->priv->compress = g_value_get_boolean(value);
310         break;
311     case PROP_METHOD:
312         src->priv->httpMethod.reset(g_value_dup_string(value));
313         break;
314     default:
315         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, pspec);
316         break;
317     }
318 }
319
320 static void webKitWebSrcGetProperty(GObject* object, guint propID, GValue* value, GParamSpec* pspec)
321 {
322     WebKitWebSrc* src = WEBKIT_WEB_SRC(object);
323     WebKitWebSrcPrivate* priv = src->priv;
324
325     switch (propID) {
326     case PROP_LOCATION:
327         g_value_set_string(value, priv->originalURI.data());
328         break;
329     case PROP_RESOLVED_LOCATION:
330         g_value_set_string(value, priv->redirectedURI.isNull() ? priv->originalURI.data() : priv->redirectedURI.data());
331         break;
332     case PROP_KEEP_ALIVE:
333         g_value_set_boolean(value, priv->keepAlive);
334         break;
335     case PROP_EXTRA_HEADERS:
336         gst_value_set_structure(value, priv->extraHeaders.get());
337         break;
338     case PROP_COMPRESS:
339         g_value_set_boolean(value, priv->compress);
340         break;
341     case PROP_METHOD:
342         g_value_set_string(value, priv->httpMethod.get());
343         break;
344     default:
345         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, pspec);
346         break;
347     }
348 }
349
350 static void webKitWebSrcStop(WebKitWebSrc* src)
351 {
352     WebKitWebSrcPrivate* priv = src->priv;
353
354     if (priv->resource || (priv->loader && !priv->keepAlive)) {
355         GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src);
356         priv->notifier->cancelPendingNotifications(MainThreadSourceNotification::NeedData | MainThreadSourceNotification::EnoughData | MainThreadSourceNotification::Seek);
357         priv->notifier->notify(MainThreadSourceNotification::Stop, [protector, keepAlive = priv->keepAlive] {
358             WebKitWebSrcPrivate* priv = protector->priv;
359
360             if (priv->resource) {
361                 priv->resource->stop();
362                 priv->resource->setClient(nullptr);
363                 priv->resource = nullptr;
364             }
365
366             if (!keepAlive)
367                 priv->loader = nullptr;
368         });
369     }
370
371     bool wasSeeking = std::exchange(priv->isSeeking, false);
372
373     priv->paused = false;
374
375     priv->offset = 0;
376
377     if (!wasSeeking) {
378         priv->requestedOffset = 0;
379         priv->player = nullptr;
380         priv->seekable = FALSE;
381     }
382
383     if (priv->appsrc) {
384         gst_app_src_set_caps(priv->appsrc, nullptr);
385         if (!wasSeeking)
386             gst_app_src_set_size(priv->appsrc, -1);
387     }
388
389     GST_DEBUG_OBJECT(src, "Stopped request");
390 }
391
392 static bool webKitWebSrcSetExtraHeader(GQuark fieldId, const GValue* value, gpointer userData)
393 {
394     GUniquePtr<gchar> fieldContent;
395
396     if (G_VALUE_HOLDS_STRING(value))
397         fieldContent.reset(g_value_dup_string(value));
398     else {
399         GValue dest = G_VALUE_INIT;
400
401         g_value_init(&dest, G_TYPE_STRING);
402         if (g_value_transform(value, &dest))
403             fieldContent.reset(g_value_dup_string(&dest));
404     }
405
406     const gchar* fieldName = g_quark_to_string(fieldId);
407     if (!fieldContent.get()) {
408         GST_ERROR("extra-headers field '%s' contains no value or can't be converted to a string", fieldName);
409         return false;
410     }
411
412     GST_DEBUG("Appending extra header: \"%s: %s\"", fieldName, fieldContent.get());
413     ResourceRequest* request = static_cast<ResourceRequest*>(userData);
414     request->setHTTPHeaderField(fieldName, fieldContent.get());
415     return true;
416 }
417
418 static gboolean webKitWebSrcProcessExtraHeaders(GQuark fieldId, const GValue* value, gpointer userData)
419 {
420     if (G_VALUE_TYPE(value) == GST_TYPE_ARRAY) {
421         unsigned size = gst_value_array_get_size(value);
422
423         for (unsigned i = 0; i < size; i++) {
424             if (!webKitWebSrcSetExtraHeader(fieldId, gst_value_array_get_value(value, i), userData))
425                 return FALSE;
426         }
427         return TRUE;
428     }
429
430     if (G_VALUE_TYPE(value) == GST_TYPE_LIST) {
431         unsigned size = gst_value_list_get_size(value);
432
433         for (unsigned i = 0; i < size; i++) {
434             if (!webKitWebSrcSetExtraHeader(fieldId, gst_value_list_get_value(value, i), userData))
435                 return FALSE;
436         }
437         return TRUE;
438     }
439
440     return webKitWebSrcSetExtraHeader(fieldId, value, userData);
441 }
442
443 static void webKitWebSrcStart(WebKitWebSrc* src)
444 {
445     WebKitWebSrcPrivate* priv = src->priv;
446     ASSERT(priv->player);
447
448     priv->didPassAccessControlCheck = false;
449
450     if (priv->originalURI.isNull()) {
451         GST_ERROR_OBJECT(src, "No URI provided");
452         webKitWebSrcStop(src);
453         return;
454     }
455
456     GST_DEBUG_OBJECT(src, "Fetching %s", priv->originalURI.data());
457     URL url = URL(URL(), priv->originalURI.data());
458
459     ResourceRequest request(url);
460     request.setAllowCookies(true);
461     request.setFirstPartyForCookies(url);
462
463     request.setHTTPReferrer(priv->player->referrer());
464
465     if (priv->httpMethod.get())
466         request.setHTTPMethod(priv->httpMethod.get());
467
468 #if USE(SOUP)
469     // By default, HTTP Accept-Encoding is disabled here as we don't
470     // want the received response to be encoded in any way as we need
471     // to rely on the proper size of the returned data on
472     // didReceiveResponse.
473     // If Accept-Encoding is used, the server may send the data in encoded format and
474     // request.expectedContentLength() will have the "wrong" size (the size of the
475     // compressed data), even though the data received in didReceiveData is uncompressed.
476     // This is however useful to enable for adaptive streaming
477     // scenarios, when the demuxer needs to download playlists.
478     if (!priv->compress)
479         request.setAcceptEncoding(false);
480 #endif
481
482     // Let Apple web servers know we want to access their nice movie trailers.
483     if (!g_ascii_strcasecmp("movies.apple.com", url.host().utf8().data())
484         || !g_ascii_strcasecmp("trailers.apple.com", url.host().utf8().data()))
485         request.setHTTPUserAgent("Quicktime/7.6.6");
486
487     if (priv->requestedOffset) {
488         GUniquePtr<gchar> val(g_strdup_printf("bytes=%" G_GUINT64_FORMAT "-", priv->requestedOffset));
489         request.setHTTPHeaderField(HTTPHeaderName::Range, val.get());
490     }
491     priv->offset = priv->requestedOffset;
492
493     GST_DEBUG_OBJECT(src, "Persistent connection support %s", priv->keepAlive ? "enabled" : "disabled");
494     if (!priv->keepAlive) {
495         request.setHTTPHeaderField(HTTPHeaderName::Connection, "close");
496     }
497
498     if (priv->extraHeaders)
499         gst_structure_foreach(priv->extraHeaders.get(), webKitWebSrcProcessExtraHeaders, &request);
500
501     // We always request Icecast/Shoutcast metadata, just in case ...
502     request.setHTTPHeaderField(HTTPHeaderName::IcyMetadata, "1");
503
504     GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src);
505     priv->notifier->notify(MainThreadSourceNotification::Start, [protector, request = WTFMove(request)] {
506         WebKitWebSrcPrivate* priv = protector->priv;
507
508         if (!priv->loader)
509             priv->loader = priv->player->createResourceLoader();
510
511         PlatformMediaResourceLoader::LoadOptions loadOptions = 0;
512         if (request.url().protocolIsBlob())
513             loadOptions |= PlatformMediaResourceLoader::LoadOption::BufferData;
514         priv->resource = priv->loader->requestResource(ResourceRequest(request), loadOptions);
515         if (priv->resource) {
516             priv->resource->setClient(std::make_unique<CachedResourceStreamingClient>(protector.get(), ResourceRequest(request)));
517             GST_DEBUG_OBJECT(protector.get(), "Started request");
518         } else {
519             GST_ERROR_OBJECT(protector.get(), "Failed to setup streaming client");
520             priv->loader = nullptr;
521             webKitWebSrcStop(protector.get());
522         }
523     });
524 }
525
526 static GstStateChangeReturn webKitWebSrcChangeState(GstElement* element, GstStateChange transition)
527 {
528     GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
529     WebKitWebSrc* src = WEBKIT_WEB_SRC(element);
530     WebKitWebSrcPrivate* priv = src->priv;
531
532     switch (transition) {
533     case GST_STATE_CHANGE_NULL_TO_READY:
534         if (!priv->appsrc) {
535             gst_element_post_message(element,
536                                      gst_missing_element_message_new(element, "appsrc"));
537             GST_ELEMENT_ERROR(src, CORE, MISSING_PLUGIN, (nullptr), ("no appsrc"));
538             return GST_STATE_CHANGE_FAILURE;
539         }
540         break;
541     default:
542         break;
543     }
544
545     ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
546     if (G_UNLIKELY(ret == GST_STATE_CHANGE_FAILURE)) {
547         GST_DEBUG_OBJECT(src, "State change failed");
548         return ret;
549     }
550
551     switch (transition) {
552     case GST_STATE_CHANGE_READY_TO_PAUSED:
553     {
554         GST_DEBUG_OBJECT(src, "READY->PAUSED");
555         webKitWebSrcStart(src);
556         break;
557     }
558     case GST_STATE_CHANGE_PAUSED_TO_READY:
559     {
560         GST_DEBUG_OBJECT(src, "PAUSED->READY");
561         webKitWebSrcStop(src);
562         break;
563     }
564     default:
565         break;
566     }
567
568     return ret;
569 }
570
571 static gboolean webKitWebSrcQueryWithParent(GstPad* pad, GstObject* parent, GstQuery* query)
572 {
573     WebKitWebSrc* src = WEBKIT_WEB_SRC(GST_ELEMENT(parent));
574     WebKitWebSrcPrivate* priv = src->priv;
575     gboolean result = FALSE;
576
577     switch (GST_QUERY_TYPE(query)) {
578     case GST_QUERY_URI: {
579         gst_query_set_uri(query, priv->originalURI.data());
580         if (!priv->redirectedURI.isNull())
581             gst_query_set_uri_redirection(query, priv->redirectedURI.data());
582         result = TRUE;
583         break;
584     }
585     case GST_QUERY_SCHEDULING: {
586         GstSchedulingFlags flags;
587         int minSize, maxSize, align;
588
589         gst_query_parse_scheduling(query, &flags, &minSize, &maxSize, &align);
590         gst_query_set_scheduling(query, static_cast<GstSchedulingFlags>(flags | GST_SCHEDULING_FLAG_BANDWIDTH_LIMITED), minSize, maxSize, align);
591         result = TRUE;
592         break;
593     }
594     default: {
595         GRefPtr<GstPad> target = adoptGRef(gst_ghost_pad_get_target(GST_GHOST_PAD_CAST(pad)));
596
597         // Forward the query to the proxy target pad.
598         if (target)
599             result = gst_pad_query(target.get(), query);
600         break;
601     }
602     }
603
604     return result;
605 }
606
607 static bool urlHasSupportedProtocol(const URL& url)
608 {
609     return url.isValid() && (url.protocolIsInHTTPFamily() || url.protocolIsBlob());
610 }
611
612 // uri handler interface
613
614 static GstURIType webKitWebSrcUriGetType(GType)
615 {
616     return GST_URI_SRC;
617 }
618
619 const gchar* const* webKitWebSrcGetProtocols(GType)
620 {
621     static const char* protocols[] = {"webkit+http", "webkit+https", "webkit+blob", nullptr };
622     return protocols;
623 }
624
625 static URL convertPlaybinURI(const char* uriString)
626 {
627     URL url(URL(), uriString);
628     ASSERT(url.protocol().substring(0, 7) == "webkit+");
629     url.setProtocol(url.protocol().substring(7).toString());
630     return url;
631 }
632
633 static gchar* webKitWebSrcGetUri(GstURIHandler* handler)
634 {
635     WebKitWebSrc* src = WEBKIT_WEB_SRC(handler);
636     gchar* ret = g_strdup(src->priv->originalURI.data());
637     return ret;
638 }
639
640 static gboolean webKitWebSrcSetUri(GstURIHandler* handler, const gchar* uri, GError** error)
641 {
642     WebKitWebSrc* src = WEBKIT_WEB_SRC(handler);
643     WebKitWebSrcPrivate* priv = src->priv;
644
645     if (GST_STATE(src) >= GST_STATE_PAUSED) {
646         GST_ERROR_OBJECT(src, "URI can only be set in states < PAUSED");
647         return FALSE;
648     }
649
650     priv->redirectedURI = CString();
651     priv->originalURI = CString();
652     if (!uri)
653         return TRUE;
654
655     URL url = convertPlaybinURI(uri);
656     if (!urlHasSupportedProtocol(url)) {
657         g_set_error(error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI, "Invalid URI '%s'", uri);
658         return FALSE;
659     }
660
661     priv->originalURI = url.string().utf8();
662     return TRUE;
663 }
664
665 static void webKitWebSrcUriHandlerInit(gpointer gIface, gpointer)
666 {
667     GstURIHandlerInterface* iface = (GstURIHandlerInterface *) gIface;
668
669     iface->get_type = webKitWebSrcUriGetType;
670     iface->get_protocols = webKitWebSrcGetProtocols;
671     iface->get_uri = webKitWebSrcGetUri;
672     iface->set_uri = webKitWebSrcSetUri;
673 }
674
675 static void webKitWebSrcNeedData(WebKitWebSrc* src)
676 {
677     WebKitWebSrcPrivate* priv = src->priv;
678
679     GST_LOG_OBJECT(src, "Need more data");
680
681     if (!priv->paused)
682         return;
683     priv->paused = false;
684
685     GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src);
686     priv->notifier->notify(MainThreadSourceNotification::NeedData, [protector] { });
687 }
688
689 static void webKitWebSrcEnoughData(WebKitWebSrc* src)
690 {
691     WebKitWebSrcPrivate* priv = src->priv;
692
693     GST_DEBUG_OBJECT(src, "Have enough data");
694
695     if (priv->paused)
696         return;
697     priv->paused = true;
698
699     GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src);
700     priv->notifier->notify(MainThreadSourceNotification::EnoughData, [protector] {
701         WebKitWebSrcPrivate* priv = protector->priv;
702         if (priv->resource)
703             priv->resource->stop();
704     });
705 }
706
707 static gboolean webKitWebSrcSeek(WebKitWebSrc* src, guint64 offset)
708 {
709     WebKitWebSrcPrivate* priv = src->priv;
710
711     if (offset == priv->offset && priv->requestedOffset == priv->offset)
712         return TRUE;
713
714     if (!priv->seekable)
715         return FALSE;
716
717     priv->isSeeking = true;
718     priv->requestedOffset = offset;
719
720     GST_DEBUG_OBJECT(src, "Seeking to offset: %" G_GUINT64_FORMAT, src->priv->requestedOffset);
721
722     GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src);
723     priv->notifier->notify(MainThreadSourceNotification::Seek, [protector] {
724         webKitWebSrcStop(protector.get());
725         webKitWebSrcStart(protector.get());
726     });
727     return TRUE;
728 }
729
730 void webKitWebSrcSetMediaPlayer(WebKitWebSrc* src, WebCore::MediaPlayer* player)
731 {
732     ASSERT(player);
733     src->priv->player = player;
734 }
735
736 bool webKitSrcPassedCORSAccessCheck(WebKitWebSrc* src)
737 {
738     return src->priv->didPassAccessControlCheck;
739 }
740
741 CachedResourceStreamingClient::CachedResourceStreamingClient(WebKitWebSrc* src, ResourceRequest&& request)
742     : m_src(GST_ELEMENT(src))
743     , m_request(WTFMove(request))
744 {
745 }
746
747 CachedResourceStreamingClient::~CachedResourceStreamingClient() = default;
748
749 void CachedResourceStreamingClient::checkUpdateBlocksize(uint64_t bytesRead)
750 {
751     WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get());
752     WebKitWebSrcPrivate* priv = src->priv;
753
754     uint64_t blocksize = gst_base_src_get_blocksize(GST_BASE_SRC_CAST(priv->appsrc));
755     GST_LOG_OBJECT(src, "Checking to update blocksize. Read:%" PRIu64 " blocksize:%" PRIu64, bytesRead, blocksize);
756
757     if (bytesRead >= blocksize * s_growBlocksizeLimit) {
758         m_reduceBlocksizeCount = 0;
759         m_increaseBlocksizeCount++;
760
761         if (m_increaseBlocksizeCount >= s_growBlocksizeCount) {
762             blocksize *= s_growBlocksizeFactor;
763             GST_DEBUG_OBJECT(src, "Increased blocksize to %" PRIu64, blocksize);
764             gst_base_src_set_blocksize(GST_BASE_SRC_CAST(priv->appsrc), blocksize);
765             m_increaseBlocksizeCount = 0;
766         }
767     } else if (bytesRead < blocksize * s_reduceBlocksizeLimit) {
768         m_reduceBlocksizeCount++;
769         m_increaseBlocksizeCount = 0;
770
771         if (m_reduceBlocksizeCount >= s_reduceBlocksizeCount) {
772             blocksize *= s_reduceBlocksizeFactor;
773             blocksize = std::max(blocksize, priv->minimumBlocksize);
774             GST_DEBUG_OBJECT(src, "Decreased blocksize to %" PRIu64, blocksize);
775             gst_base_src_set_blocksize(GST_BASE_SRC_CAST(priv->appsrc), blocksize);
776             m_reduceBlocksizeCount = 0;
777         }
778     } else {
779         m_reduceBlocksizeCount = 0;
780         m_increaseBlocksizeCount = 0;
781     }
782 }
783
784 void CachedResourceStreamingClient::responseReceived(PlatformMediaResource&, const ResourceResponse& response, CompletionHandler<void(ShouldContinue)>&& completionHandler)
785 {
786     WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get());
787     WebKitWebSrcPrivate* priv = src->priv;
788     priv->didPassAccessControlCheck = priv->resource->didPassAccessControlCheck();
789
790     GST_DEBUG_OBJECT(src, "Received response: %d", response.httpStatusCode());
791
792     auto origin = SecurityOrigin::create(response.url());
793     m_origins.add(WTFMove(origin));
794
795     auto responseURI = response.url().string().utf8();
796     if (priv->originalURI != responseURI)
797         priv->redirectedURI = WTFMove(responseURI);
798
799     if (response.httpStatusCode() >= 400) {
800         GST_ELEMENT_ERROR(src, RESOURCE, READ, ("Received %d HTTP error code", response.httpStatusCode()), (nullptr));
801         gst_app_src_end_of_stream(priv->appsrc);
802         webKitWebSrcStop(src);
803         return completionHandler(ShouldContinue::No);
804     }
805
806     if (priv->isSeeking) {
807         GST_DEBUG_OBJECT(src, "Seek in progress, ignoring response");
808         return completionHandler(ShouldContinue::Yes);
809     }
810
811     if (priv->requestedOffset) {
812         // Seeking ... we expect a 206 == PARTIAL_CONTENT
813         if (response.httpStatusCode() == 200) {
814             // Range request didn't have a ranged response; resetting offset.
815             priv->offset = 0;
816         } else if (response.httpStatusCode() != 206) {
817             // Range request completely failed.
818             GST_ELEMENT_ERROR(src, RESOURCE, READ, ("Received unexpected %d HTTP status code", response.httpStatusCode()), (nullptr));
819             gst_app_src_end_of_stream(priv->appsrc);
820             webKitWebSrcStop(src);
821             return completionHandler(ShouldContinue::No);
822         }
823     }
824
825     long long length = response.expectedContentLength();
826     if (length > 0 && priv->requestedOffset && response.httpStatusCode() == 206)
827         length += priv->requestedOffset;
828
829     priv->seekable = length > 0 && g_ascii_strcasecmp("none", response.httpHeaderField(HTTPHeaderName::AcceptRanges).utf8().data());
830
831     GST_DEBUG_OBJECT(src, "Size: %lld, seekable: %s", length, priv->seekable ? "yes" : "no");
832     // notify size/duration
833     if (length > 0) {
834         if (!priv->haveSize || (static_cast<long long>(priv->size) != length)) {
835             priv->haveSize = TRUE;
836             priv->size = length;
837             gst_app_src_set_size(priv->appsrc, length);
838         }
839     } else {
840         gst_app_src_set_size(priv->appsrc, -1);
841         if (!priv->seekable)
842             gst_app_src_set_stream_type(priv->appsrc, GST_APP_STREAM_TYPE_STREAM);
843     }
844
845     // Signal to downstream if this is an Icecast stream.
846     GRefPtr<GstCaps> caps;
847     String metadataIntervalAsString = response.httpHeaderField(HTTPHeaderName::IcyMetaInt);
848     if (!metadataIntervalAsString.isEmpty()) {
849         bool isMetadataIntervalParsed;
850         int metadataInterval = metadataIntervalAsString.toInt(&isMetadataIntervalParsed);
851         if (isMetadataIntervalParsed && metadataInterval > 0) {
852             caps = adoptGRef(gst_caps_new_simple("application/x-icy", "metadata-interval", G_TYPE_INT, metadataInterval, nullptr));
853
854             String contentType = response.httpHeaderField(HTTPHeaderName::ContentType);
855             GST_DEBUG_OBJECT(src, "Response ContentType: %s", contentType.utf8().data());
856             gst_caps_set_simple(caps.get(), "content-type", G_TYPE_STRING, contentType.utf8().data(), nullptr);
857
858             gst_app_src_set_stream_type(priv->appsrc, GST_APP_STREAM_TYPE_STREAM);
859         }
860     }
861
862     gst_app_src_set_caps(priv->appsrc, caps.get());
863
864     // Emit a GST_EVENT_CUSTOM_DOWNSTREAM_STICKY event and message to let
865     // GStreamer know about the HTTP headers sent and received.
866     GstStructure* httpHeaders = gst_structure_new_empty("http-headers");
867     gst_structure_set(httpHeaders, "uri", G_TYPE_STRING, priv->originalURI.data(),
868         "http-status-code", G_TYPE_UINT, response.httpStatusCode(), nullptr);
869     if (!priv->redirectedURI.isNull())
870         gst_structure_set(httpHeaders, "redirection-uri", G_TYPE_STRING, priv->redirectedURI.data(), nullptr);
871     GUniquePtr<GstStructure> headers(gst_structure_new_empty("request-headers"));
872     for (const auto& header : m_request.httpHeaderFields())
873         gst_structure_set(headers.get(), header.key.utf8().data(), G_TYPE_STRING, header.value.utf8().data(), nullptr);
874     GST_DEBUG_OBJECT(src, "Request headers going downstream: %" GST_PTR_FORMAT, headers.get());
875     gst_structure_set(httpHeaders, "request-headers", GST_TYPE_STRUCTURE, headers.get(), nullptr);
876     headers.reset(gst_structure_new_empty("response-headers"));
877     for (const auto& header : response.httpHeaderFields())
878         gst_structure_set(headers.get(), header.key.utf8().data(), G_TYPE_STRING, header.value.utf8().data(), nullptr);
879     gst_structure_set(httpHeaders, "response-headers", GST_TYPE_STRUCTURE, headers.get(), nullptr);
880     GST_DEBUG_OBJECT(src, "Response headers going downstream: %" GST_PTR_FORMAT, headers.get());
881
882     gst_element_post_message(GST_ELEMENT_CAST(src), gst_message_new_element(GST_OBJECT_CAST(src),
883         gst_structure_copy(httpHeaders)));
884     gst_pad_push_event(GST_BASE_SRC_PAD(priv->appsrc), gst_event_new_custom(GST_EVENT_CUSTOM_DOWNSTREAM_STICKY, httpHeaders));
885     
886     completionHandler(ShouldContinue::Yes);
887 }
888
889 void CachedResourceStreamingClient::dataReceived(PlatformMediaResource&, const char* data, int length)
890 {
891     WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get());
892     WebKitWebSrcPrivate* priv = src->priv;
893
894     if (priv->isSeeking) {
895         GST_DEBUG_OBJECT(src, "Seek in progress, ignoring data");
896         return;
897     }
898
899     if (priv->offset < priv->requestedOffset) {
900         // Range request failed; seeking manually.
901         if (priv->offset + length <= priv->requestedOffset) {
902             // Discard all the buffers coming before the requested seek position.
903             priv->offset += length;
904             return;
905         }
906
907         if (priv->offset + length > priv->requestedOffset) {
908             guint64 offset = priv->requestedOffset - priv->offset;
909             data += offset;
910             length -= offset;
911             priv->offset = priv->requestedOffset;
912         }
913
914         priv->requestedOffset = 0;
915     }
916
917     checkUpdateBlocksize(length);
918
919     uint64_t startingOffset = priv->offset;
920
921     if (priv->requestedOffset == priv->offset)
922         priv->requestedOffset += length;
923     priv->offset += length;
924     // priv->size == 0 if received length on didReceiveResponse < 0.
925     if (priv->size > 0 && priv->offset > priv->size) {
926         GST_DEBUG_OBJECT(src, "Updating internal size from %" G_GUINT64_FORMAT " to %" G_GUINT64_FORMAT, priv->size, priv->offset);
927         gst_app_src_set_size(priv->appsrc, priv->offset);
928         priv->size = priv->offset;
929     }
930
931     // Now split the recv'd buffer into buffers that are of a size basesrc suggests. It is important not
932     // to push buffers that are too large, otherwise incorrect buffering messages can be sent from the
933     // pipeline.
934     uint64_t bufferSize = static_cast<uint64_t>(length);
935     uint64_t blockSize = static_cast<uint64_t>(gst_base_src_get_blocksize(GST_BASE_SRC_CAST(priv->appsrc)));
936     GST_LOG_OBJECT(src, "Splitting the received buffer into %" PRIu64 " blocks", bufferSize / blockSize);
937     for (uint64_t currentOffset = 0; currentOffset < bufferSize; currentOffset += blockSize) {
938         uint64_t subBufferOffset = startingOffset + currentOffset;
939         uint64_t currentOffsetSize = std::min(blockSize, bufferSize - currentOffset);
940
941         GstBuffer* subBuffer = gst_buffer_new_wrapped(g_memdup(data + currentOffset, currentOffsetSize), currentOffsetSize);
942         if (UNLIKELY(!subBuffer)) {
943             GST_ELEMENT_ERROR(src, CORE, FAILED, ("Failed to allocate sub-buffer"), (nullptr));
944             break;
945         }
946
947         GST_TRACE_OBJECT(src, "Sub-buffer bounds: %" PRIu64 " -- %" PRIu64, subBufferOffset, subBufferOffset + currentOffsetSize);
948         GST_BUFFER_OFFSET(subBuffer) = subBufferOffset;
949         GST_BUFFER_OFFSET_END(subBuffer) = subBufferOffset + currentOffsetSize;
950
951         if (priv->isSeeking) {
952             GST_TRACE_OBJECT(src, "Stopping buffer appends due to seek");
953             // A seek has happened in the middle of us breaking the
954             // incoming data up from a previous request. Stop pushing
955             // buffers that are now from the incorrect offset.
956             break;
957         }
958
959         // It may be tempting to use a GstBufferList here, but note
960         // that there is a race condition in GstDownloadBuffer during
961         // seek flushes that can cause decoders to read at incorrect
962         // offsets.
963         GstFlowReturn ret = gst_app_src_push_buffer(priv->appsrc, subBuffer);
964
965         if (UNLIKELY(ret != GST_FLOW_OK && ret != GST_FLOW_EOS && ret != GST_FLOW_FLUSHING)) {
966             GST_ELEMENT_ERROR(src, CORE, FAILED, (nullptr), (nullptr));
967             break;
968         }
969     }
970 }
971
972 void CachedResourceStreamingClient::accessControlCheckFailed(PlatformMediaResource&, const ResourceError& error)
973 {
974     WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get());
975     GST_ELEMENT_ERROR(src, RESOURCE, READ, ("%s", error.localizedDescription().utf8().data()), (nullptr));
976     gst_app_src_end_of_stream(src->priv->appsrc);
977     webKitWebSrcStop(src);
978 }
979
980 void CachedResourceStreamingClient::loadFailed(PlatformMediaResource&, const ResourceError& error)
981 {
982     WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get());
983
984     if (!error.isCancellation()) {
985         GST_ERROR_OBJECT(src, "Have failure: %s", error.localizedDescription().utf8().data());
986         GST_ELEMENT_ERROR(src, RESOURCE, FAILED, ("%s", error.localizedDescription().utf8().data()), (nullptr));
987     }
988
989     gst_app_src_end_of_stream(src->priv->appsrc);
990 }
991
992 void CachedResourceStreamingClient::loadFinished(PlatformMediaResource&)
993 {
994     WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src.get());
995     WebKitWebSrcPrivate* priv = src->priv;
996
997     GST_DEBUG_OBJECT(src, "Have EOS");
998
999     if (!priv->isSeeking)
1000         gst_app_src_end_of_stream(priv->appsrc);
1001 }
1002
1003 bool webKitSrcWouldTaintOrigin(WebKitWebSrc* src, const SecurityOrigin& origin)
1004 {
1005     WebKitWebSrcPrivate* priv = src->priv;
1006
1007     auto* cachedResourceStreamingClient = reinterpret_cast<CachedResourceStreamingClient*>(priv->resource->client());
1008     for (auto& responseOrigin : cachedResourceStreamingClient->securityOrigins()) {
1009         if (!origin.canAccess(*responseOrigin))
1010             return true;
1011     }
1012     return false;
1013 }
1014
1015 #endif // ENABLE(VIDEO) && USE(GSTREAMER)