16a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com#include <cstdio>
26a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com#include <string>
36a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
46a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com#include "ppapi/cpp/completion_callback.h"
56a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com#include "ppapi/cpp/graphics_2d.h"
66a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com#include "ppapi/cpp/image_data.h"
76a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com#include "ppapi/cpp/instance.h"
86a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com#include "ppapi/cpp/module.h"
96a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com#include "ppapi/cpp/var.h"
106a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
116a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com#include "SampleApp.h"
126a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com#include "SkApplication.h"
136a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com#include "SkCanvas.h"
146a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com#include "SkBitmap.h"
156a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com#include "SkEvent.h"
166a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com#include "SkWindow.h"
176a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
186a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.comclass SkiaInstance;
196a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
206a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.comnamespace {
216a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.comvoid FlushCallback(void* data, int32_t result);
226a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com}
236a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
246a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.comSkiaInstance* gPluginInstance;
256a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.comextern int main(int, char**);
266a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
276a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.comclass SkiaInstance : public pp::Instance {
286a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com public:
296a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    explicit SkiaInstance(PP_Instance instance) : pp::Instance(instance),
306a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com                                                  fFlushPending(false),
316a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com                                                  fGraphics2dContext(NULL),
326a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com                                                  fPixelBuffer(NULL)
336a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    {
346a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        gPluginInstance = this;
356a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        application_init();
366a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        char* commandName = "SampleApp";
376a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        fWindow = new SampleWindow(NULL, 0, &commandName, NULL);
386a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    }
396a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
406a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    virtual ~SkiaInstance() {
416a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        gPluginInstance = NULL;
426a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        delete fWindow;
436a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        application_term();
446a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    }
456a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
466a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    virtual void HandleMessage(const pp::Var& var_message) {
476a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        // Receive a message from javascript.  Right now this just signals us to
486a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        // get started.
496a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        uint32_t width = 500;
506a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        uint32_t height = 500;
516a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        char buffer[2048];
526a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        sprintf(buffer, "SetSize:%d,%d", width, height);
536a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        PostMessage(buffer);
546a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    }
556a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
566a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    virtual void DidChangeView(const pp::Rect& position,
576a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com                               const pp::Rect& clip) {
586a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        if (position.size().width() == width() &&
596a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com            position.size().height() == height()) {
606a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com            return;  // Size didn't change, no need to update anything.
616a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        }
626a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        // Create a new device context with the new size.
636a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        DestroyContext();
646a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        CreateContext(position.size());
656a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        // Delete the old pixel buffer and create a new one.
666a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        delete fPixelBuffer;
676a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        fPixelBuffer = NULL;
686a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        if (fGraphics2dContext != NULL) {
696a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com            fPixelBuffer = new pp::ImageData(this,
706a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com                                              PP_IMAGEDATAFORMAT_BGRA_PREMUL,
716a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com                                              fGraphics2dContext->size(),
726a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com                                              false);
736a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com            fWindow->resize(position.size().width(), position.size().height());
746a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com            fWindow->update(NULL);
756a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com            paint();
766a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        }
776a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    }
786a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
796a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    // Indicate whether a flush is pending.  This can only be called from the
806a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    // main thread; it is not thread safe.
816a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    bool flush_pending() const {
826a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        return fFlushPending;
836a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    }
846a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    void set_flush_pending(bool flag) {
856a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        fFlushPending = flag;
866a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    }
876a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
886a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com  void paint() {
896a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    if (fPixelBuffer) {
906a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com      // Draw some stuff.  TODO(borenet): Actually have SampleApp draw into
916a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com      // the plugin area.
926a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com      uint32_t w = fPixelBuffer->size().width();
936a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com      uint32_t h = fPixelBuffer->size().height();
946a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com      uint32_t* data = (uint32_t*) fPixelBuffer->data();
956a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com      // Create a bitmap using the fPixelBuffer pixels
966a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com      SkBitmap bitmap;
976a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com      bitmap.setConfig(SkBitmap::kARGB_8888_Config, w, h);
986a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com      bitmap.setPixels(data);
996a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com      // Create a canvas with the bitmap as the backend
1006a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com      SkCanvas canvas(bitmap);
1016a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
1026a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com      canvas.drawColor(SK_ColorBLUE);
1036a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com      SkRect rect = SkRect::MakeXYWH(10, 10, 80, 80);
1046a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com      SkPaint rect_paint;
1056a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com      rect_paint.setStyle(SkPaint::kFill_Style);
1066a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com      rect_paint.setColor(SK_ColorRED);
1076a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com      canvas.drawRect(rect, rect_paint);
1086a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
1096a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com      FlushPixelBuffer();
1106a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    }
1116a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com  }
1126a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
1136a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.comprivate:
1146a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    int width() const {
1156a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        return fPixelBuffer ? fPixelBuffer->size().width() : 0;
1166a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    }
1176a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
1186a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    int height() const {
1196a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        return fPixelBuffer ? fPixelBuffer->size().height() : 0;
1206a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    }
1216a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
1226a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    bool IsContextValid() const {
1236a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        return fGraphics2dContext != NULL;
1246a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    }
1256a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
1266a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    void CreateContext(const pp::Size& size) {
1276a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        if (IsContextValid())
1286a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com            return;
1296a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        fGraphics2dContext = new pp::Graphics2D(this, size, false);
1306a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        if (!BindGraphics(*fGraphics2dContext)) {
1316a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com            SkDebugf("Couldn't bind the device context");
1326a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        }
1336a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    }
1346a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
1356a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    void DestroyContext() {
1366a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        if (!IsContextValid())
1376a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com            return;
1386a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        delete fGraphics2dContext;
1396a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        fGraphics2dContext = NULL;
1406a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    }
1416a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
1426a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    void FlushPixelBuffer() {
1436a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        if (!IsContextValid())
1446a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com            return;
1456a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        // Note that the pixel lock is held while the buffer is copied into the
1466a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        // device context and then flushed.
1476a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        fGraphics2dContext->PaintImageData(*fPixelBuffer, pp::Point());
1486a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        if (flush_pending())
1496a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com            return;
1506a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        set_flush_pending(true);
1516a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        fGraphics2dContext->Flush(pp::CompletionCallback(&FlushCallback, this));
1526a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    }
1536a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
1546a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    bool fFlushPending;
1556a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    pp::Graphics2D* fGraphics2dContext;
1566a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    pp::ImageData* fPixelBuffer;
1576a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    SampleWindow* fWindow;
1586a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com};
1596a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
1606a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.comclass SkiaModule : public pp::Module {
1616a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.compublic:
1626a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    SkiaModule() : pp::Module() {}
1636a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    virtual ~SkiaModule() {}
1646a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
1656a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    virtual pp::Instance* CreateInstance(PP_Instance instance) {
1666a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        gPluginInstance = new SkiaInstance(instance);
1676a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com        return gPluginInstance;
1686a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    }
1696a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com};
1706a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
1716a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.comnamespace {
1726a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.comvoid FlushCallback(void* data, int32_t result) {
1736a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    static_cast<SkiaInstance*>(data)->set_flush_pending(false);
1746a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com}
1756a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com}
1766a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
1776a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.comnamespace pp {
1786a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.comModule* CreateModule() {
1796a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    return new SkiaModule();
1806a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com}
1816a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com}  // namespace pp
1826a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
1836a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
1846a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com///////////////////////////////////////////
1856a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com///////////// SkOSWindow impl /////////////
1866a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com///////////////////////////////////////////
1876a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
1886a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.comvoid SkOSWindow::onSetTitle(const char title[])
1896a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com{
1906a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    char buffer[2048];
1916a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    sprintf(buffer, "SetTitle:%s", title);
1926a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    gPluginInstance->PostMessage(buffer);
1936a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com}
1946a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
1956a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.comvoid SkOSWindow::onHandleInval(const SkIRect& rect)
1966a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com{
1976a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com    gPluginInstance->paint();
1986a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com}
1996a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
2006a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.comvoid SkOSWindow::onPDFSaved(const char title[], const char desc[],
2016a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com                            const char path[]) {
2026a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com}
2036a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
2046a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com///////////////////////////////////////////
2056a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com/////////////// SkEvent impl //////////////
2066a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com///////////////////////////////////////////
2076a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
2086a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.comvoid SkEvent::SignalQueueTimer(SkMSec ms) {
2096a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com}
2106a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com
2116a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.comvoid SkEvent::SignalNonEmptyQueue() {
2126a98b8c0b5ffe1a23902cdf7e692f702b703eaebborenet@google.com}
213