Move URL from WebCore to WTF
[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/ResourceError.h>
30 #include <libsoup/soup.h>
31 #include <wtf/glib/GRefPtr.h>
32 #include <wtf/glib/GUniquePtrSoup.h>
33 #include <wtf/glib/RunLoopSourcePriority.h>
34 #include <wtf/glib/WTFGType.h>
35 #include <wtf/text/CString.h>
36
37 using namespace WebKit;
38 using namespace WebCore;
39
40 /**
41  * SECTION: WebKitURISchemeRequest
42  * @Short_description: Represents a URI scheme request
43  * @Title: WebKitURISchemeRequest
44  *
45  * If you register a particular URI scheme in a #WebKitWebContext,
46  * using webkit_web_context_register_uri_scheme(), you have to provide
47  * a #WebKitURISchemeRequestCallback. After that, when a URI request
48  * is made with that particular scheme, your callback will be
49  * called. There you will be able to access properties such as the
50  * scheme, the URI and path, and the #WebKitWebView that initiated the
51  * request, and also finish the request with
52  * webkit_uri_scheme_request_finish().
53  *
54  */
55
56 static const unsigned int gReadBufferSize = 8192;
57
58 struct _WebKitURISchemeRequestPrivate {
59     WebKitWebContext* webContext;
60     LegacyCustomProtocolManagerProxy* manager;
61     RefPtr<WebPageProxy> initiatingPage;
62     uint64_t requestID;
63     CString uri;
64     GUniquePtr<SoupURI> soupURI;
65
66     GRefPtr<GInputStream> stream;
67     uint64_t streamLength;
68     GRefPtr<GCancellable> cancellable;
69     char readBuffer[gReadBufferSize];
70     uint64_t bytesRead;
71     CString mimeType;
72 };
73
74 WEBKIT_DEFINE_TYPE(WebKitURISchemeRequest, webkit_uri_scheme_request, G_TYPE_OBJECT)
75
76 static void webkit_uri_scheme_request_class_init(WebKitURISchemeRequestClass*)
77 {
78 }
79
80 WebKitURISchemeRequest* webkitURISchemeRequestCreate(uint64_t requestID, WebKitWebContext* webContext, const ResourceRequest& resourceRequest, LegacyCustomProtocolManagerProxy& manager)
81 {
82     WebKitURISchemeRequest* request = WEBKIT_URI_SCHEME_REQUEST(g_object_new(WEBKIT_TYPE_URI_SCHEME_REQUEST, nullptr));
83     request->priv->webContext = webContext;
84     request->priv->manager = &manager;
85     request->priv->uri = resourceRequest.url().string().utf8();
86     request->priv->initiatingPage = WebProcessProxy::webPage(resourceRequest.initiatingPageID());
87     request->priv->requestID = requestID;
88     return request;
89 }
90
91 void webkitURISchemeRequestCancel(WebKitURISchemeRequest* request)
92 {
93     g_cancellable_cancel(request->priv->cancellable.get());
94 }
95
96 LegacyCustomProtocolManagerProxy* webkitURISchemeRequestGetManager(WebKitURISchemeRequest* request)
97 {
98     return request->priv->manager;
99 }
100
101 void webkitURISchemeRequestInvalidate(WebKitURISchemeRequest* request)
102 {
103     request->priv->manager = nullptr;
104     webkitURISchemeRequestCancel(request);
105 }
106
107 /**
108  * webkit_uri_scheme_request_get_scheme:
109  * @request: a #WebKitURISchemeRequest
110  *
111  * Get the URI scheme of @request
112  *
113  * Returns: the URI scheme of @request
114  */
115 const char* webkit_uri_scheme_request_get_scheme(WebKitURISchemeRequest* request)
116 {
117     g_return_val_if_fail(WEBKIT_IS_URI_SCHEME_REQUEST(request), 0);
118
119     if (!request->priv->soupURI)
120         request->priv->soupURI.reset(soup_uri_new(request->priv->uri.data()));
121     return request->priv->soupURI->scheme;
122 }
123
124 /**
125  * webkit_uri_scheme_request_get_uri:
126  * @request: a #WebKitURISchemeRequest
127  *
128  * Get the URI of @request
129  *
130  * Returns: the full URI of @request
131  */
132 const char* webkit_uri_scheme_request_get_uri(WebKitURISchemeRequest* request)
133 {
134     g_return_val_if_fail(WEBKIT_IS_URI_SCHEME_REQUEST(request), 0);
135
136     return request->priv->uri.data();
137 }
138
139 /**
140  * webkit_uri_scheme_request_get_path:
141  * @request: a #WebKitURISchemeRequest
142  *
143  * Get the URI path of @request
144  *
145  * Returns: the URI path of @request
146  */
147 const char* webkit_uri_scheme_request_get_path(WebKitURISchemeRequest* request)
148 {
149     g_return_val_if_fail(WEBKIT_IS_URI_SCHEME_REQUEST(request), 0);
150
151     if (!request->priv->soupURI)
152         request->priv->soupURI.reset(soup_uri_new(request->priv->uri.data()));
153     return request->priv->soupURI->path;
154 }
155
156 /**
157  * webkit_uri_scheme_request_get_web_view:
158  * @request: a #WebKitURISchemeRequest
159  *
160  * Get the #WebKitWebView that initiated the request.
161  *
162  * Returns: (transfer none): the #WebKitWebView that initiated @request.
163  */
164 WebKitWebView* webkit_uri_scheme_request_get_web_view(WebKitURISchemeRequest* request)
165 {
166     g_return_val_if_fail(WEBKIT_IS_URI_SCHEME_REQUEST(request), 0);
167
168     // FIXME: initiatingPage is now always null, we need to re-implement this somehow.
169     return request->priv->initiatingPage ? webkitWebContextGetWebViewForPage(request->priv->webContext, request->priv->initiatingPage.get()) : nullptr;
170 }
171
172 static void webkitURISchemeRequestReadCallback(GInputStream* inputStream, GAsyncResult* result, WebKitURISchemeRequest* schemeRequest)
173 {
174     GRefPtr<WebKitURISchemeRequest> request = adoptGRef(schemeRequest);
175     WebKitURISchemeRequestPrivate* priv = request->priv;
176     GUniqueOutPtr<GError> error;
177     gssize bytesRead = g_input_stream_read_finish(inputStream, result, &error.outPtr());
178     if (!priv->manager) {
179         webkitWebContextDidFinishLoadingCustomProtocol(priv->webContext, priv->requestID);
180         return;
181     }
182
183     if (bytesRead == -1) {
184         webkit_uri_scheme_request_finish_error(request.get(), error.get());
185         return;
186     }
187
188     // Need to check the stream before proceeding as it can be cancelled if finish_error
189     // was previously call, which won't be detected by g_input_stream_read_finish().
190     if (!request->priv->stream)
191         return;
192
193     auto webData = IPC::DataReference(reinterpret_cast<const uint8_t*>(priv->readBuffer), bytesRead);
194     if (!priv->bytesRead) {
195         // First chunk read. In case of empty reply an empty API::Data is sent to the networking process.
196         ResourceResponse response(URL(URL(), String::fromUTF8(priv->uri)), String::fromUTF8(priv->mimeType.data()),
197             priv->streamLength, emptyString());
198         priv->manager->didReceiveResponse(priv->requestID, response, 0);
199         priv->manager->didLoadData(priv->requestID, webData);
200     } else if (bytesRead || (!bytesRead && !priv->streamLength)) {
201         // Subsequent chunk read. We only send an empty API::Data to the networking process when stream length is unknown.
202         priv->manager->didLoadData(priv->requestID, webData);
203     }
204
205     if (!bytesRead) {
206         priv->manager->didFinishLoading(priv->requestID);
207         webkitWebContextDidFinishLoadingCustomProtocol(priv->webContext, priv->requestID);
208         return;
209     }
210
211     priv->bytesRead += bytesRead;
212     g_input_stream_read_async(inputStream, priv->readBuffer, gReadBufferSize, RunLoopSourcePriority::AsyncIONetwork, priv->cancellable.get(),
213         reinterpret_cast<GAsyncReadyCallback>(webkitURISchemeRequestReadCallback), g_object_ref(request.get()));
214 }
215
216 /**
217  * webkit_uri_scheme_request_finish:
218  * @request: a #WebKitURISchemeRequest
219  * @stream: a #GInputStream to read the contents of the request
220  * @stream_length: the length of the stream or -1 if not known
221  * @mime_type: (allow-none): the content type of the stream or %NULL if not known
222  *
223  * Finish a #WebKitURISchemeRequest by setting the contents of the request and its mime type.
224  */
225 void webkit_uri_scheme_request_finish(WebKitURISchemeRequest* request, GInputStream* inputStream, gint64 streamLength, const gchar* mimeType)
226 {
227     g_return_if_fail(WEBKIT_IS_URI_SCHEME_REQUEST(request));
228     g_return_if_fail(G_IS_INPUT_STREAM(inputStream));
229     g_return_if_fail(streamLength == -1 || streamLength >= 0);
230
231     request->priv->stream = inputStream;
232     // We use -1 in the API for consistency with soup when the content length is not known, but 0 internally.
233     request->priv->streamLength = streamLength == -1 ? 0 : streamLength;
234     request->priv->cancellable = adoptGRef(g_cancellable_new());
235     request->priv->bytesRead = 0;
236     request->priv->mimeType = mimeType;
237     g_input_stream_read_async(inputStream, request->priv->readBuffer, gReadBufferSize, RunLoopSourcePriority::AsyncIONetwork, request->priv->cancellable.get(),
238         reinterpret_cast<GAsyncReadyCallback>(webkitURISchemeRequestReadCallback), g_object_ref(request));
239 }
240
241 /**
242  * webkit_uri_scheme_request_finish_error:
243  * @request: a #WebKitURISchemeRequest
244  * @error: a #GError that will be passed to the #WebKitWebView
245  *
246  * Finish a #WebKitURISchemeRequest with a #GError.
247  *
248  * Since: 2.2
249  */
250 void webkit_uri_scheme_request_finish_error(WebKitURISchemeRequest* request, GError* error)
251 {
252     g_return_if_fail(WEBKIT_IS_URI_SCHEME_REQUEST(request));
253     g_return_if_fail(error);
254
255     WebKitURISchemeRequestPrivate* priv = request->priv;
256     if (!webkitWebContextIsLoadingCustomProtocol(priv->webContext, priv->requestID))
257         return;
258
259     priv->stream = nullptr;
260     ResourceError resourceError(g_quark_to_string(error->domain), toWebCoreError(error->code), URL(priv->soupURI.get()), String::fromUTF8(error->message));
261     priv->manager->didFailWithError(priv->requestID, resourceError);
262     webkitWebContextDidFinishLoadingCustomProtocol(priv->webContext, priv->requestID);
263 }