Home | History | Annotate | Download | only in test_runner
      1 // Copyright 2013 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 #include "content/shell/renderer/test_runner/TestPlugin.h"
      6 
      7 #include "base/basictypes.h"
      8 #include "base/bind.h"
      9 #include "base/logging.h"
     10 #include "base/memory/shared_memory.h"
     11 #include "content/public/renderer/render_thread.h"
     12 #include "content/shell/renderer/test_runner/TestCommon.h"
     13 #include "content/shell/renderer/test_runner/WebTestDelegate.h"
     14 #include "third_party/skia/include/core/SkBitmap.h"
     15 #include "third_party/skia/include/core/SkCanvas.h"
     16 #include "third_party/skia/include/core/SkColor.h"
     17 #include "third_party/WebKit/public/platform/Platform.h"
     18 #include "third_party/WebKit/public/platform/WebCompositorSupport.h"
     19 #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
     20 #include "third_party/WebKit/public/web/WebFrame.h"
     21 #include "third_party/WebKit/public/web/WebInputEvent.h"
     22 #include "third_party/WebKit/public/web/WebKit.h"
     23 #include "third_party/WebKit/public/web/WebPluginParams.h"
     24 #include "third_party/WebKit/public/web/WebTouchPoint.h"
     25 #include "third_party/WebKit/public/web/WebUserGestureIndicator.h"
     26 
     27 using namespace blink;
     28 using namespace std;
     29 
     30 namespace content {
     31 
     32 namespace {
     33 
     34 // GLenum values copied from gl2.h.
     35 #define GL_FALSE                  0
     36 #define GL_TRUE                   1
     37 #define GL_ONE                    1
     38 #define GL_TRIANGLES              0x0004
     39 #define GL_ONE_MINUS_SRC_ALPHA    0x0303
     40 #define GL_DEPTH_TEST             0x0B71
     41 #define GL_BLEND                  0x0BE2
     42 #define GL_SCISSOR_TEST           0x0B90
     43 #define GL_TEXTURE_2D             0x0DE1
     44 #define GL_FLOAT                  0x1406
     45 #define GL_RGBA                   0x1908
     46 #define GL_UNSIGNED_BYTE          0x1401
     47 #define GL_TEXTURE_MAG_FILTER     0x2800
     48 #define GL_TEXTURE_MIN_FILTER     0x2801
     49 #define GL_TEXTURE_WRAP_S         0x2802
     50 #define GL_TEXTURE_WRAP_T         0x2803
     51 #define GL_NEAREST                0x2600
     52 #define GL_COLOR_BUFFER_BIT       0x4000
     53 #define GL_CLAMP_TO_EDGE          0x812F
     54 #define GL_ARRAY_BUFFER           0x8892
     55 #define GL_STATIC_DRAW            0x88E4
     56 #define GL_FRAGMENT_SHADER        0x8B30
     57 #define GL_VERTEX_SHADER          0x8B31
     58 #define GL_COMPILE_STATUS         0x8B81
     59 #define GL_LINK_STATUS            0x8B82
     60 #define GL_COLOR_ATTACHMENT0      0x8CE0
     61 #define GL_FRAMEBUFFER_COMPLETE   0x8CD5
     62 #define GL_FRAMEBUFFER            0x8D40
     63 
     64 void premultiplyAlpha(const unsigned colorIn[3], float alpha, float colorOut[4])
     65 {
     66     for (int i = 0; i < 3; ++i)
     67         colorOut[i] = (colorIn[i] / 255.0f) * alpha;
     68 
     69     colorOut[3] = alpha;
     70 }
     71 
     72 const char* pointState(WebTouchPoint::State state)
     73 {
     74     switch (state) {
     75     case WebTouchPoint::StateReleased:
     76         return "Released";
     77     case WebTouchPoint::StatePressed:
     78         return "Pressed";
     79     case WebTouchPoint::StateMoved:
     80         return "Moved";
     81     case WebTouchPoint::StateCancelled:
     82         return "Cancelled";
     83     default:
     84         return "Unknown";
     85     }
     86 }
     87 
     88 void printTouchList(WebTestDelegate* delegate, const WebTouchPoint* points, int length)
     89 {
     90     for (int i = 0; i < length; ++i) {
     91         char buffer[100];
     92         snprintf(buffer,
     93                  sizeof(buffer),
     94                  "* %.2f, %.2f: %s\n",
     95                  points[i].position.x,
     96                  points[i].position.y,
     97                  pointState(points[i].state));
     98         delegate->printMessage(buffer);
     99     }
    100 }
    101 
    102 void printEventDetails(WebTestDelegate* delegate, const WebInputEvent& event)
    103 {
    104     if (WebInputEvent::isTouchEventType(event.type)) {
    105         const WebTouchEvent& touch = static_cast<const WebTouchEvent&>(event);
    106         printTouchList(delegate, touch.touches, touch.touchesLength);
    107         printTouchList(delegate, touch.changedTouches, touch.changedTouchesLength);
    108         printTouchList(delegate, touch.targetTouches, touch.targetTouchesLength);
    109     } else if (WebInputEvent::isMouseEventType(event.type) || event.type == WebInputEvent::MouseWheel) {
    110         const WebMouseEvent& mouse = static_cast<const WebMouseEvent&>(event);
    111         char buffer[100];
    112         snprintf(buffer, sizeof(buffer), "* %d, %d\n", mouse.x, mouse.y);
    113         delegate->printMessage(buffer);
    114     } else if (WebInputEvent::isGestureEventType(event.type)) {
    115         const WebGestureEvent& gesture = static_cast<const WebGestureEvent&>(event);
    116         char buffer[100];
    117         snprintf(buffer, sizeof(buffer), "* %d, %d\n", gesture.x, gesture.y);
    118         delegate->printMessage(buffer);
    119     }
    120 }
    121 
    122 WebPluginContainer::TouchEventRequestType parseTouchEventRequestType(const WebString& string)
    123 {
    124     if (string == WebString::fromUTF8("raw"))
    125         return WebPluginContainer::TouchEventRequestTypeRaw;
    126     if (string == WebString::fromUTF8("synthetic"))
    127         return WebPluginContainer::TouchEventRequestTypeSynthesizedMouse;
    128     return WebPluginContainer::TouchEventRequestTypeNone;
    129 }
    130 
    131 void deferredDelete(void* context)
    132 {
    133     TestPlugin* plugin = static_cast<TestPlugin*>(context);
    134     delete plugin;
    135 }
    136 
    137 }
    138 
    139 TestPlugin::TestPlugin(WebFrame* frame, const WebPluginParams& params, WebTestDelegate* delegate)
    140     : m_frame(frame)
    141     , m_delegate(delegate)
    142     , m_container(0)
    143     , m_context(0)
    144     , m_colorTexture(0)
    145     , m_mailboxChanged(false)
    146     , m_framebuffer(0)
    147     , m_touchEventRequest(WebPluginContainer::TouchEventRequestTypeNone)
    148     , m_reRequestTouchEvents(false)
    149     , m_printEventDetails(false)
    150     , m_printUserGestureStatus(false)
    151     , m_canProcessDrag(false)
    152     , m_isPersistent(params.mimeType == pluginPersistsMimeType())
    153     , m_canCreateWithoutRenderer(params.mimeType == canCreateWithoutRendererMimeType())
    154 {
    155     const CR_DEFINE_STATIC_LOCAL(WebString, kAttributePrimitive, ("primitive"));
    156     const CR_DEFINE_STATIC_LOCAL(WebString, kAttributeBackgroundColor, ("background-color"));
    157     const CR_DEFINE_STATIC_LOCAL(WebString, kAttributePrimitiveColor, ("primitive-color"));
    158     const CR_DEFINE_STATIC_LOCAL(WebString, kAttributeOpacity, ("opacity"));
    159     const CR_DEFINE_STATIC_LOCAL(WebString, kAttributeAcceptsTouch, ("accepts-touch"));
    160     const CR_DEFINE_STATIC_LOCAL(WebString, kAttributeReRequestTouchEvents, ("re-request-touch"));
    161     const CR_DEFINE_STATIC_LOCAL(WebString, kAttributePrintEventDetails, ("print-event-details"));
    162     const CR_DEFINE_STATIC_LOCAL(WebString, kAttributeCanProcessDrag, ("can-process-drag"));
    163     const CR_DEFINE_STATIC_LOCAL(WebString, kAttributePrintUserGestureStatus, ("print-user-gesture-status"));
    164 
    165     DCHECK_EQ(params.attributeNames.size(), params.attributeValues.size());
    166     size_t size = params.attributeNames.size();
    167     for (size_t i = 0; i < size; ++i) {
    168         const WebString& attributeName = params.attributeNames[i];
    169         const WebString& attributeValue = params.attributeValues[i];
    170 
    171         if (attributeName == kAttributePrimitive)
    172             m_scene.primitive = parsePrimitive(attributeValue);
    173         else if (attributeName == kAttributeBackgroundColor)
    174             parseColor(attributeValue, m_scene.backgroundColor);
    175         else if (attributeName == kAttributePrimitiveColor)
    176             parseColor(attributeValue, m_scene.primitiveColor);
    177         else if (attributeName == kAttributeOpacity)
    178             m_scene.opacity = parseOpacity(attributeValue);
    179         else if (attributeName == kAttributeAcceptsTouch)
    180             m_touchEventRequest = parseTouchEventRequestType(attributeValue);
    181         else if (attributeName == kAttributeReRequestTouchEvents)
    182             m_reRequestTouchEvents = parseBoolean(attributeValue);
    183         else if (attributeName == kAttributePrintEventDetails)
    184             m_printEventDetails = parseBoolean(attributeValue);
    185         else if (attributeName == kAttributeCanProcessDrag)
    186             m_canProcessDrag = parseBoolean(attributeValue);
    187         else if (attributeName == kAttributePrintUserGestureStatus)
    188             m_printUserGestureStatus = parseBoolean(attributeValue);
    189     }
    190     if (m_canCreateWithoutRenderer)
    191         m_delegate->printMessage(std::string("TestPlugin: canCreateWithoutRenderer\n"));
    192 }
    193 
    194 TestPlugin::~TestPlugin()
    195 {
    196 }
    197 
    198 bool TestPlugin::initialize(WebPluginContainer* container)
    199 {
    200     WebGraphicsContext3D::Attributes attrs;
    201     m_context = Platform::current()->createOffscreenGraphicsContext3D(attrs);
    202     if (m_context && !m_context->makeContextCurrent()) {
    203         delete m_context;
    204         m_context = 0;
    205     }
    206 
    207     if (!initScene())
    208         return false;
    209 
    210     m_layer = cc::TextureLayer::CreateForMailbox(this);
    211     m_webLayer = make_scoped_ptr(InstantiateWebLayer(m_layer));
    212     m_container = container;
    213     m_container->setWebLayer(m_webLayer.get());
    214     if (m_reRequestTouchEvents) {
    215         m_container->requestTouchEventType(WebPluginContainer::TouchEventRequestTypeSynthesizedMouse);
    216         m_container->requestTouchEventType(WebPluginContainer::TouchEventRequestTypeRaw);
    217     }
    218     m_container->requestTouchEventType(m_touchEventRequest);
    219     m_container->setWantsWheelEvents(true);
    220     return true;
    221 }
    222 
    223 void TestPlugin::destroy()
    224 {
    225     if (m_layer.get())
    226         m_layer->ClearTexture();
    227     if (m_container)
    228         m_container->setWebLayer(0);
    229     m_webLayer.reset();
    230     m_layer = NULL;
    231     destroyScene();
    232 
    233     delete m_context;
    234     m_context = 0;
    235 
    236     m_container = 0;
    237     m_frame = 0;
    238 
    239     Platform::current()->callOnMainThread(deferredDelete, this);
    240 }
    241 
    242 NPObject* TestPlugin::scriptableObject()
    243 {
    244     return 0;
    245 }
    246 
    247 bool TestPlugin::canProcessDrag() const
    248 {
    249     return m_canProcessDrag;
    250 }
    251 
    252 void TestPlugin::updateGeometry(const WebRect& frameRect, const WebRect& clipRect, const WebVector<WebRect>& cutOutsRects, bool isVisible)
    253 {
    254     if (clipRect == m_rect)
    255         return;
    256     m_rect = clipRect;
    257 
    258     if (m_rect.isEmpty()) {
    259         m_textureMailbox = cc::TextureMailbox();
    260     } else if (m_context) {
    261         m_context->viewport(0, 0, m_rect.width, m_rect.height);
    262 
    263         m_context->bindTexture(GL_TEXTURE_2D, m_colorTexture);
    264         m_context->texParameteri(
    265             GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    266         m_context->texParameteri(
    267             GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    268         m_context->texParameteri(
    269             GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    270         m_context->texParameteri(
    271             GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    272         m_context->texImage2D(GL_TEXTURE_2D,
    273                               0,
    274                               GL_RGBA,
    275                               m_rect.width,
    276                               m_rect.height,
    277                               0,
    278                               GL_RGBA,
    279                               GL_UNSIGNED_BYTE,
    280                               0);
    281         m_context->bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
    282         m_context->framebufferTexture2D(GL_FRAMEBUFFER,
    283                                         GL_COLOR_ATTACHMENT0,
    284                                         GL_TEXTURE_2D,
    285                                         m_colorTexture,
    286                                         0);
    287 
    288         drawSceneGL();
    289 
    290         gpu::Mailbox mailbox;
    291         m_context->genMailboxCHROMIUM(mailbox.name);
    292         m_context->produceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
    293         m_context->flush();
    294         uint32 syncPoint = m_context->insertSyncPoint();
    295         m_textureMailbox = cc::TextureMailbox(mailbox, GL_TEXTURE_2D, syncPoint);
    296     } else {
    297         size_t bytes = 4 * m_rect.width * m_rect.height;
    298         scoped_ptr<base::SharedMemory> bitmap =
    299             RenderThread::Get()->HostAllocateSharedMemoryBuffer(bytes);
    300         if (!bitmap->Map(bytes)) {
    301             m_textureMailbox = cc::TextureMailbox();
    302         } else {
    303             drawSceneSoftware(bitmap->memory(), bytes);
    304             m_textureMailbox = cc::TextureMailbox(
    305                 bitmap.get(), gfx::Size(m_rect.width, m_rect.height));
    306             m_sharedBitmap = bitmap.Pass();
    307         }
    308     }
    309 
    310     m_mailboxChanged = true;
    311     m_layer->SetNeedsDisplay();
    312 }
    313 
    314 bool TestPlugin::acceptsInputEvents()
    315 {
    316     return true;
    317 }
    318 
    319 bool TestPlugin::isPlaceholder()
    320 {
    321     return false;
    322 }
    323 
    324 static void ignoreReleaseCallback(uint32 sync_point, bool lost) {}
    325 
    326 static void releaseSharedMemory(scoped_ptr<base::SharedMemory> bitmap,
    327                                 uint32 sync_point,
    328                                 bool lost) {}
    329 
    330 bool TestPlugin::PrepareTextureMailbox(
    331     cc::TextureMailbox* mailbox,
    332     scoped_ptr<cc::SingleReleaseCallback>* releaseCallback,
    333     bool useSharedMemory) {
    334     if (!m_mailboxChanged)
    335         return false;
    336     *mailbox = m_textureMailbox;
    337     if (m_textureMailbox.IsTexture()) {
    338       *releaseCallback =
    339           cc::SingleReleaseCallback::Create(base::Bind(&ignoreReleaseCallback));
    340     } else {
    341       *releaseCallback = cc::SingleReleaseCallback::Create(
    342           base::Bind(&releaseSharedMemory, base::Passed(&m_sharedBitmap)));
    343     }
    344     m_mailboxChanged = false;
    345     return true;
    346 }
    347 
    348 TestPlugin::Primitive TestPlugin::parsePrimitive(const WebString& string)
    349 {
    350     const CR_DEFINE_STATIC_LOCAL(WebString, kPrimitiveNone, ("none"));
    351     const CR_DEFINE_STATIC_LOCAL(WebString, kPrimitiveTriangle, ("triangle"));
    352 
    353     Primitive primitive = PrimitiveNone;
    354     if (string == kPrimitiveNone)
    355         primitive = PrimitiveNone;
    356     else if (string == kPrimitiveTriangle)
    357         primitive = PrimitiveTriangle;
    358     else
    359         NOTREACHED();
    360     return primitive;
    361 }
    362 
    363 // FIXME: This method should already exist. Use it.
    364 // For now just parse primary colors.
    365 void TestPlugin::parseColor(const WebString& string, unsigned color[3])
    366 {
    367     color[0] = color[1] = color[2] = 0;
    368     if (string == "black")
    369         return;
    370 
    371     if (string == "red")
    372         color[0] = 255;
    373     else if (string == "green")
    374         color[1] = 255;
    375     else if (string == "blue")
    376         color[2] = 255;
    377     else
    378         NOTREACHED();
    379 }
    380 
    381 float TestPlugin::parseOpacity(const WebString& string)
    382 {
    383     return static_cast<float>(atof(string.utf8().data()));
    384 }
    385 
    386 bool TestPlugin::parseBoolean(const WebString& string)
    387 {
    388     const CR_DEFINE_STATIC_LOCAL(WebString, kPrimitiveTrue, ("true"));
    389     return string == kPrimitiveTrue;
    390 }
    391 
    392 bool TestPlugin::initScene()
    393 {
    394     if (!m_context)
    395         return true;
    396 
    397     float color[4];
    398     premultiplyAlpha(m_scene.backgroundColor, m_scene.opacity, color);
    399 
    400     m_colorTexture = m_context->createTexture();
    401     m_framebuffer = m_context->createFramebuffer();
    402 
    403     m_context->viewport(0, 0, m_rect.width, m_rect.height);
    404     m_context->disable(GL_DEPTH_TEST);
    405     m_context->disable(GL_SCISSOR_TEST);
    406 
    407     m_context->clearColor(color[0], color[1], color[2], color[3]);
    408 
    409     m_context->enable(GL_BLEND);
    410     m_context->blendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
    411 
    412     return m_scene.primitive != PrimitiveNone ? initProgram() && initPrimitive() : true;
    413 }
    414 
    415 void TestPlugin::drawSceneGL() {
    416     m_context->viewport(0, 0, m_rect.width, m_rect.height);
    417     m_context->clear(GL_COLOR_BUFFER_BIT);
    418 
    419     if (m_scene.primitive != PrimitiveNone)
    420         drawPrimitive();
    421 }
    422 
    423 void TestPlugin::drawSceneSoftware(void* memory, size_t bytes) {
    424     DCHECK_EQ(bytes, m_rect.width * m_rect.height * 4u);
    425 
    426     SkColor backgroundColor =
    427             SkColorSetARGB(static_cast<uint8>(m_scene.opacity * 255),
    428                            m_scene.backgroundColor[0],
    429                            m_scene.backgroundColor[1],
    430                            m_scene.backgroundColor[2]);
    431 
    432     SkBitmap bitmap;
    433     bitmap.setConfig(SkBitmap::kARGB_8888_Config, m_rect.width, m_rect.height);
    434     bitmap.setPixels(memory);
    435     SkCanvas canvas(bitmap);
    436     canvas.clear(backgroundColor);
    437 
    438     if (m_scene.primitive != PrimitiveNone) {
    439         DCHECK_EQ(PrimitiveTriangle, m_scene.primitive);
    440         SkColor foregroundColor =
    441                 SkColorSetARGB(static_cast<uint8>(m_scene.opacity * 255),
    442                                m_scene.primitiveColor[0],
    443                                m_scene.primitiveColor[1],
    444                                m_scene.primitiveColor[2]);
    445         SkPath trianglePath;
    446         trianglePath.moveTo(0.5f * m_rect.width, 0.9f * m_rect.height);
    447         trianglePath.lineTo(0.1f * m_rect.width, 0.1f * m_rect.height);
    448         trianglePath.lineTo(0.9f * m_rect.width, 0.1f * m_rect.height);
    449         SkPaint paint;
    450         paint.setColor(foregroundColor);
    451         paint.setStyle(SkPaint::kFill_Style);
    452         canvas.drawPath(trianglePath, paint);
    453     }
    454 }
    455 
    456 void TestPlugin::destroyScene()
    457 {
    458     if (m_scene.program) {
    459         m_context->deleteProgram(m_scene.program);
    460         m_scene.program = 0;
    461     }
    462     if (m_scene.vbo) {
    463         m_context->deleteBuffer(m_scene.vbo);
    464         m_scene.vbo = 0;
    465     }
    466 
    467     if (m_framebuffer) {
    468         m_context->deleteFramebuffer(m_framebuffer);
    469         m_framebuffer = 0;
    470     }
    471 
    472     if (m_colorTexture) {
    473         m_context->deleteTexture(m_colorTexture);
    474         m_colorTexture = 0;
    475     }
    476 }
    477 
    478 bool TestPlugin::initProgram()
    479 {
    480     const string vertexSource(
    481         "attribute vec4 position;  \n"
    482         "void main() {             \n"
    483         "  gl_Position = position; \n"
    484         "}                         \n"
    485     );
    486 
    487     const string fragmentSource(
    488         "precision mediump float; \n"
    489         "uniform vec4 color;      \n"
    490         "void main() {            \n"
    491         "  gl_FragColor = color;  \n"
    492         "}                        \n"
    493     );
    494 
    495     m_scene.program = loadProgram(vertexSource, fragmentSource);
    496     if (!m_scene.program)
    497         return false;
    498 
    499     m_scene.colorLocation = m_context->getUniformLocation(m_scene.program, "color");
    500     m_scene.positionLocation = m_context->getAttribLocation(m_scene.program, "position");
    501     return true;
    502 }
    503 
    504 bool TestPlugin::initPrimitive()
    505 {
    506     DCHECK_EQ(m_scene.primitive, PrimitiveTriangle);
    507 
    508     m_scene.vbo = m_context->createBuffer();
    509     if (!m_scene.vbo)
    510         return false;
    511 
    512     const float vertices[] = {
    513         0.0f,  0.8f, 0.0f,
    514         -0.8f, -0.8f, 0.0f,
    515         0.8f, -0.8f, 0.0f };
    516     m_context->bindBuffer(GL_ARRAY_BUFFER, m_scene.vbo);
    517     m_context->bufferData(GL_ARRAY_BUFFER, sizeof(vertices), 0, GL_STATIC_DRAW);
    518     m_context->bufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
    519     return true;
    520 }
    521 
    522 void TestPlugin::drawPrimitive()
    523 {
    524     DCHECK_EQ(m_scene.primitive, PrimitiveTriangle);
    525     DCHECK(m_scene.vbo);
    526     DCHECK(m_scene.program);
    527 
    528     m_context->useProgram(m_scene.program);
    529 
    530     // Bind primitive color.
    531     float color[4];
    532     premultiplyAlpha(m_scene.primitiveColor, m_scene.opacity, color);
    533     m_context->uniform4f(m_scene.colorLocation, color[0], color[1], color[2], color[3]);
    534 
    535     // Bind primitive vertices.
    536     m_context->bindBuffer(GL_ARRAY_BUFFER, m_scene.vbo);
    537     m_context->enableVertexAttribArray(m_scene.positionLocation);
    538     m_context->vertexAttribPointer(m_scene.positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
    539     m_context->drawArrays(GL_TRIANGLES, 0, 3);
    540 }
    541 
    542 unsigned TestPlugin::loadShader(unsigned type, const string& source)
    543 {
    544     unsigned shader = m_context->createShader(type);
    545     if (shader) {
    546         m_context->shaderSource(shader, source.data());
    547         m_context->compileShader(shader);
    548 
    549         int compiled = 0;
    550         m_context->getShaderiv(shader, GL_COMPILE_STATUS, &compiled);
    551         if (!compiled) {
    552             m_context->deleteShader(shader);
    553             shader = 0;
    554         }
    555     }
    556     return shader;
    557 }
    558 
    559 unsigned TestPlugin::loadProgram(const string& vertexSource, const string& fragmentSource)
    560 {
    561     unsigned vertexShader = loadShader(GL_VERTEX_SHADER, vertexSource);
    562     unsigned fragmentShader = loadShader(GL_FRAGMENT_SHADER, fragmentSource);
    563     unsigned program = m_context->createProgram();
    564     if (vertexShader && fragmentShader && program) {
    565         m_context->attachShader(program, vertexShader);
    566         m_context->attachShader(program, fragmentShader);
    567         m_context->linkProgram(program);
    568 
    569         int linked = 0;
    570         m_context->getProgramiv(program, GL_LINK_STATUS, &linked);
    571         if (!linked) {
    572             m_context->deleteProgram(program);
    573             program = 0;
    574         }
    575     }
    576     if (vertexShader)
    577         m_context->deleteShader(vertexShader);
    578     if (fragmentShader)
    579         m_context->deleteShader(fragmentShader);
    580 
    581     return program;
    582 }
    583 
    584 bool TestPlugin::handleInputEvent(const WebInputEvent& event, WebCursorInfo& info)
    585 {
    586     const char* eventName = 0;
    587     switch (event.type) {
    588     case WebInputEvent::Undefined:           eventName = "unknown"; break;
    589 
    590     case WebInputEvent::MouseDown:           eventName = "MouseDown"; break;
    591     case WebInputEvent::MouseUp:             eventName = "MouseUp"; break;
    592     case WebInputEvent::MouseMove:           eventName = "MouseMove"; break;
    593     case WebInputEvent::MouseEnter:          eventName = "MouseEnter"; break;
    594     case WebInputEvent::MouseLeave:          eventName = "MouseLeave"; break;
    595     case WebInputEvent::ContextMenu:         eventName = "ContextMenu"; break;
    596 
    597     case WebInputEvent::MouseWheel:          eventName = "MouseWheel"; break;
    598 
    599     case WebInputEvent::RawKeyDown:          eventName = "RawKeyDown"; break;
    600     case WebInputEvent::KeyDown:             eventName = "KeyDown"; break;
    601     case WebInputEvent::KeyUp:               eventName = "KeyUp"; break;
    602     case WebInputEvent::Char:                eventName = "Char"; break;
    603 
    604     case WebInputEvent::GestureScrollBegin:  eventName = "GestureScrollBegin"; break;
    605     case WebInputEvent::GestureScrollEnd:    eventName = "GestureScrollEnd"; break;
    606     case WebInputEvent::GestureScrollUpdateWithoutPropagation:
    607     case WebInputEvent::GestureScrollUpdate: eventName = "GestureScrollUpdate"; break;
    608     case WebInputEvent::GestureFlingStart:   eventName = "GestureFlingStart"; break;
    609     case WebInputEvent::GestureFlingCancel:  eventName = "GestureFlingCancel"; break;
    610     case WebInputEvent::GestureTap:          eventName = "GestureTap"; break;
    611     case WebInputEvent::GestureTapUnconfirmed:
    612                                              eventName = "GestureTapUnconfirmed"; break;
    613     case WebInputEvent::GestureTapDown:      eventName = "GestureTapDown"; break;
    614     case WebInputEvent::GestureShowPress:    eventName = "GestureShowPress"; break;
    615     case WebInputEvent::GestureTapCancel:    eventName = "GestureTapCancel"; break;
    616     case WebInputEvent::GestureDoubleTap:    eventName = "GestureDoubleTap"; break;
    617     case WebInputEvent::GestureTwoFingerTap: eventName = "GestureTwoFingerTap"; break;
    618     case WebInputEvent::GestureLongPress:    eventName = "GestureLongPress"; break;
    619     case WebInputEvent::GestureLongTap:      eventName = "GestureLongTap"; break;
    620     case WebInputEvent::GesturePinchBegin:   eventName = "GesturePinchBegin"; break;
    621     case WebInputEvent::GesturePinchEnd:     eventName = "GesturePinchEnd"; break;
    622     case WebInputEvent::GesturePinchUpdate:  eventName = "GesturePinchUpdate"; break;
    623 
    624     case WebInputEvent::TouchStart:          eventName = "TouchStart"; break;
    625     case WebInputEvent::TouchMove:           eventName = "TouchMove"; break;
    626     case WebInputEvent::TouchEnd:            eventName = "TouchEnd"; break;
    627     case WebInputEvent::TouchCancel:         eventName = "TouchCancel"; break;
    628     }
    629 
    630     m_delegate->printMessage(std::string("Plugin received event: ") + (eventName ? eventName : "unknown") + "\n");
    631     if (m_printEventDetails)
    632         printEventDetails(m_delegate, event);
    633     if (m_printUserGestureStatus)
    634         m_delegate->printMessage(std::string("* ") + (WebUserGestureIndicator::isProcessingUserGesture() ? "" : "not ") + "handling user gesture\n");
    635     if (m_isPersistent)
    636         m_delegate->printMessage(std::string("TestPlugin: isPersistent\n"));
    637     return false;
    638 }
    639 
    640 bool TestPlugin::handleDragStatusUpdate(WebDragStatus dragStatus, const WebDragData&, WebDragOperationsMask, const WebPoint& position, const WebPoint& screenPosition)
    641 {
    642     const char* dragStatusName = 0;
    643     switch (dragStatus) {
    644     case WebDragStatusEnter:
    645         dragStatusName = "DragEnter";
    646         break;
    647     case WebDragStatusOver:
    648         dragStatusName = "DragOver";
    649         break;
    650     case WebDragStatusLeave:
    651         dragStatusName = "DragLeave";
    652         break;
    653     case WebDragStatusDrop:
    654         dragStatusName = "DragDrop";
    655         break;
    656     case WebDragStatusUnknown:
    657         NOTREACHED();
    658     }
    659     m_delegate->printMessage(std::string("Plugin received event: ") + dragStatusName + "\n");
    660     return false;
    661 }
    662 
    663 TestPlugin* TestPlugin::create(WebFrame* frame, const WebPluginParams& params, WebTestDelegate* delegate)
    664 {
    665     return new TestPlugin(frame, params, delegate);
    666 }
    667 
    668 const WebString& TestPlugin::mimeType()
    669 {
    670     const CR_DEFINE_STATIC_LOCAL(WebString, kMimeType, ("application/x-webkit-test-webplugin"));
    671     return kMimeType;
    672 }
    673 
    674 const WebString& TestPlugin::canCreateWithoutRendererMimeType()
    675 {
    676     const CR_DEFINE_STATIC_LOCAL(WebString, kCanCreateWithoutRendererMimeType, ("application/x-webkit-test-webplugin-can-create-without-renderer"));
    677     return kCanCreateWithoutRendererMimeType;
    678 }
    679 
    680 const WebString& TestPlugin::pluginPersistsMimeType()
    681 {
    682     const CR_DEFINE_STATIC_LOCAL(WebString, kPluginPersistsMimeType, ("application/x-webkit-test-webplugin-persistent"));
    683     return kPluginPersistsMimeType;
    684 }
    685 
    686 bool TestPlugin::isSupportedMimeType(const WebString& mimeType)
    687 {
    688     return mimeType == TestPlugin::mimeType()
    689            || mimeType == pluginPersistsMimeType()
    690            || mimeType == canCreateWithoutRendererMimeType();
    691 }
    692 
    693 }  // namespace content
    694