4427940ac0bafff30eec238983e95f2736c9077d
[WebKit-https.git] / Source / WebKit / UIProcess / API / glib / WebKitURISchemeRequest.cpp
1 /*
2  * Copyright (C) 2012 Igalia S.L.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #include "config.h"
21 #include "WebKitURISchemeRequest.h"
22
23 #include "APIData.h"
24 #include "WebKitPrivate.h"
25 #include "WebKitURISchemeRequestPrivate.h"
26 #include "WebKitWebContextPrivate.h"
27 #include "WebKitWebView.h"
28 #include "WebPageProxy.h"
29 #include <WebCore/GUniquePtrSoup.h>
30 #include <WebCore/ResourceError.h>
31 #include <WebCore/URLSoup.h>
32 #include <libsoup/soup.h>
33 #include <wtf/glib/GRefPtr.h>
34 #include <wtf/glib/RunLoopSourcePriority.h>
35 #include <wtf/glib/WTFGType.h>
36 #include <wtf/text/CString.h>
37
38 using namespace WebKit;
39 using namespace WebCore;
40
41 /**
42  * SECTION: WebKitURISchemeRequest
43  * @Short_description: Represents a URI scheme request
44  * @Title: WebKitURISchemeRequest
45  *
46  * If you register a particular URI scheme in a #WebKitWebContext,
47  * using webkit_web_context_register_uri_scheme(), you have to provide
48  * a #WebKitURISchemeRequestCallback. After that, when a URI request
49  * is made with that particular scheme, your callback will be
50  * called. There you will be able to access properties such as the
51  * scheme, the URI and path, and the #WebKitWebView that initiated the
52  * request, and also finish the request with
53  * webkit_uri_scheme_request_finish().
54  *
55  */
56
57 static const unsigned int gReadBufferSize = 8192;
58
59 struct _WebKitURISchemeRequestPrivate {
60     WebKitWebContext* webContext;
61     LegacyCustomProtocolManagerProxy* manager;
62     RefPtr<WebPageProxy> initiatingPage;
63     uint64_t requestID;
64     CString uri;
65     GUniquePtr<SoupURI> soupURI;
66
67     GRefPtr<GInputStream> stream;
68     uint64_t streamLength;
69     GRefPtr<GCancellable> cancellable;
70     char readBuffer[gReadBufferSize];
71     uint64_t bytesRead;
72     CString mimeType;
73 };
74
75 WEBKIT_DEFINE_TYPE(WebKitURISchemeRequest, webkit_uri_scheme_request, G_TYPE_OBJECT)
76
77 static void webkit_uri_scheme_request_class_init(WebKitURISchemeRequestClass*)
78 {
79 }
80
81 WebKitURISchemeRequest* webkitURISchemeRequestCreate(uint64_t requestID, WebKitWebContext* webContext, const ResourceRequest& resourceRequest, LegacyCustomProtocolManagerProxy& manager)
82 {
83     WebKitURISchemeRequest* request = WEBKIT_URI_SCHEME_REQUEST(g_object_new(WEBKIT_TYPE_URI_SCHEME_REQUEST, nullptr));
84     request->priv->webContext = webContext;
85     request->priv->manager = &manager;
86     request->priv->uri = resourceRequest.url().string().utf8();
87     request->priv->requestID = requestID;
88
89     ASSERT(resourceRequest.initiatingPageID());
90     request->priv->initiatingPage = WebProcessProxy::webPage(makeObjectIdentifier<WebPageProxyIdentifierType>(*resourceRequest.initiatingPageID()));
91     ASSERT(request->priv->initiatingPage);
92
93     return request;
94 }
95
96 void webkitURISchemeRequestCancel(WebKitURISchemeRequest* request)
97 {
98     g_cancellable_cancel(request->priv->cancellable.get());
99 }
100
101 LegacyCustomProtocolManagerProxy* webkitURISchemeRequestGetManager(WebKitURISchemeRequest* request)
102 {
103     return request->priv->manager;
104 }
105
106 void webkitURISchemeRequestInvalidate(WebKitURISchemeRequest* request)
107 {
108     request->priv->manager = nullptr;
109     webkitURISchemeRequestCancel(request);
110 }
111
112 /**
113  * webkit_uri_scheme_request_get_scheme:
114  * @request: a #WebKitURISchemeRequest
115  *
116  * Get the URI scheme of @request
117  *
118  * Returns: the URI scheme of @request
119  */
120 const char* webkit_uri_scheme_request_get_scheme(WebKitURISchemeRequest* request)
121 {
122     g_return_val_if_fail(WEBKIT_IS_URI_SCHEME_REQUEST(request), 0);
123
124     if (!request->priv->soupURI)
125         request->priv->soupURI.reset(soup_uri_new(request->priv->uri.data()));
126     return request->priv->soupURI->scheme;
127 }
128
129 /**
130  * webkit_uri_scheme_request_get_uri:
131  * @request: a #WebKitURISchemeRequest
132  *
133  * Get the URI of @request
134  *
135  * Returns: the full URI of @request
136  */
137 const char* webkit_uri_scheme_request_get_uri(WebKitURISchemeRequest* request)
138 {
139     g_return_val_if_fail(WEBKIT_IS_URI_SCHEME_REQUEST(request), 0);
140
141     return request->priv->uri.data();
142 }
143
144 /**
145  * webkit_uri_scheme_request_get_path:
146  * @request: a #WebKitURISchemeRequest
147  *
148  * Get the URI path of @request
149  *
150  * Returns: the URI path of @request
151  */
152 const char* webkit_uri_scheme_request_get_path(WebKitURISchemeRequest* request)
153 {
154     g_return_val_if_fail(WEBKIT_IS_URI_SCHEME_REQUEST(request), 0);
155
156     if (!request->priv->soupURI)
157         request->priv->soupURI.reset(soup_uri_new(request->priv->uri.data()));
158     return request->priv->soupURI->path;
159 }
160
161 /**
162  * webkit_uri_scheme_request_get_web_view:
163  * @request: a #WebKitURISchemeRequest
164  *
165  * Get the #WebKitWebView that initiated the request.
166  *
167  * Returns: (transfer none): the #WebKitWebView that initiated @request.
168  */
169 WebKitWebView* webkit_uri_scheme_request_get_web_view(WebKitURISchemeRequest* request)
170 {
171     g_return_val_if_fail(WEBKIT_IS_URI_SCHEME_REQUEST(request), 0);
172
173     return webkitWebContextGetWebViewForPage(request->priv->webContext, request->priv->initiatingPage.get());
174 }
175
176 static void webkitURISchemeRequestReadCallback(GInputStream* inputStream, GAsyncResult* result, WebKitURISchemeRequest* schemeRequest)
177 {
178     GRefPtr<WebKitURISchemeRequest> request = adoptGRef(schemeRequest);
179     WebKitURISchemeRequestPrivate* priv = request->priv;
180     GUniqueOutPtr<GError> error;
181     gssize bytesRead = g_input_stream_read_finish(inputStream, result, &error.outPtr());
182     if (!priv->manager) {
183         webkitWebContextDidFinishLoadingCustomProtocol(priv->webContext, priv->requestID);
184         return;
185     }
186
187     if (bytesRead == -1) {
188         webkit_uri_scheme_request_finish_error(request.get(), error.get());
189         return;
190     }
191
192     // Need to check the stream before proceeding as it can be cancelled if finish_error
193     // was previously call, which won't be detected by g_input_stream_read_finish().
194     if (!request->priv->stream)
195         return;
196
197     auto webData = IPC::DataReference(reinterpret_cast<const uint8_t*>(priv->readBuffer), bytesRead);
198     if (!priv->bytesRead) {
199         // First chunk read. In case of empty reply an empty API::Data is sent to the networking process.
200         ResourceResponse response(URL(URL(), String::fromUTF8(priv->uri)), String::fromUTF8(priv->mimeType.data()),
201             priv->streamLength, emptyString());
202         priv->manager->didReceiveResponse(priv->requestID, response, 0);
203         priv->manager->didLoadData(priv->requestID, webData);
204     } else if (bytesRead || (!bytesRead && !priv->streamLength)) {
205         // Subsequent chunk read. We only send an empty API::Data to the networking process when stream length is unknown.
206         priv->manager->didLoadData(priv->requestID, webData);
207     }
208
209     if (!bytesRead) {
210         priv->manager->didFinishLoading(priv->requestID);
211         webkitWebContextDidFinishLoadingCustomProtocol(priv->webContext, priv->requestID);
212         return;
213     }
214
215     priv->bytesRead += bytesRead;
216     g_input_stream_read_async(inputStream, priv->readBuffer, gReadBufferSize, RunLoopSourcePriority::AsyncIONetwork, priv->cancellable.get(),
217         reinterpret_cast<GAsyncReadyCallback>(webkitURISchemeRequestReadCallback), g_object_ref(request.get()));
218 }
219
220 /**
221  * webkit_uri_scheme_request_finish:
222  * @request: a #WebKitURISchemeRequest
223  * @stream: a #GInputStream to read the contents of the request
224  * @stream_length: the length of the stream or -1 if not known
225  * @mime_type: (allow-none): the content type of the stream or %NULL if not known
226  *
227  * Finish a #WebKitURISchemeRequest by setting the contents of the request and its mime type.
228  */
229 void webkit_uri_scheme_request_finish(WebKitURISchemeRequest* request, GInputStream* inputStream, gint64 streamLength, const gchar* mimeType)
230 {
231     g_return_if_fail(WEBKIT_IS_URI_SCHEME_REQUEST(request));
232     g_return_if_fail(G_IS_INPUT_STREAM(inputStream));
233     g_return_if_fail(streamLength == -1 || streamLength >= 0);
234
235     request->priv->stream = inputStream;
236     // We use -1 in the API for consistency with soup when the content length is not known, but 0 internally.
237     request->priv->streamLength = streamLength == -1 ? 0 : streamLength;
238     request->priv->cancellable = adoptGRef(g_cancellable_new());
239     request->priv->bytesRead = 0;
240     request->priv->mimeType = mimeType;
241     g_input_stream_read_async(inputStream, request->priv->readBuffer, gReadBufferSize, RunLoopSourcePriority::AsyncIONetwork, request->priv->cancellable.get(),
242         reinterpret_cast<GAsyncReadyCallback>(webkitURISchemeRequestReadCallback), g_object_ref(request));
243 }
244
245 /**
246  * webkit_uri_scheme_request_finish_error:
247  * @request: a #WebKitURISchemeRequest
248  * @error: a #GError that will be passed to the #WebKitWebView
249  *
250  * Finish a #WebKitURISchemeRequest with a #GError.
251  *
252  * Since: 2.2
253  */
254 void webkit_uri_scheme_request_finish_error(WebKitURISchemeRequest* request, GError* error)
255 {
256     g_return_if_fail(WEBKIT_IS_URI_SCHEME_REQUEST(request));
257     g_return_if_fail(error);
258
259     WebKitURISchemeRequestPrivate* priv = request->priv;
260     if (!webkitWebContextIsLoadingCustomProtocol(priv->webContext, priv->requestID))
261         return;
262
263     priv->stream = nullptr;
264     ResourceError resourceError(g_quark_to_string(error->domain), toWebCoreError(error->code), soupURIToURL(priv->soupURI.get()), String::fromUTF8(error->message));
265     priv->manager->didFailWithError(priv->requestID, resourceError);
266     webkitWebContextDidFinishLoadingCustomProtocol(priv->webContext, priv->requestID);
267 }