blob: cb58ffe9f86f38b149d5f7e7b72fd23c97acba7e [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;
ethannicholas272af6e2016-02-17 10:30:27 -080075 SkAutoTUnref<SkSurface> fSurface;
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 }
ethannicholascecbbe22016-02-17 11:49:43 -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*));
ethannicholascecbbe22016-02-17 11:49:43 -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) {
ethannicholascecbbe22016-02-17 11:49:43 -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
ethannicholas272af6e2016-02-17 10:30:27 -0800132SkData* writeCanvasToPng(SkCanvas* canvas) {
133 // capture pixels
134 SkBitmap bmp;
135 bmp.setInfo(canvas->imageInfo());
136 if (!canvas->readPixels(&bmp, 0, 0)) {
137 fprintf(stderr, "Can't read pixels\n");
138 return nullptr;
139 }
140
joshualitt609d9792016-01-27 11:07:23 -0800141 // write to png
ethannicholas546d6652016-02-16 11:03:04 -0800142 SkDynamicMemoryWStream buffer;
143 write_png((const png_bytep) bmp.getPixels(), bmp.width(), bmp.height(), buffer);
144 return buffer.copyToData();
joshualitt136f5172016-02-02 11:07:39 -0800145}
146
joshualitt6b3cf732016-02-17 11:20:26 -0800147SkCanvas* getCanvasFromRequest(Request* request) {
ethannicholasde8e54c2016-02-12 10:09:34 -0800148 GrContextFactory* factory = request->fContextFactory;
149 SkGLContext* gl = factory->getContextInfo(GrContextFactory::kNative_GLContextType,
150 GrContextFactory::kNone_GLContextOptions).fGLContext;
151 gl->makeCurrent();
152 SkASSERT(request->fDebugCanvas);
ethannicholas272af6e2016-02-17 10:30:27 -0800153 SkCanvas* target = request->fSurface->getCanvas();
joshualitt6b3cf732016-02-17 11:20:26 -0800154 return target;
155}
156
157SkData* setupAndDrawToCanvasReturnPng(Request* request, int n) {
158 SkCanvas* target = getCanvasFromRequest(request);
ethannicholas272af6e2016-02-17 10:30:27 -0800159 request->fDebugCanvas->drawTo(target, n);
160 return writeCanvasToPng(target);
joshualitt609d9792016-01-27 11:07:23 -0800161}
162
joshualitt29e5a892016-02-04 06:08:33 -0800163SkSurface* setupCpuSurface() {
164 SkImageInfo info = SkImageInfo::Make(kImageWidth, kImageHeight, kN32_SkColorType,
165 kPremul_SkAlphaType);
166 return SkSurface::NewRaster(info);
167}
168
joshualitt9a4e1882016-01-27 07:03:29 -0800169static const size_t kBufferSize = 1024;
170
171static int process_upload_data(void* cls, enum MHD_ValueKind kind,
172 const char* key, const char* filename,
173 const char* content_type, const char* transfer_encoding,
174 const char* data, uint64_t off, size_t size) {
175 struct UploadContext* uc = reinterpret_cast<UploadContext*>(cls);
176
177 if (0 != size) {
joshualitt609d9792016-01-27 11:07:23 -0800178 uc->fStream.write(data, size);
joshualitt9a4e1882016-01-27 07:03:29 -0800179 }
180 return MHD_YES;
181}
182
jcgregorio12d47ce2016-02-10 14:10:37 -0800183// SendOK just sends an empty response with a 200 OK status code.
184static int SendOK(MHD_Connection* connection) {
185 const char* data = "";
186
187 MHD_Response* response = MHD_create_response_from_buffer(strlen(data),
188 (void*)data,
189 MHD_RESPMEM_PERSISTENT);
190 int ret = MHD_queue_response(connection, 200, response);
191 MHD_destroy_response(response);
192 return ret;
193}
194
joshualitt792345f2016-02-02 13:02:33 -0800195static int SendData(MHD_Connection* connection, const SkData* data, const char* type,
196 bool setContentDisposition = false, const char* dispositionString = nullptr) {
joshualittccfdaa52016-01-27 07:40:29 -0800197 MHD_Response* response = MHD_create_response_from_buffer(data->size(),
198 const_cast<void*>(data->data()),
199 MHD_RESPMEM_MUST_COPY);
joshualitt609d9792016-01-27 11:07:23 -0800200 MHD_add_response_header(response, "Content-Type", type);
joshualittccfdaa52016-01-27 07:40:29 -0800201
joshualitt792345f2016-02-02 13:02:33 -0800202 if (setContentDisposition) {
203 MHD_add_response_header(response, "Content-Disposition", dispositionString);
204 }
205
joshualittccfdaa52016-01-27 07:40:29 -0800206 int ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
207 MHD_destroy_response(response);
208 return ret;
209}
210
joshualitt6b3cf732016-02-17 11:20:26 -0800211static int SendJSON(MHD_Connection* connection, SkCanvas* canvas, SkDebugCanvas* debugCanvas,
joshualittdb6a2542016-02-11 07:09:51 -0800212 UrlDataManager* urlDataManager, int n) {
joshualitt6b3cf732016-02-17 11:20:26 -0800213 Json::Value root = debugCanvas->toJSON(*urlDataManager, n, canvas);
joshualitt609d9792016-01-27 11:07:23 -0800214 SkDynamicMemoryWStream stream;
joshualittdb6a2542016-02-11 07:09:51 -0800215 stream.writeText(Json::FastWriter().write(root).c_str());
joshualitt609d9792016-01-27 11:07:23 -0800216
217 SkAutoTUnref<SkData> data(stream.copyToData());
218 return SendData(connection, data, "application/json");
219}
220
221static int SendTemplate(MHD_Connection* connection, bool redirect = false,
222 const char* redirectUrl = nullptr) {
jcgregorio21ab1202016-01-28 06:24:19 -0800223 SkString debuggerTemplate = generateTemplate(SkString(FLAGS_source[0]));
joshualittccfdaa52016-01-27 07:40:29 -0800224
225 MHD_Response* response = MHD_create_response_from_buffer(
226 debuggerTemplate.size(),
227 (void*) const_cast<char*>(debuggerTemplate.c_str()),
228 MHD_RESPMEM_MUST_COPY);
jcgregorio6a2046e2016-01-28 05:31:31 -0800229 MHD_add_response_header (response, "Access-Control-Allow-Origin", "*");
joshualittccfdaa52016-01-27 07:40:29 -0800230
jcgregorio6f17bc52016-01-27 11:44:38 -0800231 int status = MHD_HTTP_OK;
232
joshualitt609d9792016-01-27 11:07:23 -0800233 if (redirect) {
234 MHD_add_response_header (response, "Location", redirectUrl);
jcgregorio6f17bc52016-01-27 11:44:38 -0800235 status = MHD_HTTP_SEE_OTHER;
joshualitt609d9792016-01-27 11:07:23 -0800236 }
237
jcgregorio6f17bc52016-01-27 11:44:38 -0800238 int ret = MHD_queue_response(connection, status, response);
joshualittccfdaa52016-01-27 07:40:29 -0800239 MHD_destroy_response(response);
240 return ret;
241}
242
joshualitt483b9012016-02-02 07:16:24 -0800243class UrlHandler {
244public:
245 virtual ~UrlHandler() {}
246 virtual bool canHandle(const char* method, const char* url) = 0;
247 virtual int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800248 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800249 const char* upload_data, size_t* upload_data_size) = 0;
250};
joshualittccfdaa52016-01-27 07:40:29 -0800251
joshualitt29e5a892016-02-04 06:08:33 -0800252class CmdHandler : public UrlHandler {
joshualitt483b9012016-02-02 07:16:24 -0800253public:
254 bool canHandle(const char* method, const char* url) override {
joshualitt136f5172016-02-02 11:07:39 -0800255 const char* kBasePath = "/cmd";
256 return 0 == strncmp(url, kBasePath, strlen(kBasePath));
joshualittccfdaa52016-01-27 07:40:29 -0800257 }
258
joshualitt483b9012016-02-02 07:16:24 -0800259 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800260 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800261 const char* upload_data, size_t* upload_data_size) override {
joshualitt136f5172016-02-02 11:07:39 -0800262 SkTArray<SkString> commands;
263 SkStrSplit(url, "/", &commands);
264
265 if (!request->fPicture.get() || commands.count() > 3) {
266 return MHD_NO;
joshualitt483b9012016-02-02 07:16:24 -0800267 }
joshualitt136f5172016-02-02 11:07:39 -0800268
joshualittdb6a2542016-02-11 07:09:51 -0800269 // /cmd or /cmd/N
270 if (0 == strcmp(method, MHD_HTTP_METHOD_GET)) {
271 int n;
272 if (commands.count() == 1) {
ethannicholas0a0520a2016-02-12 12:06:53 -0800273 n = request->fDebugCanvas->getSize() - 1;
joshualittdb6a2542016-02-11 07:09:51 -0800274 } else {
275 sscanf(commands[1].c_str(), "%d", &n);
276 }
joshualitt6b3cf732016-02-17 11:20:26 -0800277 return SendJSON(connection, getCanvasFromRequest(request), request->fDebugCanvas,
278 &request->fUrlDataManager, n);
joshualitt136f5172016-02-02 11:07:39 -0800279 }
280
281 // /cmd/N, for now only delete supported
282 if (commands.count() == 2 && 0 == strcmp(method, MHD_HTTP_METHOD_DELETE)) {
283 int n;
284 sscanf(commands[1].c_str(), "%d", &n);
285 request->fDebugCanvas->deleteDrawCommandAt(n);
jcgregorio12d47ce2016-02-10 14:10:37 -0800286 return SendOK(connection);
joshualitt136f5172016-02-02 11:07:39 -0800287 }
288
289 // /cmd/N/[0|1]
290 if (commands.count() == 3 && 0 == strcmp(method, MHD_HTTP_METHOD_POST)) {
291 int n, toggle;
292 sscanf(commands[1].c_str(), "%d", &n);
293 sscanf(commands[2].c_str(), "%d", &toggle);
294 request->fDebugCanvas->toggleCommand(n, toggle);
jcgregorio12d47ce2016-02-10 14:10:37 -0800295 return SendOK(connection);
joshualitt136f5172016-02-02 11:07:39 -0800296 }
297
joshualitt483b9012016-02-02 07:16:24 -0800298 return MHD_NO;
299 }
300};
301
302class ImgHandler : public UrlHandler {
303public:
304 bool canHandle(const char* method, const char* url) override {
305 static const char* kBasePath = "/img";
306 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
307 0 == strncmp(url, kBasePath, strlen(kBasePath));
joshualittccfdaa52016-01-27 07:40:29 -0800308 }
309
joshualitt483b9012016-02-02 07:16:24 -0800310 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800311 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800312 const char* upload_data, size_t* upload_data_size) override {
joshualitt136f5172016-02-02 11:07:39 -0800313 SkTArray<SkString> commands;
314 SkStrSplit(url, "/", &commands);
315
316 if (!request->fPicture.get() || commands.count() > 2) {
317 return MHD_NO;
joshualitt483b9012016-02-02 07:16:24 -0800318 }
joshualitta341b902016-02-02 07:37:21 -0800319
joshualitt136f5172016-02-02 11:07:39 -0800320 int n;
321 // /img or /img/N
322 if (commands.count() == 1) {
323 n = request->fDebugCanvas->getSize() - 1;
324 } else {
325 sscanf(commands[1].c_str(), "%d", &n);
326 }
327
ethannicholasde8e54c2016-02-12 10:09:34 -0800328 SkAutoTUnref<SkData> data(setupAndDrawToCanvasReturnPng(request, n));
joshualitt136f5172016-02-02 11:07:39 -0800329 return SendData(connection, data, "image/png");
joshualitt483b9012016-02-02 07:16:24 -0800330 }
331};
joshualittccfdaa52016-01-27 07:40:29 -0800332
ethannicholas0a0520a2016-02-12 12:06:53 -0800333/**
334 Updates the clip visualization alpha. On all subsequent /img requests, the clip will be drawn in
335 black with the specified alpha. 0 = no visible clip, 255 = fully opaque clip.
336 */
337class ClipAlphaHandler : public UrlHandler {
338public:
339 bool canHandle(const char* method, const char* url) override {
340 static const char* kBasePath = "/clipAlpha/";
jcgregorio3341d422016-02-16 10:31:07 -0800341 return 0 == strcmp(method, MHD_HTTP_METHOD_POST) &&
ethannicholas0a0520a2016-02-12 12:06:53 -0800342 0 == strncmp(url, kBasePath, strlen(kBasePath));
343 }
344
345 int handle(Request* request, MHD_Connection* connection,
346 const char* url, const char* method,
347 const char* upload_data, size_t* upload_data_size) override {
348 SkTArray<SkString> commands;
349 SkStrSplit(url, "/", &commands);
350
351 if (!request->fPicture.get() || commands.count() != 2) {
352 return MHD_NO;
353 }
354
355 int alpha;
356 sscanf(commands[1].c_str(), "%d", &alpha);
357
358 request->fDebugCanvas->setClipVizColor(SkColorSetARGB(alpha, 0, 0, 0));
359 return SendOK(connection);
360 }
361};
362
joshualitt483b9012016-02-02 07:16:24 -0800363class PostHandler : public UrlHandler {
364public:
365 bool canHandle(const char* method, const char* url) override {
366 return 0 == strcmp(method, MHD_HTTP_METHOD_POST) &&
367 0 == strcmp(url, "/new");
joshualittccfdaa52016-01-27 07:40:29 -0800368 }
369
joshualitt483b9012016-02-02 07:16:24 -0800370 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800371 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800372 const char* upload_data, size_t* upload_data_size) override {
373 UploadContext* uc = request->fUploadContext;
joshualittccfdaa52016-01-27 07:40:29 -0800374
joshualitt483b9012016-02-02 07:16:24 -0800375 // New connection
376 if (!uc) {
377 // TODO make this a method on request
378 uc = new UploadContext;
379 uc->connection = connection;
380 uc->fPostProcessor = MHD_create_post_processor(connection, kBufferSize,
381 &process_upload_data, uc);
382 SkASSERT(uc->fPostProcessor);
joshualitt609d9792016-01-27 11:07:23 -0800383
joshualitt483b9012016-02-02 07:16:24 -0800384 request->fUploadContext = uc;
385 return MHD_YES;
386 }
387
388 // in process upload
389 if (0 != *upload_data_size) {
390 SkASSERT(uc->fPostProcessor);
391 MHD_post_process(uc->fPostProcessor, upload_data, *upload_data_size);
392 *upload_data_size = 0;
393 return MHD_YES;
394 }
395
396 // end of upload
397 MHD_destroy_post_processor(uc->fPostProcessor);
398 uc->fPostProcessor = nullptr;
399
joshualitt136f5172016-02-02 11:07:39 -0800400 // parse picture from stream
401 request->fPicture.reset(
402 SkPicture::CreateFromStream(request->fUploadContext->fStream.detachAsStream()));
403 if (!request->fPicture.get()) {
404 fprintf(stderr, "Could not create picture from stream.\n");
405 return MHD_NO;
joshualitt483b9012016-02-02 07:16:24 -0800406 }
407
ethannicholasde8e54c2016-02-12 10:09:34 -0800408 // create surface
409 GrContextOptions grContextOpts;
410 request->fContextFactory.reset(new GrContextFactory(grContextOpts));
ethannicholas272af6e2016-02-17 10:30:27 -0800411 request->fSurface.reset(setupSurface(request->fContextFactory.get()));
ethannicholasde8e54c2016-02-12 10:09:34 -0800412
joshualitt136f5172016-02-02 11:07:39 -0800413 // pour picture into debug canvas
414 request->fDebugCanvas.reset(new SkDebugCanvas(kImageWidth, kImageHeight));
415 request->fDebugCanvas->drawPicture(request->fPicture);
416
joshualitta341b902016-02-02 07:37:21 -0800417 // clear upload context
418 delete request->fUploadContext;
419 request->fUploadContext = nullptr;
420
joshualitt483b9012016-02-02 07:16:24 -0800421 return SendTemplate(connection, true, "/");
joshualittccfdaa52016-01-27 07:40:29 -0800422 }
joshualitt483b9012016-02-02 07:16:24 -0800423};
424
joshualitt792345f2016-02-02 13:02:33 -0800425class DownloadHandler : public UrlHandler {
426public:
427 bool canHandle(const char* method, const char* url) override {
428 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
429 0 == strcmp(url, "/download");
430 }
431
432 int handle(Request* request, MHD_Connection* connection,
433 const char* url, const char* method,
434 const char* upload_data, size_t* upload_data_size) override {
435 if (!request->fPicture.get()) {
436 return MHD_NO;
437 }
438
439 // TODO move to a function
440 // Playback into picture recorder
441 SkPictureRecorder recorder;
442 SkCanvas* canvas = recorder.beginRecording(kImageWidth, kImageHeight);
443
444 request->fDebugCanvas->draw(canvas);
445
446 SkAutoTUnref<SkPicture> picture(recorder.endRecording());
447
448 SkDynamicMemoryWStream outStream;
449
450 SkAutoTUnref<SkPixelSerializer> serializer(SkImageEncoder::CreatePixelSerializer());
451 picture->serialize(&outStream, serializer);
452
453 SkAutoTUnref<SkData> data(outStream.copyToData());
454
455 // TODO fancier name handling
456 return SendData(connection, data, "application/octet-stream", true,
457 "attachment; filename=something.skp;");
458 }
459};
460
joshualitt29e5a892016-02-04 06:08:33 -0800461class InfoHandler : public UrlHandler {
462public:
463 bool canHandle(const char* method, const char* url) override {
464 const char* kBaseName = "/info";
465 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
466 0 == strncmp(url, kBaseName, strlen(kBaseName));
467 }
468
469 int handle(Request* request, MHD_Connection* connection,
470 const char* url, const char* method,
471 const char* upload_data, size_t* upload_data_size) override {
472 SkTArray<SkString> commands;
473 SkStrSplit(url, "/", &commands);
474
475 if (!request->fPicture.get() || commands.count() > 2) {
476 return MHD_NO;
477 }
478
479 // drawTo
480 SkAutoTUnref<SkSurface> surface(setupCpuSurface());
481 SkCanvas* canvas = surface->getCanvas();
482
483 int n;
484 // /info or /info/N
485 if (commands.count() == 1) {
486 n = request->fDebugCanvas->getSize() - 1;
487 } else {
488 sscanf(commands[1].c_str(), "%d", &n);
489 }
490
491 // TODO this is really slow and we should cache the matrix and clip
492 request->fDebugCanvas->drawTo(canvas, n);
493
494 // make some json
495 SkMatrix vm = request->fDebugCanvas->getCurrentMatrix();
496 SkIRect clip = request->fDebugCanvas->getCurrentClip();
497 Json::Value info(Json::objectValue);
498 info["ViewMatrix"] = SkJSONCanvas::MakeMatrix(vm);
499 info["ClipRect"] = SkJSONCanvas::MakeIRect(clip);
500
501 std::string json = Json::FastWriter().write(info);
502
503 // We don't want the null terminator so strlen is correct
504 SkAutoTUnref<SkData> data(SkData::NewWithCopy(json.c_str(), strlen(json.c_str())));
505 return SendData(connection, data, "application/json");
506 }
507};
508
joshualittcdad12f2016-02-08 07:08:21 -0800509class DataHandler : public UrlHandler {
510public:
511 bool canHandle(const char* method, const char* url) override {
512 static const char* kBaseUrl = "/data";
513 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
514 0 == strncmp(url, kBaseUrl, strlen(kBaseUrl));
515 }
516
517 int handle(Request* request, MHD_Connection* connection,
518 const char* url, const char* method,
519 const char* upload_data, size_t* upload_data_size) override {
520 SkTArray<SkString> commands;
521 SkStrSplit(url, "/", &commands);
522
523 if (!request->fPicture.get() || commands.count() != 2) {
524 return MHD_NO;
525 }
526
527 SkAutoTUnref<UrlDataManager::UrlData> urlData(
528 SkRef(request->fUrlDataManager.getDataFromUrl(SkString(url))));
529
530 if (urlData) {
531 return SendData(connection, urlData->fData.get(), urlData->fContentType.c_str());
532 }
533 return MHD_NO;
534 }
535};
joshualitt29e5a892016-02-04 06:08:33 -0800536
joshualitt873d6242016-02-08 13:57:44 -0800537class FaviconHandler : public UrlHandler {
538public:
539 bool canHandle(const char* method, const char* url) override {
540 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
541 0 == strcmp(url, "/favicon.ico");
542 }
543
544 int handle(Request* request, MHD_Connection* connection,
545 const char* url, const char* method,
546 const char* upload_data, size_t* upload_data_size) override {
547 SkString dir(FLAGS_faviconDir[0]);
548 dir.append("/favicon.ico");
549 FILE* ico = fopen(dir.c_str(), "r");
550
551 SkAutoTUnref<SkData> data(SkData::NewFromFILE(ico));
552 int ret = SendData(connection, data, "image/vnd.microsoft.icon");
553 fclose(ico);
554 return ret;
555 }
556};
557
558
joshualitt483b9012016-02-02 07:16:24 -0800559class RootHandler : public UrlHandler {
560public:
561 bool canHandle(const char* method, const char* url) override {
562 return 0 == strcmp(method, MHD_HTTP_METHOD_GET) &&
563 0 == strcmp(url, "/");
564 }
565
566 int handle(Request* request, MHD_Connection* connection,
joshualitt136f5172016-02-02 11:07:39 -0800567 const char* url, const char* method,
joshualitt483b9012016-02-02 07:16:24 -0800568 const char* upload_data, size_t* upload_data_size) override {
569 return SendTemplate(connection);
570 }
571};
joshualittccfdaa52016-01-27 07:40:29 -0800572
573class UrlManager {
574public:
575 UrlManager() {
576 // Register handlers
joshualitt483b9012016-02-02 07:16:24 -0800577 fHandlers.push_back(new RootHandler);
578 fHandlers.push_back(new PostHandler);
579 fHandlers.push_back(new ImgHandler);
ethannicholas0a0520a2016-02-12 12:06:53 -0800580 fHandlers.push_back(new ClipAlphaHandler);
joshualitt29e5a892016-02-04 06:08:33 -0800581 fHandlers.push_back(new CmdHandler);
joshualitt483b9012016-02-02 07:16:24 -0800582 fHandlers.push_back(new InfoHandler);
joshualitt792345f2016-02-02 13:02:33 -0800583 fHandlers.push_back(new DownloadHandler);
joshualittcdad12f2016-02-08 07:08:21 -0800584 fHandlers.push_back(new DataHandler);
joshualitt873d6242016-02-08 13:57:44 -0800585 fHandlers.push_back(new FaviconHandler);
joshualitt483b9012016-02-02 07:16:24 -0800586 }
587
588 ~UrlManager() {
589 for (int i = 0; i < fHandlers.count(); i++) { delete fHandlers[i]; }
joshualittccfdaa52016-01-27 07:40:29 -0800590 }
591
592 // This is clearly not efficient for a large number of urls and handlers
593 int invoke(Request* request, MHD_Connection* connection, const char* url, const char* method,
594 const char* upload_data, size_t* upload_data_size) const {
595 for (int i = 0; i < fHandlers.count(); i++) {
joshualitt483b9012016-02-02 07:16:24 -0800596 if (fHandlers[i]->canHandle(method, url)) {
joshualitt136f5172016-02-02 11:07:39 -0800597 return fHandlers[i]->handle(request, connection, url, method, upload_data,
598 upload_data_size);
joshualittccfdaa52016-01-27 07:40:29 -0800599 }
600 }
601 return MHD_NO;
602 }
603
604private:
joshualitt483b9012016-02-02 07:16:24 -0800605 SkTArray<UrlHandler*> fHandlers;
joshualittccfdaa52016-01-27 07:40:29 -0800606};
607
608const UrlManager kUrlManager;
609
joshualitt7f6a1e02016-01-22 11:21:43 -0800610int answer_to_connection(void* cls, struct MHD_Connection* connection,
611 const char* url, const char* method, const char* version,
612 const char* upload_data, size_t* upload_data_size,
613 void** con_cls) {
joshualitt9a4e1882016-01-27 07:03:29 -0800614 SkDebugf("New %s request for %s using version %s\n", method, url, version);
joshualitt7f6a1e02016-01-22 11:21:43 -0800615
joshualitt9a4e1882016-01-27 07:03:29 -0800616 Request* request = reinterpret_cast<Request*>(cls);
joshualitt483b9012016-02-02 07:16:24 -0800617 int result = kUrlManager.invoke(request, connection, url, method, upload_data,
618 upload_data_size);
619 if (MHD_NO == result) {
joshualitt873d6242016-02-08 13:57:44 -0800620 fprintf(stderr, "Invalid method and / or url: %s %s\n", method, url);
joshualitt483b9012016-02-02 07:16:24 -0800621 }
622 return result;
joshualitt7f6a1e02016-01-22 11:21:43 -0800623}
624
625int skiaserve_main() {
joshualittcdad12f2016-02-08 07:08:21 -0800626 Request request(SkString("/data")); // This simple server has one request
joshualitt7f6a1e02016-01-22 11:21:43 -0800627 struct MHD_Daemon* daemon;
jcgregorio21ab1202016-01-28 06:24:19 -0800628 // TODO Add option to bind this strictly to an address, e.g. localhost, for security.
629 daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, FLAGS_port, nullptr, nullptr,
joshualitt9a4e1882016-01-27 07:03:29 -0800630 &answer_to_connection, &request,
631 MHD_OPTION_END);
joshualitt7f6a1e02016-01-22 11:21:43 -0800632 if (NULL == daemon) {
633 return 1;
634 }
635
636 getchar();
637 MHD_stop_daemon(daemon);
638 return 0;
639}
640
641#if !defined SK_BUILD_FOR_IOS
642int main(int argc, char** argv) {
643 SkCommandLineFlags::Parse(argc, argv);
644 return skiaserve_main();
645}
646#endif