blob: c682634fcf526cb0a339d64702a4672638dd389a [file] [log] [blame]
joshualitt7f6a1e02016-01-22 11:21:43 -08001/*
2 * Copyright 2016 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrCaps.h"
9#include "GrContextFactory.h"
10#include "SkCanvas.h"
11#include "SkCommandLineFlags.h"
joshualitt136f5172016-02-02 11:07:39 -080012#include "SkDebugCanvas.h"
joshualitt609d9792016-01-27 11:07:23 -080013#include "SkJSONCanvas.h"
joshualitt29e5a892016-02-04 06:08:33 -080014#include "SkJSONCPP.h"
joshualitt9a4e1882016-01-27 07:03:29 -080015#include "SkPicture.h"
joshualitt792345f2016-02-02 13:02:33 -080016#include "SkPictureRecorder.h"
17#include "SkPixelSerializer.h"
joshualitt7f6a1e02016-01-22 11:21:43 -080018#include "SkStream.h"
19#include "SkSurface.h"
20
joshualittcdad12f2016-02-08 07:08:21 -080021#include "UrlDataManager.h"
22
joshualitt7f6a1e02016-01-22 11:21:43 -080023#include <sys/socket.h>
joshualitt7f6a1e02016-01-22 11:21:43 -080024#include <microhttpd.h>
ethannicholas546d6652016-02-16 11:03:04 -080025#include "png.h"
joshualitt7f6a1e02016-01-22 11:21:43 -080026
27// To get image decoders linked in we have to do the below magic
28#include "SkForceLinking.h"
29#include "SkImageDecoder.h"
30__SK_FORCE_IMAGE_DECODER_LINKING;
31
jcgregorio21ab1202016-01-28 06:24:19 -080032DEFINE_string(source, "https://debugger.skia.org", "Where to load the web UI from.");
joshualitt873d6242016-02-08 13:57:44 -080033DEFINE_string(faviconDir, "tools/skiaserve", "The directory of the favicon");
jcgregorio21ab1202016-01-28 06:24:19 -080034DEFINE_int32(port, 8888, "The port to listen on.");
joshualitt7f6a1e02016-01-22 11:21:43 -080035
36// TODO probably want to make this configurable
37static const int kImageWidth = 1920;
38static const int kImageHeight = 1080;
39
joshualitt7f6a1e02016-01-22 11:21:43 -080040// TODO move to template file
41SkString generateTemplate(SkString source) {
42 SkString debuggerTemplate;
43 debuggerTemplate.appendf(
44 "<!DOCTYPE html>\n"
45 "<html>\n"
46 "<head>\n"
47 " <title>SkDebugger</title>\n"
48 " <meta charset=\"utf-8\" />\n"
49 " <meta http-equiv=\"X-UA-Compatible\" content=\"IE=egde,chrome=1\">\n"
50 " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"
51 " <script src=\"%s/res/js/core.js\" type=\"text/javascript\" charset=\"utf-8\"></script>\n"
52 " <link href=\"%s/res/vul/elements.html\" rel=\"import\" />\n"
53 "</head>\n"
54 "<body class=\"fullbleed layout vertical\">\n"
55 " <debugger-app-sk>This is the app."
56 " </debugger-app-sk>\n"
57 "</body>\n"
58 "</html>", source.c_str(), source.c_str());
59 return debuggerTemplate;
60
61}
62
joshualitt9a4e1882016-01-27 07:03:29 -080063struct UploadContext {
joshualitt609d9792016-01-27 11:07:23 -080064 SkDynamicMemoryWStream fStream;
65 MHD_PostProcessor* fPostProcessor;
joshualitt9a4e1882016-01-27 07:03:29 -080066 MHD_Connection* connection;
67};
68
69struct Request {
joshualittcdad12f2016-02-08 07:08:21 -080070 Request(SkString rootUrl) : fUploadContext(nullptr), fUrlDataManager(rootUrl) {}
joshualitt9a4e1882016-01-27 07:03:29 -080071 UploadContext* fUploadContext;
joshualitt609d9792016-01-27 11:07:23 -080072 SkAutoTUnref<SkPicture> fPicture;
joshualitt136f5172016-02-02 11:07:39 -080073 SkAutoTUnref<SkDebugCanvas> fDebugCanvas;
ethannicholasde8e54c2016-02-12 10:09:34 -080074 SkAutoTDelete<GrContextFactory> fContextFactory;
ethannicholas68c063b2016-02-17 09:17:35 -080075 SkAutoTDelete<SkBitmap> fBitmap;
joshualittcdad12f2016-02-08 07:08:21 -080076 UrlDataManager fUrlDataManager;
joshualitt9a4e1882016-01-27 07:03:29 -080077};
78
joshualitt609d9792016-01-27 11:07:23 -080079// TODO factor this out into functions, also handle CPU path
joshualitt136f5172016-02-02 11:07:39 -080080SkSurface* setupSurface(GrContextFactory* factory) {
joshualitt609d9792016-01-27 11:07:23 -080081 GrContext* context = factory->get(GrContextFactory::kNative_GLContextType,
82 GrContextFactory::kNone_GLContextOptions);
83 int maxRTSize = context->caps()->maxRenderTargetSize();
84 SkImageInfo info = SkImageInfo::Make(SkTMin(kImageWidth, maxRTSize),
85 SkTMin(kImageHeight, maxRTSize),
86 kN32_SkColorType, kPremul_SkAlphaType);
87 uint32_t flags = 0;
88 SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType);
joshualitt136f5172016-02-02 11:07:39 -080089 SkSurface* surface = SkSurface::NewRenderTarget(context, SkSurface::kNo_Budgeted, info, 0,
90 &props);
91 SkASSERT(surface);
joshualitt609d9792016-01-27 11:07:23 -080092
joshualitt136f5172016-02-02 11:07:39 -080093 return surface;
94}
joshualitt609d9792016-01-27 11:07:23 -080095
ethannicholas546d6652016-02-16 11:03:04 -080096static void write_png_callback(png_structp png_ptr, png_bytep data, png_size_t length) {
97 SkWStream* out = (SkWStream*) png_get_io_ptr(png_ptr);
98 out->write(data, length);
99}
100
101static void write_png(const png_bytep rgba, png_uint_32 width, png_uint_32 height, SkWStream& out) {
102 png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
103 SkASSERT(png != nullptr);
104 png_infop info_ptr = png_create_info_struct(png);
105 SkASSERT(info_ptr != nullptr);
106 if (setjmp(png_jmpbuf(png))) {
107 SkFAIL("png encode error");
108 }
ethannicholas68c063b2016-02-17 09:17:35 -0800109 png_set_IHDR(png, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
ethannicholas546d6652016-02-16 11:03:04 -0800110 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
111 png_set_compression_level(png, 1);
112 png_bytepp rows = (png_bytepp) sk_malloc_throw(height * sizeof(png_byte*));
ethannicholas68c063b2016-02-17 09:17:35 -0800113 png_bytep pixels = (png_bytep) sk_malloc_throw(width * height * 3);
ethannicholas546d6652016-02-16 11:03:04 -0800114 for (png_size_t y = 0; y < height; ++y) {
ethannicholas68c063b2016-02-17 09:17:35 -0800115 const png_bytep src = rgba + y * width * 4;
116 rows[y] = pixels + y * width * 3;
117 // convert from RGBA to RGB
118 for (png_size_t x = 0; x < width; ++x) {
119 rows[y][x * 3] = src[x * 4];
120 rows[y][x * 3 + 1] = src[x * 4 + 1];
121 rows[y][x * 3 + 2] = src[x * 4 + 2];
122 }
ethannicholas546d6652016-02-16 11:03:04 -0800123 }
124 png_set_filter(png, 0, PNG_NO_FILTERS);
125 png_set_rows(png, info_ptr, &rows[0]);
126 png_set_write_fn(png, &out, write_png_callback, NULL);
127 png_write_png(png, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
128 png_destroy_write_struct(&png, NULL);
129 sk_free(rows);
130}
131
ethannicholas68c063b2016-02-17 09:17:35 -0800132SkData* writeBitmapToPng(SkBitmap& bmp) {
joshualitt609d9792016-01-27 11:07:23 -0800133 // write to png
ethannicholas546d6652016-02-16 11:03:04 -0800134 SkDynamicMemoryWStream buffer;
135 write_png((const png_bytep) bmp.getPixels(), bmp.width(), bmp.height(), buffer);
136 return buffer.copyToData();
joshualitt136f5172016-02-02 11:07:39 -0800137}
138
ethannicholasde8e54c2016-02-12 10:09:34 -0800139SkData* setupAndDrawToCanvasReturnPng(Request* request, int n) {
140 GrContextFactory* factory = request->fContextFactory;
141 SkGLContext* gl = factory->getContextInfo(GrContextFactory::kNative_GLContextType,
142 GrContextFactory::kNone_GLContextOptions).fGLContext;
143 gl->makeCurrent();
144 SkASSERT(request->fDebugCanvas);
ethannicholas68c063b2016-02-17 09:17:35 -0800145 SkCanvas target(*request->fBitmap);
146 request->fDebugCanvas->drawTo(&target, n);
147 return writeBitmapToPng(*request->fBitmap);
joshualitt609d9792016-01-27 11:07:23 -0800148}
149
joshualitt29e5a892016-02-04 06:08:33 -0800150SkSurface* setupCpuSurface() {
151 SkImageInfo info = SkImageInfo::Make(kImageWidth, kImageHeight, kN32_SkColorType,
152 kPremul_SkAlphaType);
153 return SkSurface::NewRaster(info);
154}
155
joshualitt9a4e1882016-01-27 07:03:29 -0800156static const size_t kBufferSize = 1024;
157
158static int process_upload_data(void* cls, enum MHD_ValueKind kind,
159 const char* key, const char* filename,
160 const char* content_type, const char* transfer_encoding,
161 const char* data, uint64_t off, size_t size) {
162 struct UploadContext* uc = reinterpret_cast<UploadContext*>(cls);
163
164 if (0 != size) {
joshualitt609d9792016-01-27 11:07:23 -0800165 uc->fStream.write(data, size);
joshualitt9a4e1882016-01-27 07:03:29 -0800166 }
167 return MHD_YES;
168}
169
jcgregorio12d47ce2016-02-10 14:10:37 -0800170// SendOK just sends an empty response with a 200 OK status code.
171static int SendOK(MHD_Connection* connection) {
172 const char* data = "";
173
174 MHD_Response* response = MHD_create_response_from_buffer(strlen(data),
175 (void*)data,
176 MHD_RESPMEM_PERSISTENT);
177 int ret = MHD_queue_response(connection, 200, response);
178 MHD_destroy_response(response);
179 return ret;
180}
181
joshualitt792345f2016-02-02 13:02:33 -0800182static int SendData(MHD_Connection* connection, const SkData* data, const char* type,
183 bool setContentDisposition = false, const char* dispositionString = nullptr) {
joshualittccfdaa52016-01-27 07:40:29 -0800184 MHD_Response* response = MHD_create_response_from_buffer(data->size(),
185 const_cast<void*>(data->data()),
186 MHD_RESPMEM_MUST_COPY);
joshualitt609d9792016-01-27 11:07:23 -0800187 MHD_add_response_header(response, "Content-Type", type);
joshualittccfdaa52016-01-27 07:40:29 -0800188
joshualitt792345f2016-02-02 13:02:33 -0800189 if (setContentDisposition) {
190 MHD_add_response_header(response, "Content-Disposition", dispositionString);
191 }
192
joshualittccfdaa52016-01-27 07:40:29 -0800193 int ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
194 MHD_destroy_response(response);
195 return ret;
196}
197
joshualittdb6a2542016-02-11 07:09:51 -0800198static int SendJSON(MHD_Connection* connection, SkDebugCanvas* debugCanvas,
199 UrlDataManager* urlDataManager, int n) {
200 Json::Value root = debugCanvas->toJSON(*urlDataManager, n);
joshualitt609d9792016-01-27 11:07:23 -0800201 SkDynamicMemoryWStream stream;
joshualittdb6a2542016-02-11 07:09:51 -0800202 stream.writeText(Json::FastWriter().write(root).c_str());
joshualitt609d9792016-01-27 11:07:23 -0800203
204 SkAutoTUnref<SkData> data(stream.copyToData());
205 return SendData(connection, data, "application/json");
206}
207
208static int SendTemplate(MHD_Connection* connection, bool redirect = false,
209 const char* redirectUrl = nullptr) {
jcgregorio21ab1202016-01-28 06:24:19 -0800210 SkString debuggerTemplate = generateTemplate(SkString(FLAGS_source[0]));
joshualittccfdaa52016-01-27 07:40:29 -0800211
212 MHD_Response* response = MHD_create_response_from_buffer(
213 debuggerTemplate.size(),
214 (void*) const_cast<char*>(debuggerTemplate.c_str()),
215 MHD_RESPMEM_MUST_COPY);
jcgregorio6a2046e2016-01-28 05:31:31 -0800216 MHD_add_response_header (response, "Access-Control-Allow-Origin", "*");
joshualittccfdaa52016-01-27 07:40:29 -0800217
jcgregorio6f17bc52016-01-27 11:44:38 -0800218 int status = MHD_HTTP_OK;
219
joshualitt609d9792016-01-27 11:07:23 -0800220 if (redirect) {
221 MHD_add_response_header (response, "Location", redirectUrl);
jcgregorio6f17bc52016-01-27 11:44:38 -0800222 status = MHD_HTTP_SEE_OTHER;
joshualitt609d9792016-01-27 11:07:23 -0800223 }
224
jcgregorio6f17bc52016-01-27 11:44:38 -0800225 int ret = MHD_queue_response(connection, status, response);
joshualittccfdaa52016-01-27 07:40:29 -0800226 MHD_destroy_response(response);
227 return ret;
228}
229
joshualitt483b9012016-02-02 07:16:24 -0800230class UrlHandler {
231public:
232 virtual ~UrlHandler() {}
233 virtual bool canHandle(const char* method, const char* url) = 0;
234 virtual int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800235 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800236 const char* upload_data, size_t* upload_data_size) = 0;
237};
joshualittccfdaa52016-01-27 07:40:29 -0800238
joshualitt29e5a892016-02-04 06:08:33 -0800239class CmdHandler : public UrlHandler {
joshualitt483b9012016-02-02 07:16:24 -0800240public:
241 bool canHandle(const char* method, const char* url) override {
joshualitt136f5172016-02-02 11:07:39 -0800242 const char* kBasePath = "/cmd";
243 return 0 == strncmp(url, kBasePath, strlen(kBasePath));
joshualittccfdaa52016-01-27 07:40:29 -0800244 }
245
joshualitt483b9012016-02-02 07:16:24 -0800246 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800247 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800248 const char* upload_data, size_t* upload_data_size) override {
joshualitt136f5172016-02-02 11:07:39 -0800249 SkTArray<SkString> commands;
250 SkStrSplit(url, "/", &commands);
251
252 if (!request->fPicture.get() || commands.count() > 3) {
253 return MHD_NO;
joshualitt483b9012016-02-02 07:16:24 -0800254 }
joshualitt136f5172016-02-02 11:07:39 -0800255
joshualittdb6a2542016-02-11 07:09:51 -0800256 // /cmd or /cmd/N
257 if (0 == strcmp(method, MHD_HTTP_METHOD_GET)) {
258 int n;
259 if (commands.count() == 1) {
ethannicholas0a0520a2016-02-12 12:06:53 -0800260 n = request->fDebugCanvas->getSize() - 1;
joshualittdb6a2542016-02-11 07:09:51 -0800261 } else {
262 sscanf(commands[1].c_str(), "%d", &n);
263 }
264 return SendJSON(connection, request->fDebugCanvas, &request->fUrlDataManager, n);
joshualitt136f5172016-02-02 11:07:39 -0800265 }
266
267 // /cmd/N, for now only delete supported
268 if (commands.count() == 2 && 0 == strcmp(method, MHD_HTTP_METHOD_DELETE)) {
269 int n;
270 sscanf(commands[1].c_str(), "%d", &n);
271 request->fDebugCanvas->deleteDrawCommandAt(n);
jcgregorio12d47ce2016-02-10 14:10:37 -0800272 return SendOK(connection);
joshualitt136f5172016-02-02 11:07:39 -0800273 }
274
275 // /cmd/N/[0|1]
276 if (commands.count() == 3 && 0 == strcmp(method, MHD_HTTP_METHOD_POST)) {
277 int n, toggle;
278 sscanf(commands[1].c_str(), "%d", &n);
279 sscanf(commands[2].c_str(), "%d", &toggle);
280 request->fDebugCanvas->toggleCommand(n, toggle);
jcgregorio12d47ce2016-02-10 14:10:37 -0800281 return SendOK(connection);
joshualitt136f5172016-02-02 11:07:39 -0800282 }
283
joshualitt483b9012016-02-02 07:16:24 -0800284 return MHD_NO;
285 }
286};
287
288class ImgHandler : public UrlHandler {
289public:
290 bool canHandle(const char* method, const char* url) override {
291 static const char* kBasePath = "/img";
292 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
293 0 == strncmp(url, kBasePath, strlen(kBasePath));
joshualittccfdaa52016-01-27 07:40:29 -0800294 }
295
joshualitt483b9012016-02-02 07:16:24 -0800296 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800297 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800298 const char* upload_data, size_t* upload_data_size) override {
joshualitt136f5172016-02-02 11:07:39 -0800299 SkTArray<SkString> commands;
300 SkStrSplit(url, "/", &commands);
301
302 if (!request->fPicture.get() || commands.count() > 2) {
303 return MHD_NO;
joshualitt483b9012016-02-02 07:16:24 -0800304 }
joshualitta341b902016-02-02 07:37:21 -0800305
joshualitt136f5172016-02-02 11:07:39 -0800306 int n;
307 // /img or /img/N
308 if (commands.count() == 1) {
309 n = request->fDebugCanvas->getSize() - 1;
310 } else {
311 sscanf(commands[1].c_str(), "%d", &n);
312 }
313
ethannicholasde8e54c2016-02-12 10:09:34 -0800314 SkAutoTUnref<SkData> data(setupAndDrawToCanvasReturnPng(request, n));
joshualitt136f5172016-02-02 11:07:39 -0800315 return SendData(connection, data, "image/png");
joshualitt483b9012016-02-02 07:16:24 -0800316 }
317};
joshualittccfdaa52016-01-27 07:40:29 -0800318
ethannicholas0a0520a2016-02-12 12:06:53 -0800319/**
320 Updates the clip visualization alpha. On all subsequent /img requests, the clip will be drawn in
321 black with the specified alpha. 0 = no visible clip, 255 = fully opaque clip.
322 */
323class ClipAlphaHandler : public UrlHandler {
324public:
325 bool canHandle(const char* method, const char* url) override {
326 static const char* kBasePath = "/clipAlpha/";
jcgregorio3341d422016-02-16 10:31:07 -0800327 return 0 == strcmp(method, MHD_HTTP_METHOD_POST) &&
ethannicholas0a0520a2016-02-12 12:06:53 -0800328 0 == strncmp(url, kBasePath, strlen(kBasePath));
329 }
330
331 int handle(Request* request, MHD_Connection* connection,
332 const char* url, const char* method,
333 const char* upload_data, size_t* upload_data_size) override {
334 SkTArray<SkString> commands;
335 SkStrSplit(url, "/", &commands);
336
337 if (!request->fPicture.get() || commands.count() != 2) {
338 return MHD_NO;
339 }
340
341 int alpha;
342 sscanf(commands[1].c_str(), "%d", &alpha);
343
344 request->fDebugCanvas->setClipVizColor(SkColorSetARGB(alpha, 0, 0, 0));
345 return SendOK(connection);
346 }
347};
348
joshualitt483b9012016-02-02 07:16:24 -0800349class PostHandler : public UrlHandler {
350public:
351 bool canHandle(const char* method, const char* url) override {
352 return 0 == strcmp(method, MHD_HTTP_METHOD_POST) &&
353 0 == strcmp(url, "/new");
joshualittccfdaa52016-01-27 07:40:29 -0800354 }
355
joshualitt483b9012016-02-02 07:16:24 -0800356 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800357 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800358 const char* upload_data, size_t* upload_data_size) override {
359 UploadContext* uc = request->fUploadContext;
joshualittccfdaa52016-01-27 07:40:29 -0800360
joshualitt483b9012016-02-02 07:16:24 -0800361 // New connection
362 if (!uc) {
363 // TODO make this a method on request
364 uc = new UploadContext;
365 uc->connection = connection;
366 uc->fPostProcessor = MHD_create_post_processor(connection, kBufferSize,
367 &process_upload_data, uc);
368 SkASSERT(uc->fPostProcessor);
joshualitt609d9792016-01-27 11:07:23 -0800369
joshualitt483b9012016-02-02 07:16:24 -0800370 request->fUploadContext = uc;
371 return MHD_YES;
372 }
373
374 // in process upload
375 if (0 != *upload_data_size) {
376 SkASSERT(uc->fPostProcessor);
377 MHD_post_process(uc->fPostProcessor, upload_data, *upload_data_size);
378 *upload_data_size = 0;
379 return MHD_YES;
380 }
381
382 // end of upload
383 MHD_destroy_post_processor(uc->fPostProcessor);
384 uc->fPostProcessor = nullptr;
385
joshualitt136f5172016-02-02 11:07:39 -0800386 // parse picture from stream
387 request->fPicture.reset(
388 SkPicture::CreateFromStream(request->fUploadContext->fStream.detachAsStream()));
389 if (!request->fPicture.get()) {
390 fprintf(stderr, "Could not create picture from stream.\n");
391 return MHD_NO;
joshualitt483b9012016-02-02 07:16:24 -0800392 }
393
ethannicholasde8e54c2016-02-12 10:09:34 -0800394 // create surface
395 GrContextOptions grContextOpts;
396 request->fContextFactory.reset(new GrContextFactory(grContextOpts));
ethannicholasde8e54c2016-02-12 10:09:34 -0800397
joshualitt136f5172016-02-02 11:07:39 -0800398 // pour picture into debug canvas
399 request->fDebugCanvas.reset(new SkDebugCanvas(kImageWidth, kImageHeight));
400 request->fDebugCanvas->drawPicture(request->fPicture);
401
ethannicholas68c063b2016-02-17 09:17:35 -0800402 request->fBitmap.reset(new SkBitmap);
403 SkImageInfo imageInfo = SkImageInfo::Make(kImageWidth, kImageHeight, kRGBA_8888_SkColorType,
404 kOpaque_SkAlphaType);
405 request->fBitmap->allocPixels(imageInfo);
406
joshualitta341b902016-02-02 07:37:21 -0800407 // clear upload context
408 delete request->fUploadContext;
409 request->fUploadContext = nullptr;
410
joshualitt483b9012016-02-02 07:16:24 -0800411 return SendTemplate(connection, true, "/");
joshualittccfdaa52016-01-27 07:40:29 -0800412 }
joshualitt483b9012016-02-02 07:16:24 -0800413};
414
joshualitt792345f2016-02-02 13:02:33 -0800415class DownloadHandler : public UrlHandler {
416public:
417 bool canHandle(const char* method, const char* url) override {
418 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
419 0 == strcmp(url, "/download");
420 }
421
422 int handle(Request* request, MHD_Connection* connection,
423 const char* url, const char* method,
424 const char* upload_data, size_t* upload_data_size) override {
425 if (!request->fPicture.get()) {
426 return MHD_NO;
427 }
428
429 // TODO move to a function
430 // Playback into picture recorder
431 SkPictureRecorder recorder;
432 SkCanvas* canvas = recorder.beginRecording(kImageWidth, kImageHeight);
433
434 request->fDebugCanvas->draw(canvas);
435
436 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
437
438 SkDynamicMemoryWStream outStream;
439
440 SkAutoTUnref<SkPixelSerializer> serializer(SkImageEncoder::CreatePixelSerializer());
441 picture->serialize(&outStream, serializer);
442
443 SkAutoTUnref<SkData> data(outStream.copyToData());
444
445 // TODO fancier name handling
446 return SendData(connection, data, "application/octet-stream", true,
447 "attachment; filename=something.skp;");
448 }
449};
450
joshualitt29e5a892016-02-04 06:08:33 -0800451class InfoHandler : public UrlHandler {
452public:
453 bool canHandle(const char* method, const char* url) override {
454 const char* kBaseName = "/info";
455 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
456 0 == strncmp(url, kBaseName, strlen(kBaseName));
457 }
458
459 int handle(Request* request, MHD_Connection* connection,
460 const char* url, const char* method,
461 const char* upload_data, size_t* upload_data_size) override {
462 SkTArray<SkString> commands;
463 SkStrSplit(url, "/", &commands);
464
465 if (!request->fPicture.get() || commands.count() > 2) {
466 return MHD_NO;
467 }
468
469 // drawTo
470 SkAutoTUnref<SkSurface> surface(setupCpuSurface());
471 SkCanvas* canvas = surface->getCanvas();
472
473 int n;
474 // /info or /info/N
475 if (commands.count() == 1) {
476 n = request->fDebugCanvas->getSize() - 1;
477 } else {
478 sscanf(commands[1].c_str(), "%d", &n);
479 }
480
481 // TODO this is really slow and we should cache the matrix and clip
482 request->fDebugCanvas->drawTo(canvas, n);
483
484 // make some json
485 SkMatrix vm = request->fDebugCanvas->getCurrentMatrix();
486 SkIRect clip = request->fDebugCanvas->getCurrentClip();
487 Json::Value info(Json::objectValue);
488 info["ViewMatrix"] = SkJSONCanvas::MakeMatrix(vm);
489 info["ClipRect"] = SkJSONCanvas::MakeIRect(clip);
490
491 std::string json = Json::FastWriter().write(info);
492
493 // We don't want the null terminator so strlen is correct
494 SkAutoTUnref<SkData> data(SkData::NewWithCopy(json.c_str(), strlen(json.c_str())));
495 return SendData(connection, data, "application/json");
496 }
497};
498
joshualittcdad12f2016-02-08 07:08:21 -0800499class DataHandler : public UrlHandler {
500public:
501 bool canHandle(const char* method, const char* url) override {
502 static const char* kBaseUrl = "/data";
503 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
504 0 == strncmp(url, kBaseUrl, strlen(kBaseUrl));
505 }
506
507 int handle(Request* request, MHD_Connection* connection,
508 const char* url, const char* method,
509 const char* upload_data, size_t* upload_data_size) override {
510 SkTArray<SkString> commands;
511 SkStrSplit(url, "/", &commands);
512
513 if (!request->fPicture.get() || commands.count() != 2) {
514 return MHD_NO;
515 }
516
517 SkAutoTUnref<UrlDataManager::UrlData> urlData(
518 SkRef(request->fUrlDataManager.getDataFromUrl(SkString(url))));
519
520 if (urlData) {
521 return SendData(connection, urlData->fData.get(), urlData->fContentType.c_str());
522 }
523 return MHD_NO;
524 }
525};
joshualitt29e5a892016-02-04 06:08:33 -0800526
joshualitt873d6242016-02-08 13:57:44 -0800527class FaviconHandler : public UrlHandler {
528public:
529 bool canHandle(const char* method, const char* url) override {
530 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
531 0 == strcmp(url, "/favicon.ico");
532 }
533
534 int handle(Request* request, MHD_Connection* connection,
535 const char* url, const char* method,
536 const char* upload_data, size_t* upload_data_size) override {
537 SkString dir(FLAGS_faviconDir[0]);
538 dir.append("/favicon.ico");
539 FILE* ico = fopen(dir.c_str(), "r");
540
541 SkAutoTUnref<SkData> data(SkData::NewFromFILE(ico));
542 int ret = SendData(connection, data, "image/vnd.microsoft.icon");
543 fclose(ico);
544 return ret;
545 }
546};
547
548
joshualitt483b9012016-02-02 07:16:24 -0800549class RootHandler : public UrlHandler {
550public:
551 bool canHandle(const char* method, const char* url) override {
552 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
553 0 == strcmp(url, "/");
554 }
555
556 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800557 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800558 const char* upload_data, size_t* upload_data_size) override {
559 return SendTemplate(connection);
560 }
561};
joshualittccfdaa52016-01-27 07:40:29 -0800562
563class UrlManager {
564public:
565 UrlManager() {
566 // Register handlers
joshualitt483b9012016-02-02 07:16:24 -0800567 fHandlers.push_back(new RootHandler);
568 fHandlers.push_back(new PostHandler);
569 fHandlers.push_back(new ImgHandler);
ethannicholas0a0520a2016-02-12 12:06:53 -0800570 fHandlers.push_back(new ClipAlphaHandler);
joshualitt29e5a892016-02-04 06:08:33 -0800571 fHandlers.push_back(new CmdHandler);
joshualitt483b9012016-02-02 07:16:24 -0800572 fHandlers.push_back(new InfoHandler);
joshualitt792345f2016-02-02 13:02:33 -0800573 fHandlers.push_back(new DownloadHandler);
joshualittcdad12f2016-02-08 07:08:21 -0800574 fHandlers.push_back(new DataHandler);
joshualitt873d6242016-02-08 13:57:44 -0800575 fHandlers.push_back(new FaviconHandler);
joshualitt483b9012016-02-02 07:16:24 -0800576 }
577
578 ~UrlManager() {
579 for (int i = 0; i < fHandlers.count(); i++) { delete fHandlers[i]; }
joshualittccfdaa52016-01-27 07:40:29 -0800580 }
581
582 // This is clearly not efficient for a large number of urls and handlers
583 int invoke(Request* request, MHD_Connection* connection, const char* url, const char* method,
584 const char* upload_data, size_t* upload_data_size) const {
585 for (int i = 0; i < fHandlers.count(); i++) {
joshualitt483b9012016-02-02 07:16:24 -0800586 if (fHandlers[i]->canHandle(method, url)) {
joshualitt136f5172016-02-02 11:07:39 -0800587 return fHandlers[i]->handle(request, connection, url, method, upload_data,
588 upload_data_size);
joshualittccfdaa52016-01-27 07:40:29 -0800589 }
590 }
591 return MHD_NO;
592 }
593
594private:
joshualitt483b9012016-02-02 07:16:24 -0800595 SkTArray<UrlHandler*> fHandlers;
joshualittccfdaa52016-01-27 07:40:29 -0800596};
597
598const UrlManager kUrlManager;
599
joshualitt7f6a1e02016-01-22 11:21:43 -0800600int answer_to_connection(void* cls, struct MHD_Connection* connection,
601 const char* url, const char* method, const char* version,
602 const char* upload_data, size_t* upload_data_size,
603 void** con_cls) {
joshualitt9a4e1882016-01-27 07:03:29 -0800604 SkDebugf("New %s request for %s using version %s\n", method, url, version);
joshualitt7f6a1e02016-01-22 11:21:43 -0800605
joshualitt9a4e1882016-01-27 07:03:29 -0800606 Request* request = reinterpret_cast<Request*>(cls);
joshualitt483b9012016-02-02 07:16:24 -0800607 int result = kUrlManager.invoke(request, connection, url, method, upload_data,
608 upload_data_size);
609 if (MHD_NO == result) {
joshualitt873d6242016-02-08 13:57:44 -0800610 fprintf(stderr, "Invalid method and / or url: %s %s\n", method, url);
joshualitt483b9012016-02-02 07:16:24 -0800611 }
612 return result;
joshualitt7f6a1e02016-01-22 11:21:43 -0800613}
614
615int skiaserve_main() {
joshualittcdad12f2016-02-08 07:08:21 -0800616 Request request(SkString("/data")); // This simple server has one request
joshualitt7f6a1e02016-01-22 11:21:43 -0800617 struct MHD_Daemon* daemon;
jcgregorio21ab1202016-01-28 06:24:19 -0800618 // TODO Add option to bind this strictly to an address, e.g. localhost, for security.
619 daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, FLAGS_port, nullptr, nullptr,
joshualitt9a4e1882016-01-27 07:03:29 -0800620 &answer_to_connection, &request,
621 MHD_OPTION_END);
joshualitt7f6a1e02016-01-22 11:21:43 -0800622 if (NULL == daemon) {
623 return 1;
624 }
625
626 getchar();
627 MHD_stop_daemon(daemon);
628 return 0;
629}
630
631#if !defined SK_BUILD_FOR_IOS
632int main(int argc, char** argv) {
633 SkCommandLineFlags::Parse(argc, argv);
634 return skiaserve_main();
635}
636#endif