Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(922)

Side by Side Diff: content/renderer/gpu/webgraphicscontext3d_command_buffer_impl.h

Issue 9340012: Move gpu client files to content_common, in content/common/gpu/client (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: remove unneeded enums Created 8 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef CONTENT_RENDERER_GPU_WEBGRAPHICSCONTEXT3D_COMMAND_BUFFER_IMPL_H_
6 #define CONTENT_RENDERER_GPU_WEBGRAPHICSCONTEXT3D_COMMAND_BUFFER_IMPL_H_
7 #pragma once
8
9 #include <string>
10 #include <vector>
11
12 #include "base/memory/scoped_ptr.h"
13 #include "base/memory/weak_ptr.h"
14 #include "content/renderer/gpu/renderer_gl_context.h"
15 #include "googleurl/src/gurl.h"
16 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebGraphicsC ontext3D.h"
17 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"
18 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
19 #include "ui/gfx/gl/gpu_preference.h"
20 #include "ui/gfx/native_widget_types.h"
21
22 #if defined(USE_SKIA)
23 #define FLIP_FRAMEBUFFER_VERTICALLY
24 #endif
25
26 class GpuChannelHost;
27
28 namespace gpu {
29 namespace gles2 {
30 class GLES2Implementation;
31 }
32 }
33
34 using WebKit::WebGLId;
35
36 using WebKit::WGC3Dchar;
37 using WebKit::WGC3Denum;
38 using WebKit::WGC3Dboolean;
39 using WebKit::WGC3Dbitfield;
40 using WebKit::WGC3Dint;
41 using WebKit::WGC3Dsizei;
42 using WebKit::WGC3Duint;
43 using WebKit::WGC3Dfloat;
44 using WebKit::WGC3Dclampf;
45 using WebKit::WGC3Dintptr;
46 using WebKit::WGC3Dsizeiptr;
47
48 // TODO(piman): move this logic to the compositor and remove it from the
49 // context...
50 class WebGraphicsContext3DSwapBuffersClient {
51 public:
52 virtual ~WebGraphicsContext3DSwapBuffersClient() { }
53 virtual void OnViewContextSwapBuffersPosted() = 0;
54 virtual void OnViewContextSwapBuffersComplete() = 0;
55 virtual void OnViewContextSwapBuffersAborted() = 0;
56 };
57
58 class WebGraphicsContext3DCommandBufferImpl
59 : public WebKit::WebGraphicsContext3D {
60 public:
61 WebGraphicsContext3DCommandBufferImpl(
62 int surface_id,
63 const GURL& active_url,
64 const base::WeakPtr<WebGraphicsContext3DSwapBuffersClient>& swap_client);
65 virtual ~WebGraphicsContext3DCommandBufferImpl();
66
67 bool Initialize(const Attributes& attributes);
68
69 //----------------------------------------------------------------------
70 // WebGraphicsContext3D methods
71
72 // Must be called after initialize() and before any of the following methods.
73 // Permanently binds to the first calling thread. Returns false if the
74 // graphics context fails to create. Do not call from more than one thread.
75 virtual bool makeContextCurrent();
76
77 virtual int width();
78 virtual int height();
79
80 virtual bool isGLES2Compliant();
81
82 virtual bool setParentContext(WebGraphicsContext3D* parent_context);
83
84 virtual void reshape(int width, int height);
85
86 virtual bool readBackFramebuffer(unsigned char* pixels, size_t buffer_size);
87 virtual bool readBackFramebuffer(unsigned char* pixels, size_t buffer_size,
88 WebGLId framebuffer, int width, int height);
89
90 virtual WebGLId getPlatformTextureId();
91 virtual void prepareTexture();
92 virtual void postSubBufferCHROMIUM(int x, int y, int width, int height);
93
94 virtual void activeTexture(WGC3Denum texture);
95 virtual void attachShader(WebGLId program, WebGLId shader);
96 virtual void bindAttribLocation(WebGLId program, WGC3Duint index,
97 const WGC3Dchar* name);
98 virtual void bindBuffer(WGC3Denum target, WebGLId buffer);
99 virtual void bindFramebuffer(WGC3Denum target, WebGLId framebuffer);
100 virtual void bindRenderbuffer(WGC3Denum target, WebGLId renderbuffer);
101 virtual void bindTexture(WGC3Denum target, WebGLId texture);
102 virtual void blendColor(WGC3Dclampf red, WGC3Dclampf green,
103 WGC3Dclampf blue, WGC3Dclampf alpha);
104 virtual void blendEquation(WGC3Denum mode);
105 virtual void blendEquationSeparate(WGC3Denum modeRGB,
106 WGC3Denum modeAlpha);
107 virtual void blendFunc(WGC3Denum sfactor, WGC3Denum dfactor);
108 virtual void blendFuncSeparate(WGC3Denum srcRGB,
109 WGC3Denum dstRGB,
110 WGC3Denum srcAlpha,
111 WGC3Denum dstAlpha);
112
113 virtual void bufferData(WGC3Denum target, WGC3Dsizeiptr size,
114 const void* data, WGC3Denum usage);
115 virtual void bufferSubData(WGC3Denum target, WGC3Dintptr offset,
116 WGC3Dsizeiptr size, const void* data);
117
118 virtual WGC3Denum checkFramebufferStatus(WGC3Denum target);
119 virtual void clear(WGC3Dbitfield mask);
120 virtual void clearColor(WGC3Dclampf red, WGC3Dclampf green,
121 WGC3Dclampf blue, WGC3Dclampf alpha);
122 virtual void clearDepth(WGC3Dclampf depth);
123 virtual void clearStencil(WGC3Dint s);
124 virtual void colorMask(WGC3Dboolean red, WGC3Dboolean green,
125 WGC3Dboolean blue, WGC3Dboolean alpha);
126 virtual void compileShader(WebGLId shader);
127
128 virtual void compressedTexImage2D(WGC3Denum target,
129 WGC3Dint level,
130 WGC3Denum internalformat,
131 WGC3Dsizei width,
132 WGC3Dsizei height,
133 WGC3Dint border,
134 WGC3Dsizei imageSize,
135 const void* data);
136 virtual void compressedTexSubImage2D(WGC3Denum target,
137 WGC3Dint level,
138 WGC3Dint xoffset,
139 WGC3Dint yoffset,
140 WGC3Dsizei width,
141 WGC3Dsizei height,
142 WGC3Denum format,
143 WGC3Dsizei imageSize,
144 const void* data);
145 virtual void copyTexImage2D(WGC3Denum target,
146 WGC3Dint level,
147 WGC3Denum internalformat,
148 WGC3Dint x,
149 WGC3Dint y,
150 WGC3Dsizei width,
151 WGC3Dsizei height,
152 WGC3Dint border);
153 virtual void copyTexSubImage2D(WGC3Denum target,
154 WGC3Dint level,
155 WGC3Dint xoffset,
156 WGC3Dint yoffset,
157 WGC3Dint x,
158 WGC3Dint y,
159 WGC3Dsizei width,
160 WGC3Dsizei height);
161 virtual void cullFace(WGC3Denum mode);
162 virtual void depthFunc(WGC3Denum func);
163 virtual void depthMask(WGC3Dboolean flag);
164 virtual void depthRange(WGC3Dclampf zNear, WGC3Dclampf zFar);
165 virtual void detachShader(WebGLId program, WebGLId shader);
166 virtual void disable(WGC3Denum cap);
167 virtual void disableVertexAttribArray(WGC3Duint index);
168 virtual void drawArrays(WGC3Denum mode, WGC3Dint first, WGC3Dsizei count);
169 virtual void drawElements(WGC3Denum mode,
170 WGC3Dsizei count,
171 WGC3Denum type,
172 WGC3Dintptr offset);
173
174 virtual void enable(WGC3Denum cap);
175 virtual void enableVertexAttribArray(WGC3Duint index);
176 virtual void finish();
177 virtual void flush();
178 virtual void framebufferRenderbuffer(WGC3Denum target,
179 WGC3Denum attachment,
180 WGC3Denum renderbuffertarget,
181 WebGLId renderbuffer);
182 virtual void framebufferTexture2D(WGC3Denum target,
183 WGC3Denum attachment,
184 WGC3Denum textarget,
185 WebGLId texture,
186 WGC3Dint level);
187 virtual void frontFace(WGC3Denum mode);
188 virtual void generateMipmap(WGC3Denum target);
189
190 virtual bool getActiveAttrib(WebGLId program,
191 WGC3Duint index,
192 ActiveInfo&);
193 virtual bool getActiveUniform(WebGLId program,
194 WGC3Duint index,
195 ActiveInfo&);
196
197 virtual void getAttachedShaders(WebGLId program,
198 WGC3Dsizei maxCount,
199 WGC3Dsizei* count,
200 WebGLId* shaders);
201
202 virtual WGC3Dint getAttribLocation(WebGLId program, const WGC3Dchar* name);
203
204 virtual void getBooleanv(WGC3Denum pname, WGC3Dboolean* value);
205
206 virtual void getBufferParameteriv(WGC3Denum target,
207 WGC3Denum pname,
208 WGC3Dint* value);
209
210 virtual Attributes getContextAttributes();
211
212 virtual WGC3Denum getError();
213
214 virtual bool isContextLost();
215
216 virtual void getFloatv(WGC3Denum pname, WGC3Dfloat* value);
217
218 virtual void getFramebufferAttachmentParameteriv(WGC3Denum target,
219 WGC3Denum attachment,
220 WGC3Denum pname,
221 WGC3Dint* value);
222
223 virtual void getIntegerv(WGC3Denum pname, WGC3Dint* value);
224
225 virtual void getProgramiv(WebGLId program, WGC3Denum pname, WGC3Dint* value);
226
227 virtual WebKit::WebString getProgramInfoLog(WebGLId program);
228
229 virtual void getRenderbufferParameteriv(WGC3Denum target,
230 WGC3Denum pname,
231 WGC3Dint* value);
232
233 virtual void getShaderiv(WebGLId shader, WGC3Denum pname, WGC3Dint* value);
234
235 virtual WebKit::WebString getShaderInfoLog(WebGLId shader);
236
237 // TBD
238 // void glGetShaderPrecisionFormat (GLenum shadertype,
239 // GLenum precisiontype,
240 // GLint* range,
241 // GLint* precision);
242
243 virtual WebKit::WebString getShaderSource(WebGLId shader);
244 virtual WebKit::WebString getString(WGC3Denum name);
245
246 virtual void getTexParameterfv(WGC3Denum target,
247 WGC3Denum pname,
248 WGC3Dfloat* value);
249 virtual void getTexParameteriv(WGC3Denum target,
250 WGC3Denum pname,
251 WGC3Dint* value);
252
253 virtual void getUniformfv(WebGLId program,
254 WGC3Dint location,
255 WGC3Dfloat* value);
256 virtual void getUniformiv(WebGLId program,
257 WGC3Dint location,
258 WGC3Dint* value);
259
260 virtual WGC3Dint getUniformLocation(WebGLId program, const WGC3Dchar* name);
261
262 virtual void getVertexAttribfv(WGC3Duint index, WGC3Denum pname,
263 WGC3Dfloat* value);
264 virtual void getVertexAttribiv(WGC3Duint index, WGC3Denum pname,
265 WGC3Dint* value);
266
267 virtual WGC3Dsizeiptr getVertexAttribOffset(WGC3Duint index, WGC3Denum pname);
268
269 virtual void hint(WGC3Denum target, WGC3Denum mode);
270 virtual WGC3Dboolean isBuffer(WebGLId buffer);
271 virtual WGC3Dboolean isEnabled(WGC3Denum cap);
272 virtual WGC3Dboolean isFramebuffer(WebGLId framebuffer);
273 virtual WGC3Dboolean isProgram(WebGLId program);
274 virtual WGC3Dboolean isRenderbuffer(WebGLId renderbuffer);
275 virtual WGC3Dboolean isShader(WebGLId shader);
276 virtual WGC3Dboolean isTexture(WebGLId texture);
277 virtual void lineWidth(WGC3Dfloat);
278 virtual void linkProgram(WebGLId program);
279 virtual void pixelStorei(WGC3Denum pname, WGC3Dint param);
280 virtual void polygonOffset(WGC3Dfloat factor, WGC3Dfloat units);
281
282 virtual void readPixels(WGC3Dint x,
283 WGC3Dint y,
284 WGC3Dsizei width,
285 WGC3Dsizei height,
286 WGC3Denum format,
287 WGC3Denum type,
288 void* pixels);
289
290 virtual void releaseShaderCompiler();
291 virtual void renderbufferStorage(WGC3Denum target,
292 WGC3Denum internalformat,
293 WGC3Dsizei width,
294 WGC3Dsizei height);
295 virtual void sampleCoverage(WGC3Dfloat value, WGC3Dboolean invert);
296 virtual void scissor(WGC3Dint x, WGC3Dint y,
297 WGC3Dsizei width, WGC3Dsizei height);
298 virtual void shaderSource(WebGLId shader, const WGC3Dchar* string);
299 virtual void stencilFunc(WGC3Denum func, WGC3Dint ref, WGC3Duint mask);
300 virtual void stencilFuncSeparate(WGC3Denum face,
301 WGC3Denum func,
302 WGC3Dint ref,
303 WGC3Duint mask);
304 virtual void stencilMask(WGC3Duint mask);
305 virtual void stencilMaskSeparate(WGC3Denum face, WGC3Duint mask);
306 virtual void stencilOp(WGC3Denum fail,
307 WGC3Denum zfail,
308 WGC3Denum zpass);
309 virtual void stencilOpSeparate(WGC3Denum face,
310 WGC3Denum fail,
311 WGC3Denum zfail,
312 WGC3Denum zpass);
313
314 virtual void texImage2D(WGC3Denum target,
315 WGC3Dint level,
316 WGC3Denum internalformat,
317 WGC3Dsizei width,
318 WGC3Dsizei height,
319 WGC3Dint border,
320 WGC3Denum format,
321 WGC3Denum type,
322 const void* pixels);
323
324 virtual void texParameterf(WGC3Denum target,
325 WGC3Denum pname,
326 WGC3Dfloat param);
327 virtual void texParameteri(WGC3Denum target,
328 WGC3Denum pname,
329 WGC3Dint param);
330
331 virtual void texSubImage2D(WGC3Denum target,
332 WGC3Dint level,
333 WGC3Dint xoffset,
334 WGC3Dint yoffset,
335 WGC3Dsizei width,
336 WGC3Dsizei height,
337 WGC3Denum format,
338 WGC3Denum type,
339 const void* pixels);
340
341 virtual void uniform1f(WGC3Dint location, WGC3Dfloat x);
342 virtual void uniform1fv(WGC3Dint location,
343 WGC3Dsizei count, const WGC3Dfloat* v);
344 virtual void uniform1i(WGC3Dint location, WGC3Dint x);
345 virtual void uniform1iv(WGC3Dint location,
346 WGC3Dsizei count, const WGC3Dint* v);
347 virtual void uniform2f(WGC3Dint location, WGC3Dfloat x, WGC3Dfloat y);
348 virtual void uniform2fv(WGC3Dint location,
349 WGC3Dsizei count, const WGC3Dfloat* v);
350 virtual void uniform2i(WGC3Dint location, WGC3Dint x, WGC3Dint y);
351 virtual void uniform2iv(WGC3Dint location,
352 WGC3Dsizei count, const WGC3Dint* v);
353 virtual void uniform3f(WGC3Dint location,
354 WGC3Dfloat x, WGC3Dfloat y, WGC3Dfloat z);
355 virtual void uniform3fv(WGC3Dint location,
356 WGC3Dsizei count, const WGC3Dfloat* v);
357 virtual void uniform3i(WGC3Dint location,
358 WGC3Dint x, WGC3Dint y, WGC3Dint z);
359 virtual void uniform3iv(WGC3Dint location,
360 WGC3Dsizei count, const WGC3Dint* v);
361 virtual void uniform4f(WGC3Dint location,
362 WGC3Dfloat x, WGC3Dfloat y,
363 WGC3Dfloat z, WGC3Dfloat w);
364 virtual void uniform4fv(WGC3Dint location,
365 WGC3Dsizei count, const WGC3Dfloat* v);
366 virtual void uniform4i(WGC3Dint location,
367 WGC3Dint x, WGC3Dint y, WGC3Dint z, WGC3Dint w);
368 virtual void uniform4iv(WGC3Dint location,
369 WGC3Dsizei count, const WGC3Dint* v);
370 virtual void uniformMatrix2fv(WGC3Dint location,
371 WGC3Dsizei count,
372 WGC3Dboolean transpose,
373 const WGC3Dfloat* value);
374 virtual void uniformMatrix3fv(WGC3Dint location,
375 WGC3Dsizei count,
376 WGC3Dboolean transpose,
377 const WGC3Dfloat* value);
378 virtual void uniformMatrix4fv(WGC3Dint location,
379 WGC3Dsizei count,
380 WGC3Dboolean transpose,
381 const WGC3Dfloat* value);
382
383 virtual void useProgram(WebGLId program);
384 virtual void validateProgram(WebGLId program);
385
386 virtual void vertexAttrib1f(WGC3Duint index, WGC3Dfloat x);
387 virtual void vertexAttrib1fv(WGC3Duint index, const WGC3Dfloat* values);
388 virtual void vertexAttrib2f(WGC3Duint index, WGC3Dfloat x, WGC3Dfloat y);
389 virtual void vertexAttrib2fv(WGC3Duint index, const WGC3Dfloat* values);
390 virtual void vertexAttrib3f(WGC3Duint index,
391 WGC3Dfloat x, WGC3Dfloat y, WGC3Dfloat z);
392 virtual void vertexAttrib3fv(WGC3Duint index, const WGC3Dfloat* values);
393 virtual void vertexAttrib4f(WGC3Duint index,
394 WGC3Dfloat x, WGC3Dfloat y,
395 WGC3Dfloat z, WGC3Dfloat w);
396 virtual void vertexAttrib4fv(WGC3Duint index, const WGC3Dfloat* values);
397 virtual void vertexAttribPointer(WGC3Duint index,
398 WGC3Dint size,
399 WGC3Denum type,
400 WGC3Dboolean normalized,
401 WGC3Dsizei stride,
402 WGC3Dintptr offset);
403
404 virtual void viewport(WGC3Dint x, WGC3Dint y,
405 WGC3Dsizei width, WGC3Dsizei height);
406
407 // Support for buffer creation and deletion
408 virtual WebGLId createBuffer();
409 virtual WebGLId createFramebuffer();
410 virtual WebGLId createProgram();
411 virtual WebGLId createRenderbuffer();
412 virtual WebGLId createShader(WGC3Denum);
413 virtual WebGLId createTexture();
414
415 virtual void deleteBuffer(WebGLId);
416 virtual void deleteFramebuffer(WebGLId);
417 virtual void deleteProgram(WebGLId);
418 virtual void deleteRenderbuffer(WebGLId);
419 virtual void deleteShader(WebGLId);
420 virtual void deleteTexture(WebGLId);
421
422 virtual void synthesizeGLError(WGC3Denum);
423
424 virtual void* mapBufferSubDataCHROMIUM(
425 WGC3Denum target, WGC3Dintptr offset,
426 WGC3Dsizeiptr size, WGC3Denum access);
427 virtual void unmapBufferSubDataCHROMIUM(const void*);
428 virtual void* mapTexSubImage2DCHROMIUM(
429 WGC3Denum target,
430 WGC3Dint level,
431 WGC3Dint xoffset,
432 WGC3Dint yoffset,
433 WGC3Dsizei width,
434 WGC3Dsizei height,
435 WGC3Denum format,
436 WGC3Denum type,
437 WGC3Denum access);
438 virtual void unmapTexSubImage2DCHROMIUM(const void*);
439
440 virtual void setVisibilityCHROMIUM(bool visible);
441
442 virtual void copyTextureToParentTextureCHROMIUM(
443 WebGLId texture, WebGLId parentTexture);
444
445 virtual void rateLimitOffscreenContextCHROMIUM();
446
447 virtual WebKit::WebString getRequestableExtensionsCHROMIUM();
448 virtual void requestExtensionCHROMIUM(const char*);
449
450 virtual void blitFramebufferCHROMIUM(
451 WGC3Dint srcX0, WGC3Dint srcY0, WGC3Dint srcX1, WGC3Dint srcY1,
452 WGC3Dint dstX0, WGC3Dint dstY0, WGC3Dint dstX1, WGC3Dint dstY1,
453 WGC3Dbitfield mask, WGC3Denum filter);
454 virtual void renderbufferStorageMultisampleCHROMIUM(
455 WGC3Denum target, WGC3Dsizei samples, WGC3Denum internalformat,
456 WGC3Dsizei width, WGC3Dsizei height);
457
458 virtual WebKit::WebString getTranslatedShaderSourceANGLE(WebGLId shader);
459
460 RendererGLContext* context() { return context_; }
461
462 virtual void setContextLostCallback(
463 WebGraphicsContext3D::WebGraphicsContextLostCallback* callback);
464 virtual WGC3Denum getGraphicsResetStatusARB();
465
466 virtual void setSwapBuffersCompleteCallbackCHROMIUM(
467 WebGraphicsContext3D::
468 WebGraphicsSwapBuffersCompleteCallbackCHROMIUM* callback);
469
470 virtual void texImageIOSurface2DCHROMIUM(
471 WGC3Denum target, WGC3Dint width, WGC3Dint height,
472 WGC3Duint ioSurfaceId, WGC3Duint plane);
473
474 virtual void texStorage2DEXT(
475 WGC3Denum target, WGC3Dint levels, WGC3Duint internalformat,
476 WGC3Dint width, WGC3Dint height);
477
478 protected:
479 #if WEBKIT_USING_SKIA
480 virtual GrGLInterface* onCreateGrGLInterface();
481 #endif
482
483 private:
484 // Initialize the underlying GL context. May be called multiple times; second
485 // and subsequent calls are ignored. Must be called from the thread that is
486 // going to use this object to issue GL commands (which might not be the main
487 // thread).
488 bool MaybeInitializeGL();
489
490 // SwapBuffers callback.
491 void OnSwapBuffersComplete();
492 virtual void OnContextLost(RendererGLContext::ContextLostReason reason);
493
494 bool initialize_failed_;
495
496 // The context we use for OpenGL rendering.
497 RendererGLContext* context_;
498 // The GLES2Implementation we use for OpenGL rendering.
499 gpu::gles2::GLES2Implementation* gl_;
500
501 // State needed by MaybeInitializeGL.
502 GpuChannelHost* host_;
503 int32 surface_id_;
504 GURL active_url_;
505 base::WeakPtr<WebGraphicsContext3DSwapBuffersClient> swap_client_;
506
507 WebGraphicsContext3D::WebGraphicsContextLostCallback* context_lost_callback_;
508 WGC3Denum context_lost_reason_;
509
510 WebGraphicsContext3D::WebGraphicsSwapBuffersCompleteCallbackCHROMIUM*
511 swapbuffers_complete_callback_;
512
513 WebKit::WebGraphicsContext3D::Attributes attributes_;
514 gfx::GpuPreference gpu_preference_;
515 int cached_width_, cached_height_;
516
517 // For tracking which FBO is bound.
518 WebGLId bound_fbo_;
519
520 // Errors raised by synthesizeGLError().
521 std::vector<WGC3Denum> synthetic_errors_;
522
523 base::WeakPtrFactory<WebGraphicsContext3DCommandBufferImpl> weak_ptr_factory_;
524
525 #ifdef FLIP_FRAMEBUFFER_VERTICALLY
526 scoped_array<uint8> scanline_;
527 void FlipVertically(uint8* framebuffer,
528 unsigned int width,
529 unsigned int height);
530 #endif
531 };
532
533 #endif // CONTENT_RENDERER_GPU_WEBGRAPHICSCONTEXT3D_COMMAND_BUFFER_IMPL_H_
OLDNEW
« no previous file with comments | « content/renderer/gpu/renderer_gl_context.cc ('k') | content/renderer/gpu/webgraphicscontext3d_command_buffer_impl.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698