c3bff9a2ce24adb01b2c8e4aa3c20ca0e81a6059
[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 "GRefPtrGStreamer.h"
26 #include "GStreamerUtilities.h"
27 #include "GUniquePtrGStreamer.h"
28 #include "HTTPHeaderNames.h"
29 #include "MainThreadNotifier.h"
30 #include "MediaPlayer.h"
31 #include "NotImplemented.h"
32 #include "PlatformMediaResourceLoader.h"
33 #include "ResourceError.h"
34 #include "ResourceHandle.h"
35 #include "ResourceHandleClient.h"
36 #include "ResourceRequest.h"
37 #include "ResourceResponse.h"
38 #include "SharedBuffer.h"
39 #include <gst/app/gstappsrc.h>
40 #include <gst/gst.h>
41 #include <gst/pbutils/missing-plugins.h>
42 #include <wtf/MainThread.h>
43 #include <wtf/Noncopyable.h>
44 #include <wtf/glib/GMutexLocker.h>
45 #include <wtf/glib/GRefPtr.h>
46 #include <wtf/glib/GUniquePtr.h>
47 #include <wtf/text/CString.h>
48
49 #if USE(SOUP)
50 #include "SoupNetworkSession.h"
51 #endif
52
53 using namespace WebCore;
54
55 class StreamingClient {
56     public:
57         StreamingClient(WebKitWebSrc*);
58         virtual ~StreamingClient();
59
60     protected:
61         char* createReadBuffer(size_t requestedSize, size_t& actualSize);
62         void handleResponseReceived(const ResourceResponse&);
63         void handleDataReceived(const char*, int);
64         void handleNotifyFinished();
65
66         GstElement* m_src;
67 };
68
69 class CachedResourceStreamingClient final : public PlatformMediaResourceClient, public StreamingClient {
70     WTF_MAKE_NONCOPYABLE(CachedResourceStreamingClient);
71     public:
72         CachedResourceStreamingClient(WebKitWebSrc*);
73         virtual ~CachedResourceStreamingClient();
74
75     private:
76         // PlatformMediaResourceClient virtual methods.
77 #if USE(SOUP)
78         char* getOrCreateReadBuffer(PlatformMediaResource&, size_t requestedSize, size_t& actualSize) override;
79 #endif
80         void responseReceived(PlatformMediaResource&, const ResourceResponse&) override;
81         void dataReceived(PlatformMediaResource&, const char*, int) override;
82         void accessControlCheckFailed(PlatformMediaResource&, const ResourceError&) override;
83         void loadFailed(PlatformMediaResource&, const ResourceError&) override;
84         void loadFinished(PlatformMediaResource&) override;
85 };
86
87 class ResourceHandleStreamingClient : public ResourceHandleClient, public StreamingClient {
88     WTF_MAKE_NONCOPYABLE(ResourceHandleStreamingClient); WTF_MAKE_FAST_ALLOCATED;
89     public:
90         ResourceHandleStreamingClient(WebKitWebSrc*, ResourceRequest&&);
91         virtual ~ResourceHandleStreamingClient();
92
93         // StreamingClient virtual methods.
94         bool loadFailed() const;
95         void setDefersLoading(bool);
96
97     private:
98         // ResourceHandleClient virtual methods.
99 #if USE(SOUP)
100         char* getOrCreateReadBuffer(size_t requestedSize, size_t& actualSize) override;
101 #endif
102         ResourceRequest willSendRequest(ResourceHandle*, ResourceRequest&&, ResourceResponse&&) override;
103         void didReceiveResponse(ResourceHandle*, ResourceResponse&&) override;
104         void didReceiveData(ResourceHandle*, const char*, unsigned, int) override;
105         void didReceiveBuffer(ResourceHandle*, Ref<SharedBuffer>&&, int encodedLength) override;
106         void didFinishLoading(ResourceHandle*) override;
107         void didFail(ResourceHandle*, const ResourceError&) override;
108         void wasBlocked(ResourceHandle*) override;
109         void cannotShowURL(ResourceHandle*) override;
110
111         ThreadIdentifier m_thread { 0 };
112         Lock m_initializeRunLoopConditionMutex;
113         Condition m_initializeRunLoopCondition;
114         RunLoop* m_runLoop { nullptr };
115         Lock m_terminateRunLoopConditionMutex;
116         Condition m_terminateRunLoopCondition;
117         RefPtr<ResourceHandle> m_resource;
118 #if USE(SOUP)
119         std::unique_ptr<SoupNetworkSession> m_session;
120 #endif
121 };
122
123 enum MainThreadSourceNotification {
124     Start = 1 << 0,
125     Stop = 1 << 1,
126     NeedData = 1 << 2,
127     EnoughData = 1 << 3,
128     Seek = 1 << 4
129 };
130
131 #define WEBKIT_WEB_SRC_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), WEBKIT_TYPE_WEB_SRC, WebKitWebSrcPrivate))
132 struct _WebKitWebSrcPrivate {
133     GstAppSrc* appsrc;
134     GstPad* srcpad;
135     CString originalURI;
136     CString resolvedURI;
137     bool keepAlive;
138     GUniquePtr<GstStructure> extraHeaders;
139     bool compress;
140     GUniquePtr<gchar> httpMethod;
141
142     WebCore::MediaPlayer* player;
143
144     RefPtr<PlatformMediaResourceLoader> loader;
145     RefPtr<PlatformMediaResource> resource;
146     std::unique_ptr<ResourceHandleStreamingClient> client;
147
148     bool didPassAccessControlCheck;
149
150     guint64 offset;
151     guint64 size;
152     gboolean seekable;
153     bool paused;
154     bool isSeeking;
155
156     guint64 requestedOffset;
157
158     bool createdInMainThread;
159     MainThreadNotifier<MainThreadSourceNotification> notifier;
160     GRefPtr<GstBuffer> buffer;
161 };
162
163 enum {
164     PROP_0,
165     PROP_LOCATION,
166     PROP_RESOLVED_LOCATION,
167     PROP_KEEP_ALIVE,
168     PROP_EXTRA_HEADERS,
169     PROP_COMPRESS,
170     PROP_METHOD
171 };
172
173 static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE("src",
174                                                                   GST_PAD_SRC,
175                                                                   GST_PAD_ALWAYS,
176                                                                   GST_STATIC_CAPS_ANY);
177
178 GST_DEBUG_CATEGORY_STATIC(webkit_web_src_debug);
179 #define GST_CAT_DEFAULT webkit_web_src_debug
180
181 static void webKitWebSrcUriHandlerInit(gpointer gIface, gpointer ifaceData);
182
183 static void webKitWebSrcDispose(GObject*);
184 static void webKitWebSrcFinalize(GObject*);
185 static void webKitWebSrcSetProperty(GObject*, guint propertyID, const GValue*, GParamSpec*);
186 static void webKitWebSrcGetProperty(GObject*, guint propertyID, GValue*, GParamSpec*);
187 static GstStateChangeReturn webKitWebSrcChangeState(GstElement*, GstStateChange);
188
189 static gboolean webKitWebSrcQueryWithParent(GstPad*, GstObject*, GstQuery*);
190
191 static void webKitWebSrcNeedData(WebKitWebSrc*);
192 static void webKitWebSrcEnoughData(WebKitWebSrc*);
193 static gboolean webKitWebSrcSeek(WebKitWebSrc*, guint64);
194
195 static GstAppSrcCallbacks appsrcCallbacks = {
196     // need_data
197     [](GstAppSrc*, guint, gpointer userData) {
198         webKitWebSrcNeedData(WEBKIT_WEB_SRC(userData));
199     },
200     // enough_data
201     [](GstAppSrc*, gpointer userData) {
202         webKitWebSrcEnoughData(WEBKIT_WEB_SRC(userData));
203     },
204     // seek_data
205     [](GstAppSrc*, guint64 offset, gpointer userData) -> gboolean {
206         return webKitWebSrcSeek(WEBKIT_WEB_SRC(userData), offset);
207     },
208     { nullptr }
209 };
210
211 #define webkit_web_src_parent_class parent_class
212 // We split this out into another macro to avoid a check-webkit-style error.
213 #define WEBKIT_WEB_SRC_CATEGORY_INIT GST_DEBUG_CATEGORY_INIT(webkit_web_src_debug, "webkitwebsrc", 0, "websrc element");
214 G_DEFINE_TYPE_WITH_CODE(WebKitWebSrc, webkit_web_src, GST_TYPE_BIN,
215                          G_IMPLEMENT_INTERFACE(GST_TYPE_URI_HANDLER, webKitWebSrcUriHandlerInit);
216                          WEBKIT_WEB_SRC_CATEGORY_INIT);
217
218 static void webkit_web_src_class_init(WebKitWebSrcClass* klass)
219 {
220     GObjectClass* oklass = G_OBJECT_CLASS(klass);
221     GstElementClass* eklass = GST_ELEMENT_CLASS(klass);
222
223     oklass->dispose = webKitWebSrcDispose;
224     oklass->finalize = webKitWebSrcFinalize;
225     oklass->set_property = webKitWebSrcSetProperty;
226     oklass->get_property = webKitWebSrcGetProperty;
227
228     gst_element_class_add_pad_template(eklass,
229                                        gst_static_pad_template_get(&srcTemplate));
230     gst_element_class_set_metadata(eklass, "WebKit Web source element", "Source", "Handles HTTP/HTTPS uris",
231                                "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
232
233     /* Allows setting the uri using the 'location' property, which is used
234      * for example by gst_element_make_from_uri() */
235     g_object_class_install_property(oklass, PROP_LOCATION,
236         g_param_spec_string("location", "location", "Location to read from",
237             nullptr, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
238
239     g_object_class_install_property(oklass, PROP_RESOLVED_LOCATION,
240         g_param_spec_string("resolved-location", "Resolved location", "The location resolved by the server",
241             nullptr, static_cast<GParamFlags>(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
242
243     g_object_class_install_property(oklass, PROP_KEEP_ALIVE,
244         g_param_spec_boolean("keep-alive", "keep-alive", "Use HTTP persistent connections",
245             FALSE, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
246
247     g_object_class_install_property(oklass, PROP_EXTRA_HEADERS,
248         g_param_spec_boxed("extra-headers", "Extra Headers", "Extra headers to append to the HTTP request",
249             GST_TYPE_STRUCTURE, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
250
251     g_object_class_install_property(oklass, PROP_COMPRESS,
252         g_param_spec_boolean("compress", "Compress", "Allow compressed content encodings",
253             FALSE, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
254
255     g_object_class_install_property(oklass, PROP_METHOD,
256         g_param_spec_string("method", "method", "The HTTP method to use (default: GET)",
257             nullptr, static_cast<GParamFlags>(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
258
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->createdInMainThread = isMainThread();
272
273     priv->appsrc = GST_APP_SRC(gst_element_factory_make("appsrc", 0));
274     if (!priv->appsrc) {
275         GST_ERROR_OBJECT(src, "Failed to create appsrc");
276         return;
277     }
278
279     gst_bin_add(GST_BIN(src), GST_ELEMENT(priv->appsrc));
280
281
282     GRefPtr<GstPad> targetPad = adoptGRef(gst_element_get_static_pad(GST_ELEMENT(priv->appsrc), "src"));
283     priv->srcpad = webkitGstGhostPadFromStaticTemplate(&srcTemplate, "src", targetPad.get());
284
285     gst_element_add_pad(GST_ELEMENT(src), priv->srcpad);
286
287     GST_OBJECT_FLAG_SET(priv->srcpad, GST_PAD_FLAG_NEED_PARENT);
288     gst_pad_set_query_function(priv->srcpad, webKitWebSrcQueryWithParent);
289
290     gst_app_src_set_callbacks(priv->appsrc, &appsrcCallbacks, src, 0);
291     gst_app_src_set_emit_signals(priv->appsrc, FALSE);
292     gst_app_src_set_stream_type(priv->appsrc, GST_APP_STREAM_TYPE_SEEKABLE);
293
294     // 512k is a abitrary number but we should choose a value
295     // here to not pause/unpause the SoupMessage too often and
296     // to make sure there's always some data available for
297     // GStreamer to handle.
298     gst_app_src_set_max_bytes(priv->appsrc, 512 * 1024);
299
300     // Emit the need-data signal if the queue contains less
301     // than 20% of data. Without this the need-data signal
302     // is emitted when the queue is empty, we then dispatch
303     // the soup message unpausing to the main loop and from
304     // there unpause the soup message. This already takes
305     // quite some time and libsoup even needs some more time
306     // to actually provide data again. If we do all this
307     // already if the queue is 20% empty, it's much more
308     // likely that libsoup already provides new data before
309     // the queue is really empty.
310     // This might need tweaking for ports not using libsoup.
311     g_object_set(priv->appsrc, "min-percent", 20, NULL);
312
313     gst_app_src_set_caps(priv->appsrc, 0);
314     gst_app_src_set_size(priv->appsrc, -1);
315 }
316
317 static void webKitWebSrcDispose(GObject* object)
318 {
319     WebKitWebSrc* src = WEBKIT_WEB_SRC(object);
320     WebKitWebSrcPrivate* priv = src->priv;
321
322     priv->player = 0;
323
324     GST_CALL_PARENT(G_OBJECT_CLASS, dispose, (object));
325 }
326
327 static void webKitWebSrcFinalize(GObject* object)
328 {
329     WebKitWebSrcPrivate* priv = WEBKIT_WEB_SRC(object)->priv;
330
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     case PROP_KEEP_ALIVE:
345         src->priv->keepAlive = g_value_get_boolean(value);
346         break;
347     case PROP_EXTRA_HEADERS: {
348         const GstStructure* s = gst_value_get_structure(value);
349         src->priv->extraHeaders.reset(s ? gst_structure_copy(s) : nullptr);
350         break;
351     }
352     case PROP_COMPRESS:
353         src->priv->compress = g_value_get_boolean(value);
354         break;
355     case PROP_METHOD:
356         src->priv->httpMethod.reset(g_value_dup_string(value));
357         break;
358     default:
359         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, pspec);
360         break;
361     }
362 }
363
364 static void webKitWebSrcGetProperty(GObject* object, guint propID, GValue* value, GParamSpec* pspec)
365 {
366     WebKitWebSrc* src = WEBKIT_WEB_SRC(object);
367     WebKitWebSrcPrivate* priv = src->priv;
368
369     WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
370     switch (propID) {
371     case PROP_LOCATION:
372         g_value_set_string(value, priv->originalURI.data());
373         break;
374     case PROP_RESOLVED_LOCATION:
375         g_value_set_string(value, priv->resolvedURI.data());
376         break;
377     case PROP_KEEP_ALIVE:
378         g_value_set_boolean(value, priv->keepAlive);
379         break;
380     case PROP_EXTRA_HEADERS:
381         gst_value_set_structure(value, priv->extraHeaders.get());
382         break;
383     case PROP_COMPRESS:
384         g_value_set_boolean(value, priv->compress);
385         break;
386     case PROP_METHOD:
387         g_value_set_string(value, priv->httpMethod.get());
388         break;
389     default:
390         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, pspec);
391         break;
392     }
393 }
394
395 static void webKitWebSrcStop(WebKitWebSrc* src)
396 {
397     WebKitWebSrcPrivate* priv = src->priv;
398
399     if (priv->resource || (priv->loader && !priv->keepAlive)) {
400         GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src);
401         priv->notifier.cancelPendingNotifications(MainThreadSourceNotification::NeedData | MainThreadSourceNotification::EnoughData | MainThreadSourceNotification::Seek);
402         priv->notifier.notify(MainThreadSourceNotification::Stop, [protector, keepAlive = priv->keepAlive] {
403             WebKitWebSrcPrivate* priv = protector->priv;
404
405             WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(protector.get()));
406             if (priv->resource) {
407                 priv->resource->stop();
408                 priv->resource->setClient(nullptr);
409                 priv->resource = nullptr;
410             }
411
412             if (!keepAlive)
413                 priv->loader = nullptr;
414         });
415     }
416
417     WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
418
419     bool wasSeeking = std::exchange(priv->isSeeking, false);
420
421     priv->client = nullptr;
422
423     if (priv->buffer) {
424         unmapGstBuffer(priv->buffer.get());
425         priv->buffer.clear();
426     }
427
428     priv->paused = false;
429
430     priv->offset = 0;
431     priv->seekable = FALSE;
432
433     if (!wasSeeking) {
434         priv->size = 0;
435         priv->requestedOffset = 0;
436         priv->player = 0;
437     }
438
439     locker.unlock();
440
441     if (priv->appsrc) {
442         gst_app_src_set_caps(priv->appsrc, 0);
443         if (!wasSeeking)
444             gst_app_src_set_size(priv->appsrc, -1);
445     }
446
447     GST_DEBUG_OBJECT(src, "Stopped request");
448 }
449
450 static bool webKitWebSrcSetExtraHeader(GQuark fieldId, const GValue* value, gpointer userData)
451 {
452     GUniquePtr<gchar> fieldContent;
453
454     if (G_VALUE_HOLDS_STRING(value))
455         fieldContent.reset(g_value_dup_string(value));
456     else {
457         GValue dest = G_VALUE_INIT;
458
459         g_value_init(&dest, G_TYPE_STRING);
460         if (g_value_transform(value, &dest))
461             fieldContent.reset(g_value_dup_string(&dest));
462     }
463
464     const gchar* fieldName = g_quark_to_string(fieldId);
465     if (!fieldContent.get()) {
466         GST_ERROR("extra-headers field '%s' contains no value or can't be converted to a string", fieldName);
467         return false;
468     }
469
470     GST_DEBUG("Appending extra header: \"%s: %s\"", fieldName, fieldContent.get());
471     ResourceRequest* request = static_cast<ResourceRequest*>(userData);
472     request->setHTTPHeaderField(fieldName, fieldContent.get());
473     return true;
474 }
475
476 static gboolean webKitWebSrcProcessExtraHeaders(GQuark fieldId, const GValue* value, gpointer userData)
477 {
478     if (G_VALUE_TYPE(value) == GST_TYPE_ARRAY) {
479         unsigned size = gst_value_array_get_size(value);
480
481         for (unsigned i = 0; i < size; i++) {
482             if (!webKitWebSrcSetExtraHeader(fieldId, gst_value_array_get_value(value, i), userData))
483                 return FALSE;
484         }
485         return TRUE;
486     }
487
488     if (G_VALUE_TYPE(value) == GST_TYPE_LIST) {
489         unsigned size = gst_value_list_get_size(value);
490
491         for (unsigned i = 0; i < size; i++) {
492             if (!webKitWebSrcSetExtraHeader(fieldId, gst_value_list_get_value(value, i), userData))
493                 return FALSE;
494         }
495         return TRUE;
496     }
497
498     return webKitWebSrcSetExtraHeader(fieldId, value, userData);
499 }
500
501 static void webKitWebSrcStart(WebKitWebSrc* src)
502 {
503     WebKitWebSrcPrivate* priv = src->priv;
504
505     WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
506
507     priv->didPassAccessControlCheck = false;
508
509     if (priv->originalURI.isNull()) {
510         GST_ERROR_OBJECT(src, "No URI provided");
511         locker.unlock();
512         webKitWebSrcStop(src);
513         return;
514     }
515
516     ASSERT(!priv->client);
517
518     GST_DEBUG_OBJECT(src, "Fetching %s", priv->originalURI.data());
519     URL url = URL(URL(), priv->originalURI.data());
520
521     ResourceRequest request(url);
522     request.setAllowCookies(true);
523     request.setFirstPartyForCookies(url);
524
525     priv->size = 0;
526
527     if (priv->player)
528         request.setHTTPReferrer(priv->player->referrer());
529
530     if (priv->httpMethod.get())
531         request.setHTTPMethod(priv->httpMethod.get());
532
533 #if USE(SOUP)
534     // By default, HTTP Accept-Encoding is disabled here as we don't
535     // want the received response to be encoded in any way as we need
536     // to rely on the proper size of the returned data on
537     // didReceiveResponse.
538     // If Accept-Encoding is used, the server may send the data in encoded format and
539     // request.expectedContentLength() will have the "wrong" size (the size of the
540     // compressed data), even though the data received in didReceiveData is uncompressed.
541     // This is however useful to enable for adaptive streaming
542     // scenarios, when the demuxer needs to download playlists.
543     if (!priv->compress)
544         request.setAcceptEncoding(false);
545 #endif
546
547     // Let Apple web servers know we want to access their nice movie trailers.
548     if (!g_ascii_strcasecmp("movies.apple.com", url.host().utf8().data())
549         || !g_ascii_strcasecmp("trailers.apple.com", url.host().utf8().data()))
550         request.setHTTPUserAgent("Quicktime/7.6.6");
551
552     if (priv->requestedOffset) {
553         GUniquePtr<gchar> val(g_strdup_printf("bytes=%" G_GUINT64_FORMAT "-", priv->requestedOffset));
554         request.setHTTPHeaderField(HTTPHeaderName::Range, val.get());
555     }
556     priv->offset = priv->requestedOffset;
557
558     if (!priv->keepAlive) {
559         GST_DEBUG_OBJECT(src, "Persistent connection support disabled");
560         request.setHTTPHeaderField(HTTPHeaderName::Connection, "close");
561     }
562
563     if (priv->extraHeaders)
564         gst_structure_foreach(priv->extraHeaders.get(), webKitWebSrcProcessExtraHeaders, &request);
565
566     // We always request Icecast/Shoutcast metadata, just in case ...
567     request.setHTTPHeaderField(HTTPHeaderName::IcyMetadata, "1");
568
569     if (!priv->player || !priv->createdInMainThread) {
570         priv->client = std::make_unique<ResourceHandleStreamingClient>(src, WTFMove(request));
571         if (priv->client->loadFailed()) {
572             GST_ERROR_OBJECT(src, "Failed to setup streaming client");
573             priv->client = nullptr;
574             locker.unlock();
575             webKitWebSrcStop(src);
576         } else
577             GST_DEBUG_OBJECT(src, "Started request");
578         return;
579     }
580
581     locker.unlock();
582     GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src);
583     priv->notifier.notify(MainThreadSourceNotification::Start, [protector, request = WTFMove(request)] {
584         WebKitWebSrcPrivate* priv = protector->priv;
585
586         WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(protector.get()));
587         if (!priv->loader)
588             priv->loader = priv->player->createResourceLoader();
589
590         PlatformMediaResourceLoader::LoadOptions loadOptions = 0;
591         if (request.url().protocolIsBlob())
592             loadOptions |= PlatformMediaResourceLoader::LoadOption::BufferData;
593         // FIXME: request should be moved for efficiency
594         priv->resource = priv->loader->requestResource(ResourceRequest(request), loadOptions);
595         if (priv->resource) {
596             priv->resource->setClient(std::make_unique<CachedResourceStreamingClient>(protector.get()));
597             GST_DEBUG_OBJECT(protector.get(), "Started request");
598         } else {
599             GST_ERROR_OBJECT(protector.get(), "Failed to setup streaming client");
600             priv->loader = nullptr;
601             locker.unlock();
602             webKitWebSrcStop(protector.get());
603         }
604     });
605 }
606
607 static GstStateChangeReturn webKitWebSrcChangeState(GstElement* element, GstStateChange transition)
608 {
609     GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
610     WebKitWebSrc* src = WEBKIT_WEB_SRC(element);
611     WebKitWebSrcPrivate* priv = src->priv;
612
613     switch (transition) {
614     case GST_STATE_CHANGE_NULL_TO_READY:
615         if (!priv->appsrc) {
616             gst_element_post_message(element,
617                                      gst_missing_element_message_new(element, "appsrc"));
618             GST_ELEMENT_ERROR(src, CORE, MISSING_PLUGIN, (0), ("no appsrc"));
619             return GST_STATE_CHANGE_FAILURE;
620         }
621         break;
622     default:
623         break;
624     }
625
626     ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
627     if (G_UNLIKELY(ret == GST_STATE_CHANGE_FAILURE)) {
628         GST_DEBUG_OBJECT(src, "State change failed");
629         return ret;
630     }
631
632     switch (transition) {
633     case GST_STATE_CHANGE_READY_TO_PAUSED:
634     {
635         GST_DEBUG_OBJECT(src, "READY->PAUSED");
636         webKitWebSrcStart(src);
637         break;
638     }
639     case GST_STATE_CHANGE_PAUSED_TO_READY:
640     {
641         GST_DEBUG_OBJECT(src, "PAUSED->READY");
642         webKitWebSrcStop(src);
643         break;
644     }
645     default:
646         break;
647     }
648
649     return ret;
650 }
651
652 static gboolean webKitWebSrcQueryWithParent(GstPad* pad, GstObject* parent, GstQuery* query)
653 {
654     WebKitWebSrc* src = WEBKIT_WEB_SRC(GST_ELEMENT(parent));
655     gboolean result = FALSE;
656
657     switch (GST_QUERY_TYPE(query)) {
658     case GST_QUERY_DURATION: {
659         GstFormat format;
660
661         gst_query_parse_duration(query, &format, NULL);
662
663         GST_DEBUG_OBJECT(src, "duration query in format %s", gst_format_get_name(format));
664         WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
665         if (format == GST_FORMAT_BYTES && src->priv->size > 0) {
666             gst_query_set_duration(query, format, src->priv->size);
667             result = TRUE;
668         }
669         break;
670     }
671     case GST_QUERY_URI: {
672         WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
673         gst_query_set_uri(query, src->priv->originalURI.data());
674         result = TRUE;
675         break;
676     }
677     case GST_QUERY_SCHEDULING: {
678         GstSchedulingFlags flags;
679         int minSize, maxSize, align;
680
681         gst_query_parse_scheduling(query, &flags, &minSize, &maxSize, &align);
682         gst_query_set_scheduling(query, static_cast<GstSchedulingFlags>(flags | GST_SCHEDULING_FLAG_BANDWIDTH_LIMITED), minSize, maxSize, align);
683         result = TRUE;
684         break;
685     }
686     default: {
687         GRefPtr<GstPad> target = adoptGRef(gst_ghost_pad_get_target(GST_GHOST_PAD_CAST(pad)));
688
689         // Forward the query to the proxy target pad.
690         if (target)
691             result = gst_pad_query(target.get(), query);
692         break;
693     }
694     }
695
696     return result;
697 }
698
699 static bool urlHasSupportedProtocol(const URL& url)
700 {
701     return url.isValid() && (url.protocolIsInHTTPFamily() || url.protocolIsBlob());
702 }
703
704 // uri handler interface
705
706 static GstURIType webKitWebSrcUriGetType(GType)
707 {
708     return GST_URI_SRC;
709 }
710
711 const gchar* const* webKitWebSrcGetProtocols(GType)
712 {
713     static const char* protocols[] = {"http", "https", "blob", 0 };
714     return protocols;
715 }
716
717 static gchar* webKitWebSrcGetUri(GstURIHandler* handler)
718 {
719     WebKitWebSrc* src = WEBKIT_WEB_SRC(handler);
720     gchar* ret;
721
722     WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
723     ret = g_strdup(src->priv->originalURI.data());
724     return ret;
725 }
726
727 static gboolean webKitWebSrcSetUri(GstURIHandler* handler, const gchar* uri, GError** error)
728 {
729     WebKitWebSrc* src = WEBKIT_WEB_SRC(handler);
730     WebKitWebSrcPrivate* priv = src->priv;
731
732     if (GST_STATE(src) >= GST_STATE_PAUSED) {
733         GST_ERROR_OBJECT(src, "URI can only be set in states < PAUSED");
734         return FALSE;
735     }
736
737     WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
738
739     priv->originalURI = CString();
740     if (!uri)
741         return TRUE;
742
743     URL url(URL(), uri);
744     if (!urlHasSupportedProtocol(url)) {
745         g_set_error(error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI, "Invalid URI '%s'", uri);
746         return FALSE;
747     }
748
749     priv->originalURI = url.string().utf8();
750     return TRUE;
751 }
752
753 static void webKitWebSrcUriHandlerInit(gpointer gIface, gpointer)
754 {
755     GstURIHandlerInterface* iface = (GstURIHandlerInterface *) gIface;
756
757     iface->get_type = webKitWebSrcUriGetType;
758     iface->get_protocols = webKitWebSrcGetProtocols;
759     iface->get_uri = webKitWebSrcGetUri;
760     iface->set_uri = webKitWebSrcSetUri;
761 }
762
763 static void webKitWebSrcNeedData(WebKitWebSrc* src)
764 {
765     WebKitWebSrcPrivate* priv = src->priv;
766
767     GST_DEBUG_OBJECT(src, "Need more data");
768
769     {
770         WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
771         if (!priv->paused)
772             return;
773         priv->paused = false;
774         if (priv->client) {
775             priv->client->setDefersLoading(false);
776             return;
777         }
778     }
779
780     GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src);
781     priv->notifier.notify(MainThreadSourceNotification::NeedData, [protector] {
782         WebKitWebSrcPrivate* priv = protector->priv;
783         if (priv->resource)
784             priv->resource->setDefersLoading(false);
785     });
786 }
787
788 static void webKitWebSrcEnoughData(WebKitWebSrc* src)
789 {
790     WebKitWebSrcPrivate* priv = src->priv;
791
792     GST_DEBUG_OBJECT(src, "Have enough data");
793
794     {
795         WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
796         if (priv->paused)
797             return;
798         priv->paused = true;
799         if (priv->client) {
800             priv->client->setDefersLoading(true);
801             return;
802         }
803     }
804
805     GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src);
806     priv->notifier.notify(MainThreadSourceNotification::EnoughData, [protector] {
807         WebKitWebSrcPrivate* priv = protector->priv;
808         if (priv->resource)
809             priv->resource->setDefersLoading(true);
810     });
811 }
812
813 static gboolean webKitWebSrcSeek(WebKitWebSrc* src, guint64 offset)
814 {
815     WebKitWebSrcPrivate* priv = src->priv;
816
817     {
818         WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
819         if (offset == priv->offset && priv->requestedOffset == priv->offset)
820             return TRUE;
821
822         if (!priv->seekable)
823             return FALSE;
824
825         priv->isSeeking = true;
826         priv->requestedOffset = offset;
827     }
828
829     GST_DEBUG_OBJECT(src, "Seeking to offset: %" G_GUINT64_FORMAT, src->priv->requestedOffset);
830     if (priv->client) {
831         webKitWebSrcStop(src);
832         webKitWebSrcStart(src);
833         return TRUE;
834     }
835
836     GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src);
837     priv->notifier.notify(MainThreadSourceNotification::Seek, [protector] {
838         webKitWebSrcStop(protector.get());
839         webKitWebSrcStart(protector.get());
840     });
841     return TRUE;
842 }
843
844 void webKitWebSrcSetMediaPlayer(WebKitWebSrc* src, WebCore::MediaPlayer* player)
845 {
846     ASSERT(player);
847     ASSERT(src->priv->createdInMainThread);
848     WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
849     src->priv->player = player;
850 }
851
852 bool webKitSrcPassedCORSAccessCheck(WebKitWebSrc* src)
853 {
854     return src->priv->didPassAccessControlCheck;
855 }
856
857 StreamingClient::StreamingClient(WebKitWebSrc* src)
858     : m_src(static_cast<GstElement*>(gst_object_ref(src)))
859 {
860 }
861
862 StreamingClient::~StreamingClient()
863 {
864     gst_object_unref(m_src);
865 }
866
867 char* StreamingClient::createReadBuffer(size_t requestedSize, size_t& actualSize)
868 {
869     WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
870     WebKitWebSrcPrivate* priv = src->priv;
871
872     ASSERT(!priv->buffer);
873
874     GstBuffer* buffer = gst_buffer_new_and_alloc(requestedSize);
875
876     mapGstBuffer(buffer, GST_MAP_WRITE);
877
878     WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
879     priv->buffer = adoptGRef(buffer);
880     locker.unlock();
881
882     actualSize = gst_buffer_get_size(buffer);
883     return getGstBufferDataPointer(buffer);
884 }
885
886 void StreamingClient::handleResponseReceived(const ResourceResponse& response)
887 {
888     WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
889     WebKitWebSrcPrivate* priv = src->priv;
890
891     GST_DEBUG_OBJECT(src, "Received response: %d", response.httpStatusCode());
892
893     priv->resolvedURI = response.url().string().utf8();
894
895     if (response.httpStatusCode() >= 400) {
896         GST_ELEMENT_ERROR(src, RESOURCE, READ, ("Received %d HTTP error code", response.httpStatusCode()), (nullptr));
897         gst_app_src_end_of_stream(priv->appsrc);
898         webKitWebSrcStop(src);
899         return;
900     }
901
902     WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
903
904     if (priv->isSeeking) {
905         GST_DEBUG_OBJECT(src, "Seek in progress, ignoring response");
906         return;
907     }
908
909     if (priv->requestedOffset) {
910         // Seeking ... we expect a 206 == PARTIAL_CONTENT
911         if (response.httpStatusCode() == 200) {
912             // Range request didn't have a ranged response; resetting offset.
913             priv->offset = 0;
914         } else if (response.httpStatusCode() != 206) {
915             // Range request completely failed.
916             locker.unlock();
917             GST_ELEMENT_ERROR(src, RESOURCE, READ, ("Received unexpected %d HTTP status code", response.httpStatusCode()), (nullptr));
918             gst_app_src_end_of_stream(priv->appsrc);
919             webKitWebSrcStop(src);
920             return;
921         }
922     }
923
924     long long length = response.expectedContentLength();
925     if (length > 0 && priv->requestedOffset && response.httpStatusCode() == 206)
926         length += priv->requestedOffset;
927
928     priv->size = length >= 0 ? length : 0;
929     priv->seekable = length > 0 && g_ascii_strcasecmp("none", response.httpHeaderField(HTTPHeaderName::AcceptRanges).utf8().data());
930
931     locker.unlock();
932
933     // notify size/duration
934     if (length > 0) {
935         gst_app_src_set_size(priv->appsrc, length);
936     } else
937         gst_app_src_set_size(priv->appsrc, -1);
938
939     gst_app_src_set_caps(priv->appsrc, 0);
940 }
941
942 void StreamingClient::handleDataReceived(const char* data, int length)
943 {
944     WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
945     WebKitWebSrcPrivate* priv = src->priv;
946
947     WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
948
949     GST_LOG_OBJECT(src, "Have %lld bytes of data", priv->buffer ? static_cast<long long>(gst_buffer_get_size(priv->buffer.get())) : length);
950
951     ASSERT(!priv->buffer || data == getGstBufferDataPointer(priv->buffer.get()));
952
953     if (priv->buffer)
954         unmapGstBuffer(priv->buffer.get());
955
956     if (priv->isSeeking) {
957         GST_DEBUG_OBJECT(src, "Seek in progress, ignoring data");
958         priv->buffer.clear();
959         return;
960     }
961
962     if (priv->offset < priv->requestedOffset) {
963         // Range request failed; seeking manually.
964         if (priv->offset + length <= priv->requestedOffset) {
965             // Discard all the buffers coming before the requested seek position.
966             priv->offset += length;
967             priv->buffer.clear();
968             return;
969         }
970
971         if (priv->offset + length > priv->requestedOffset) {
972             guint64 offset = priv->requestedOffset - priv->offset;
973             data += offset;
974             length -= offset;
975             if (priv->buffer)
976                 gst_buffer_resize(priv->buffer.get(), offset, -1);
977             priv->offset = priv->requestedOffset;
978         }
979
980         priv->requestedOffset = 0;
981     }
982
983     // Ports using the GStreamer backend but not the soup implementation of ResourceHandle
984     // won't be using buffers provided by this client, the buffer is created here in that case.
985     if (!priv->buffer)
986         priv->buffer = adoptGRef(createGstBufferForData(data, length));
987     else
988         gst_buffer_set_size(priv->buffer.get(), static_cast<gssize>(length));
989
990     GST_BUFFER_OFFSET(priv->buffer.get()) = priv->offset;
991     if (priv->requestedOffset == priv->offset)
992         priv->requestedOffset += length;
993     priv->offset += length;
994     // priv->size == 0 if received length on didReceiveResponse < 0.
995     if (priv->size > 0 && priv->offset > priv->size) {
996         GST_DEBUG_OBJECT(src, "Updating internal size from %" G_GUINT64_FORMAT " to %" G_GUINT64_FORMAT, priv->size, priv->offset);
997         gst_app_src_set_size(priv->appsrc, priv->offset);
998         priv->size = priv->offset;
999     }
1000     GST_BUFFER_OFFSET_END(priv->buffer.get()) = priv->offset;
1001
1002     locker.unlock();
1003
1004     GstFlowReturn ret = gst_app_src_push_buffer(priv->appsrc, priv->buffer.leakRef());
1005     if (ret != GST_FLOW_OK && ret != GST_FLOW_EOS)
1006         GST_ELEMENT_ERROR(src, CORE, FAILED, (0), (0));
1007 }
1008
1009 void StreamingClient::handleNotifyFinished()
1010 {
1011     WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
1012     WebKitWebSrcPrivate* priv = src->priv;
1013
1014     GST_DEBUG_OBJECT(src, "Have EOS");
1015
1016     WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
1017     if (!priv->isSeeking) {
1018         locker.unlock();
1019         gst_app_src_end_of_stream(priv->appsrc);
1020     }
1021 }
1022
1023 CachedResourceStreamingClient::CachedResourceStreamingClient(WebKitWebSrc* src)
1024     : StreamingClient(src)
1025 {
1026 }
1027
1028 CachedResourceStreamingClient::~CachedResourceStreamingClient()
1029 {
1030 }
1031
1032 #if USE(SOUP)
1033 char* CachedResourceStreamingClient::getOrCreateReadBuffer(PlatformMediaResource&, size_t requestedSize, size_t& actualSize)
1034 {
1035     return createReadBuffer(requestedSize, actualSize);
1036 }
1037 #endif
1038
1039 void CachedResourceStreamingClient::responseReceived(PlatformMediaResource&, const ResourceResponse& response)
1040 {
1041     WebKitWebSrcPrivate* priv = WEBKIT_WEB_SRC(m_src)->priv;
1042     priv->didPassAccessControlCheck = priv->resource->didPassAccessControlCheck();
1043     handleResponseReceived(response);
1044 }
1045
1046 void CachedResourceStreamingClient::dataReceived(PlatformMediaResource&, const char* data, int length)
1047 {
1048     handleDataReceived(data, length);
1049 }
1050
1051 void CachedResourceStreamingClient::accessControlCheckFailed(PlatformMediaResource&, const ResourceError& error)
1052 {
1053     WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
1054     GST_ELEMENT_ERROR(src, RESOURCE, READ, ("%s", error.localizedDescription().utf8().data()), (nullptr));
1055     gst_app_src_end_of_stream(src->priv->appsrc);
1056     webKitWebSrcStop(src);
1057 }
1058
1059 void CachedResourceStreamingClient::loadFailed(PlatformMediaResource&, const ResourceError& error)
1060 {
1061     WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
1062
1063     if (!error.isCancellation()) {
1064         GST_ERROR_OBJECT(src, "Have failure: %s", error.localizedDescription().utf8().data());
1065         GST_ELEMENT_ERROR(src, RESOURCE, FAILED, ("%s", error.localizedDescription().utf8().data()), (nullptr));
1066     }
1067
1068     gst_app_src_end_of_stream(src->priv->appsrc);
1069 }
1070
1071 void CachedResourceStreamingClient::loadFinished(PlatformMediaResource&)
1072 {
1073     handleNotifyFinished();
1074 }
1075
1076 ResourceHandleStreamingClient::ResourceHandleStreamingClient(WebKitWebSrc* src, ResourceRequest&& request)
1077     : StreamingClient(src)
1078 {
1079     LockHolder locker(m_initializeRunLoopConditionMutex);
1080     m_thread = createThread("ResourceHandleStreamingClient", [this, request = WTFMove(request)] {
1081         {
1082             LockHolder locker(m_initializeRunLoopConditionMutex);
1083             m_runLoop = &RunLoop::current();
1084 #if USE(SOUP)
1085             m_session = std::make_unique<SoupNetworkSession>();
1086             m_resource = ResourceHandle::create(*m_session, request, this, true, false);
1087 #else
1088             // FIXME: This create will hit an assert in debug builds. See https://bugs.webkit.org/show_bug.cgi?id=167003.
1089             m_resource = ResourceHandle::create(nullptr /*context*/, request, this, true, false);
1090 #endif
1091             m_initializeRunLoopCondition.notifyOne();
1092         }
1093         if (!m_resource)
1094             return;
1095
1096         m_runLoop->dispatch([this] { m_resource->setDefersLoading(false); });
1097         m_runLoop->run();
1098         {
1099             LockHolder locker(m_terminateRunLoopConditionMutex);
1100             m_runLoop = nullptr;
1101             m_resource->clearClient();
1102             m_resource->cancel();
1103             m_resource = nullptr;
1104 #if USE(SOUP)
1105             m_session = nullptr;
1106 #endif
1107             m_terminateRunLoopCondition.notifyOne();
1108         }
1109     });
1110     m_initializeRunLoopCondition.wait(m_initializeRunLoopConditionMutex);
1111 }
1112
1113 ResourceHandleStreamingClient::~ResourceHandleStreamingClient()
1114 {
1115     if (m_thread) {
1116         detachThread(m_thread);
1117         m_thread = 0;
1118     }
1119
1120     if (m_runLoop == &RunLoop::current())
1121         m_runLoop->stop();
1122     else {
1123         LockHolder locker(m_terminateRunLoopConditionMutex);
1124         m_runLoop->stop();
1125         m_terminateRunLoopCondition.wait(m_terminateRunLoopConditionMutex);
1126     }
1127 }
1128
1129 bool ResourceHandleStreamingClient::loadFailed() const
1130 {
1131     return !m_resource;
1132 }
1133
1134 void ResourceHandleStreamingClient::setDefersLoading(bool defers)
1135 {
1136     m_runLoop->dispatch([this, defers] {
1137         if (m_resource)
1138             m_resource->setDefersLoading(defers);
1139     });
1140 }
1141
1142 #if USE(SOUP)
1143 char* ResourceHandleStreamingClient::getOrCreateReadBuffer(size_t requestedSize, size_t& actualSize)
1144 {
1145     return createReadBuffer(requestedSize, actualSize);
1146 }
1147 #endif
1148
1149 ResourceRequest ResourceHandleStreamingClient::willSendRequest(ResourceHandle*, ResourceRequest&& request, ResourceResponse&&)
1150 {
1151     return WTFMove(request);
1152 }
1153
1154 void ResourceHandleStreamingClient::didReceiveResponse(ResourceHandle*, ResourceResponse&& response)
1155 {
1156     handleResponseReceived(response);
1157 }
1158
1159 void ResourceHandleStreamingClient::didReceiveData(ResourceHandle*, const char* /* data */, unsigned /* length */, int)
1160 {
1161     ASSERT_NOT_REACHED();
1162 }
1163
1164 void ResourceHandleStreamingClient::didReceiveBuffer(ResourceHandle*, Ref<SharedBuffer>&& buffer, int /* encodedLength */)
1165 {
1166     // This pattern is suggested by SharedBuffer.h.
1167     const char* segment;
1168     unsigned position = 0;
1169     while (unsigned length = buffer->getSomeData(segment, position)) {
1170         handleDataReceived(segment, length);
1171         position += length;
1172     }
1173 }
1174
1175 void ResourceHandleStreamingClient::didFinishLoading(ResourceHandle*)
1176 {
1177     handleNotifyFinished();
1178 }
1179
1180 void ResourceHandleStreamingClient::didFail(ResourceHandle*, const ResourceError& error)
1181 {
1182     WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
1183
1184     GST_ERROR_OBJECT(src, "Have failure: %s", error.localizedDescription().utf8().data());
1185     GST_ELEMENT_ERROR(src, RESOURCE, FAILED, ("%s", error.localizedDescription().utf8().data()), (0));
1186     gst_app_src_end_of_stream(src->priv->appsrc);
1187 }
1188
1189 void ResourceHandleStreamingClient::wasBlocked(ResourceHandle*)
1190 {
1191     WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
1192     GUniquePtr<gchar> uri;
1193
1194     GST_ERROR_OBJECT(src, "Request was blocked");
1195
1196     WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
1197     uri.reset(g_strdup(src->priv->originalURI.data()));
1198     locker.unlock();
1199
1200     GST_ELEMENT_ERROR(src, RESOURCE, OPEN_READ, ("Access to \"%s\" was blocked", uri.get()), (0));
1201 }
1202
1203 void ResourceHandleStreamingClient::cannotShowURL(ResourceHandle*)
1204 {
1205     WebKitWebSrc* src = WEBKIT_WEB_SRC(m_src);
1206     GUniquePtr<gchar> uri;
1207
1208     GST_ERROR_OBJECT(src, "Cannot show URL");
1209
1210     WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src));
1211     uri.reset(g_strdup(src->priv->originalURI.data()));
1212     locker.unlock();
1213
1214     GST_ELEMENT_ERROR(src, RESOURCE, OPEN_READ, ("Can't show \"%s\"", uri.get()), (0));
1215 }
1216
1217 #endif // USE(GSTREAMER)
1218