1/*
2 * Copyright (C) 2011 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1.  Redistributions of source code must retain the above copyright
9 *     notice, this list of conditions and the following disclaimer.
10 * 2.  Redistributions in binary form must reproduce the above copyright
11 *     notice, this list of conditions and the following disclaimer in the
12 *     documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "TestPlugin.h"
27
28#include "TestCommon.h"
29#include "public/platform/Platform.h"
30#include "public/platform/WebCompositorSupport.h"
31#include "public/platform/WebGraphicsContext3D.h"
32#include "public/testing/WebTestDelegate.h"
33#include "public/web/WebFrame.h"
34#include "public/web/WebInputEvent.h"
35#include "public/web/WebKit.h"
36#include "public/web/WebPluginParams.h"
37#include "public/web/WebTouchPoint.h"
38#include "public/web/WebUserGestureIndicator.h"
39
40using namespace WebKit;
41using namespace std;
42
43namespace WebTestRunner {
44
45namespace {
46
47// GLenum values copied from gl2.h.
48#define GL_FALSE                  0
49#define GL_TRUE                   1
50#define GL_ONE                    1
51#define GL_TRIANGLES              0x0004
52#define GL_ONE_MINUS_SRC_ALPHA    0x0303
53#define GL_DEPTH_TEST             0x0B71
54#define GL_BLEND                  0x0BE2
55#define GL_SCISSOR_TEST           0x0B90
56#define GL_TEXTURE_2D             0x0DE1
57#define GL_FLOAT                  0x1406
58#define GL_RGBA                   0x1908
59#define GL_UNSIGNED_BYTE          0x1401
60#define GL_TEXTURE_MAG_FILTER     0x2800
61#define GL_TEXTURE_MIN_FILTER     0x2801
62#define GL_TEXTURE_WRAP_S         0x2802
63#define GL_TEXTURE_WRAP_T         0x2803
64#define GL_NEAREST                0x2600
65#define GL_COLOR_BUFFER_BIT       0x4000
66#define GL_CLAMP_TO_EDGE          0x812F
67#define GL_ARRAY_BUFFER           0x8892
68#define GL_STATIC_DRAW            0x88E4
69#define GL_FRAGMENT_SHADER        0x8B30
70#define GL_VERTEX_SHADER          0x8B31
71#define GL_COMPILE_STATUS         0x8B81
72#define GL_LINK_STATUS            0x8B82
73#define GL_COLOR_ATTACHMENT0      0x8CE0
74#define GL_FRAMEBUFFER_COMPLETE   0x8CD5
75#define GL_FRAMEBUFFER            0x8D40
76
77void premultiplyAlpha(const unsigned colorIn[3], float alpha, float colorOut[4])
78{
79    for (int i = 0; i < 3; ++i)
80        colorOut[i] = (colorIn[i] / 255.0f) * alpha;
81
82    colorOut[3] = alpha;
83}
84
85const char* pointState(WebTouchPoint::State state)
86{
87    switch (state) {
88    case WebTouchPoint::StateReleased:
89        return "Released";
90    case WebTouchPoint::StatePressed:
91        return "Pressed";
92    case WebTouchPoint::StateMoved:
93        return "Moved";
94    case WebTouchPoint::StateCancelled:
95        return "Cancelled";
96    default:
97        return "Unknown";
98    }
99
100    WEBKIT_ASSERT_NOT_REACHED();
101    return 0;
102}
103
104void printTouchList(WebTestDelegate* delegate, const WebTouchPoint* points, int length)
105{
106    for (int i = 0; i < length; ++i) {
107        char buffer[100];
108        snprintf(buffer, sizeof(buffer), "* %d, %d: %s\n", points[i].position.x, points[i].position.y, pointState(points[i].state));
109        delegate->printMessage(buffer);
110    }
111}
112
113void printEventDetails(WebTestDelegate* delegate, const WebInputEvent& event)
114{
115    if (WebInputEvent::isTouchEventType(event.type)) {
116        const WebTouchEvent& touch = static_cast<const WebTouchEvent&>(event);
117        printTouchList(delegate, touch.touches, touch.touchesLength);
118        printTouchList(delegate, touch.changedTouches, touch.changedTouchesLength);
119        printTouchList(delegate, touch.targetTouches, touch.targetTouchesLength);
120    } else if (WebInputEvent::isMouseEventType(event.type) || event.type == WebInputEvent::MouseWheel) {
121        const WebMouseEvent& mouse = static_cast<const WebMouseEvent&>(event);
122        char buffer[100];
123        snprintf(buffer, sizeof(buffer), "* %d, %d\n", mouse.x, mouse.y);
124        delegate->printMessage(buffer);
125    } else if (WebInputEvent::isGestureEventType(event.type)) {
126        const WebGestureEvent& gesture = static_cast<const WebGestureEvent&>(event);
127        char buffer[100];
128        snprintf(buffer, sizeof(buffer), "* %d, %d\n", gesture.x, gesture.y);
129        delegate->printMessage(buffer);
130    }
131}
132
133WebPluginContainer::TouchEventRequestType parseTouchEventRequestType(const WebString& string)
134{
135    if (string == WebString::fromUTF8("raw"))
136        return WebPluginContainer::TouchEventRequestTypeRaw;
137    if (string == WebString::fromUTF8("synthetic"))
138        return WebPluginContainer::TouchEventRequestTypeSynthesizedMouse;
139    return WebPluginContainer::TouchEventRequestTypeNone;
140}
141
142void deferredDelete(void* context)
143{
144    TestPlugin* plugin = static_cast<TestPlugin*>(context);
145    delete plugin;
146}
147
148}
149
150
151TestPlugin::TestPlugin(WebFrame* frame, const WebPluginParams& params, WebTestDelegate* delegate)
152    : m_frame(frame)
153    , m_delegate(delegate)
154    , m_container(0)
155    , m_context(0)
156    , m_colorTexture(0)
157    , m_mailboxChanged(false)
158    , m_framebuffer(0)
159    , m_touchEventRequest(WebPluginContainer::TouchEventRequestTypeNone)
160    , m_reRequestTouchEvents(false)
161    , m_printEventDetails(false)
162    , m_printUserGestureStatus(false)
163    , m_canProcessDrag(false)
164{
165    static const WebString kAttributePrimitive = WebString::fromUTF8("primitive");
166    static const WebString kAttributeBackgroundColor = WebString::fromUTF8("background-color");
167    static const WebString kAttributePrimitiveColor = WebString::fromUTF8("primitive-color");
168    static const WebString kAttributeOpacity = WebString::fromUTF8("opacity");
169    static const WebString kAttributeAcceptsTouch = WebString::fromUTF8("accepts-touch");
170    static const WebString kAttributeReRequestTouchEvents = WebString::fromUTF8("re-request-touch");
171    static const WebString kAttributePrintEventDetails = WebString::fromUTF8("print-event-details");
172    static const WebString kAttributeCanProcessDrag = WebString::fromUTF8("can-process-drag");
173    static const WebString kAttributePrintUserGestureStatus = WebString::fromUTF8("print-user-gesture-status");
174
175    WEBKIT_ASSERT(params.attributeNames.size() == params.attributeValues.size());
176    size_t size = params.attributeNames.size();
177    for (size_t i = 0; i < size; ++i) {
178        const WebString& attributeName = params.attributeNames[i];
179        const WebString& attributeValue = params.attributeValues[i];
180
181        if (attributeName == kAttributePrimitive)
182            m_scene.primitive = parsePrimitive(attributeValue);
183        else if (attributeName == kAttributeBackgroundColor)
184            parseColor(attributeValue, m_scene.backgroundColor);
185        else if (attributeName == kAttributePrimitiveColor)
186            parseColor(attributeValue, m_scene.primitiveColor);
187        else if (attributeName == kAttributeOpacity)
188            m_scene.opacity = parseOpacity(attributeValue);
189        else if (attributeName == kAttributeAcceptsTouch)
190            m_touchEventRequest = parseTouchEventRequestType(attributeValue);
191        else if (attributeName == kAttributeReRequestTouchEvents)
192            m_reRequestTouchEvents = parseBoolean(attributeValue);
193        else if (attributeName == kAttributePrintEventDetails)
194            m_printEventDetails = parseBoolean(attributeValue);
195        else if (attributeName == kAttributeCanProcessDrag)
196            m_canProcessDrag = parseBoolean(attributeValue);
197        else if (attributeName == kAttributePrintUserGestureStatus)
198            m_printUserGestureStatus = parseBoolean(attributeValue);
199    }
200}
201
202TestPlugin::~TestPlugin()
203{
204}
205
206bool TestPlugin::initialize(WebPluginContainer* container)
207{
208    WebGraphicsContext3D::Attributes attrs;
209    m_context = Platform::current()->createOffscreenGraphicsContext3D(attrs);
210    if (!m_context)
211        return false;
212
213    if (!m_context->makeContextCurrent())
214        return false;
215
216    if (!initScene())
217        return false;
218
219    m_layer = auto_ptr<WebExternalTextureLayer>(Platform::current()->compositorSupport()->createExternalTextureLayer(this));
220    m_container = container;
221    m_container->setWebLayer(m_layer->layer());
222    if (m_reRequestTouchEvents) {
223        m_container->requestTouchEventType(WebPluginContainer::TouchEventRequestTypeSynthesizedMouse);
224        m_container->requestTouchEventType(WebPluginContainer::TouchEventRequestTypeRaw);
225    }
226    m_container->requestTouchEventType(m_touchEventRequest);
227    m_container->setWantsWheelEvents(true);
228    return true;
229}
230
231void TestPlugin::destroy()
232{
233    if (m_layer.get())
234        m_layer->clearTexture();
235    if (m_container)
236        m_container->setWebLayer(0);
237    m_layer.reset();
238    destroyScene();
239
240    delete m_context;
241    m_context = 0;
242
243    m_container = 0;
244    m_frame = 0;
245
246    Platform::current()->callOnMainThread(deferredDelete, this);
247}
248
249void TestPlugin::updateGeometry(const WebRect& frameRect, const WebRect& clipRect, const WebVector<WebRect>& cutOutsRects, bool isVisible)
250{
251    if (clipRect == m_rect)
252        return;
253    m_rect = clipRect;
254    if (m_rect.isEmpty())
255        return;
256
257    m_context->reshape(m_rect.width, m_rect.height);
258    m_context->viewport(0, 0, m_rect.width, m_rect.height);
259
260    m_context->bindTexture(GL_TEXTURE_2D, m_colorTexture);
261    m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
262    m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
263    m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
264    m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
265    m_context->texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_rect.width, m_rect.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
266    m_context->bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
267    m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorTexture, 0);
268
269    drawScene();
270
271    m_context->genMailboxCHROMIUM(m_mailbox.name);
272    m_context->produceTextureCHROMIUM(GL_TEXTURE_2D, m_mailbox.name);
273
274    m_context->flush();
275    m_layer->layer()->invalidate();
276    m_mailboxChanged = true;
277}
278
279bool TestPlugin::prepareMailbox(WebKit::WebExternalTextureMailbox* mailbox, WebKit::WebExternalBitmap*)
280{
281    if (!m_mailboxChanged)
282        return false;
283    *mailbox = m_mailbox;
284    m_mailboxChanged = false;
285    return true;
286}
287
288void TestPlugin::mailboxReleased(const WebKit::WebExternalTextureMailbox&)
289{
290}
291
292TestPlugin::Primitive TestPlugin::parsePrimitive(const WebString& string)
293{
294    static const WebString kPrimitiveNone = WebString::fromUTF8("none");
295    static const WebString kPrimitiveTriangle = WebString::fromUTF8("triangle");
296
297    Primitive primitive = PrimitiveNone;
298    if (string == kPrimitiveNone)
299        primitive = PrimitiveNone;
300    else if (string == kPrimitiveTriangle)
301        primitive = PrimitiveTriangle;
302    else
303        WEBKIT_ASSERT_NOT_REACHED();
304    return primitive;
305}
306
307// FIXME: This method should already exist. Use it.
308// For now just parse primary colors.
309void TestPlugin::parseColor(const WebString& string, unsigned color[3])
310{
311    color[0] = color[1] = color[2] = 0;
312    if (string == "black")
313        return;
314
315    if (string == "red")
316        color[0] = 255;
317    else if (string == "green")
318        color[1] = 255;
319    else if (string == "blue")
320        color[2] = 255;
321    else
322        WEBKIT_ASSERT_NOT_REACHED();
323}
324
325float TestPlugin::parseOpacity(const WebString& string)
326{
327    return static_cast<float>(atof(string.utf8().data()));
328}
329
330bool TestPlugin::parseBoolean(const WebString& string)
331{
332    static const WebString kPrimitiveTrue = WebString::fromUTF8("true");
333    return string == kPrimitiveTrue;
334}
335
336bool TestPlugin::initScene()
337{
338    float color[4];
339    premultiplyAlpha(m_scene.backgroundColor, m_scene.opacity, color);
340
341    m_colorTexture = m_context->createTexture();
342    m_framebuffer = m_context->createFramebuffer();
343
344    m_context->viewport(0, 0, m_rect.width, m_rect.height);
345    m_context->disable(GL_DEPTH_TEST);
346    m_context->disable(GL_SCISSOR_TEST);
347
348    m_context->clearColor(color[0], color[1], color[2], color[3]);
349
350    m_context->enable(GL_BLEND);
351    m_context->blendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
352
353    return m_scene.primitive != PrimitiveNone ? initProgram() && initPrimitive() : true;
354}
355
356void TestPlugin::drawScene()
357{
358    m_context->viewport(0, 0, m_rect.width, m_rect.height);
359    m_context->clear(GL_COLOR_BUFFER_BIT);
360
361    if (m_scene.primitive != PrimitiveNone)
362        drawPrimitive();
363}
364
365void TestPlugin::destroyScene()
366{
367    if (m_scene.program) {
368        m_context->deleteProgram(m_scene.program);
369        m_scene.program = 0;
370    }
371    if (m_scene.vbo) {
372        m_context->deleteBuffer(m_scene.vbo);
373        m_scene.vbo = 0;
374    }
375
376    if (m_framebuffer) {
377        m_context->deleteFramebuffer(m_framebuffer);
378        m_framebuffer = 0;
379    }
380
381    if (m_colorTexture) {
382        m_context->deleteTexture(m_colorTexture);
383        m_colorTexture = 0;
384    }
385}
386
387bool TestPlugin::initProgram()
388{
389    const string vertexSource(
390        "attribute vec4 position;  \n"
391        "void main() {             \n"
392        "  gl_Position = position; \n"
393        "}                         \n"
394    );
395
396    const string fragmentSource(
397        "precision mediump float; \n"
398        "uniform vec4 color;      \n"
399        "void main() {            \n"
400        "  gl_FragColor = color;  \n"
401        "}                        \n"
402    );
403
404    m_scene.program = loadProgram(vertexSource, fragmentSource);
405    if (!m_scene.program)
406        return false;
407
408    m_scene.colorLocation = m_context->getUniformLocation(m_scene.program, "color");
409    m_scene.positionLocation = m_context->getAttribLocation(m_scene.program, "position");
410    return true;
411}
412
413bool TestPlugin::initPrimitive()
414{
415    WEBKIT_ASSERT(m_scene.primitive == PrimitiveTriangle);
416
417    m_scene.vbo = m_context->createBuffer();
418    if (!m_scene.vbo)
419        return false;
420
421    const float vertices[] = {
422        0.0f,  0.8f, 0.0f,
423        -0.8f, -0.8f, 0.0f,
424        0.8f, -0.8f, 0.0f };
425    m_context->bindBuffer(GL_ARRAY_BUFFER, m_scene.vbo);
426    m_context->bufferData(GL_ARRAY_BUFFER, sizeof(vertices), 0, GL_STATIC_DRAW);
427    m_context->bufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
428    return true;
429}
430
431void TestPlugin::drawPrimitive()
432{
433    WEBKIT_ASSERT(m_scene.primitive == PrimitiveTriangle);
434    WEBKIT_ASSERT(m_scene.vbo);
435    WEBKIT_ASSERT(m_scene.program);
436
437    m_context->useProgram(m_scene.program);
438
439    // Bind primitive color.
440    float color[4];
441    premultiplyAlpha(m_scene.primitiveColor, m_scene.opacity, color);
442    m_context->uniform4f(m_scene.colorLocation, color[0], color[1], color[2], color[3]);
443
444    // Bind primitive vertices.
445    m_context->bindBuffer(GL_ARRAY_BUFFER, m_scene.vbo);
446    m_context->enableVertexAttribArray(m_scene.positionLocation);
447    m_context->vertexAttribPointer(m_scene.positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
448    m_context->drawArrays(GL_TRIANGLES, 0, 3);
449}
450
451unsigned TestPlugin::loadShader(unsigned type, const string& source)
452{
453    unsigned shader = m_context->createShader(type);
454    if (shader) {
455        m_context->shaderSource(shader, source.data());
456        m_context->compileShader(shader);
457
458        int compiled = 0;
459        m_context->getShaderiv(shader, GL_COMPILE_STATUS, &compiled);
460        if (!compiled) {
461            m_context->deleteShader(shader);
462            shader = 0;
463        }
464    }
465    return shader;
466}
467
468unsigned TestPlugin::loadProgram(const string& vertexSource, const string& fragmentSource)
469{
470    unsigned vertexShader = loadShader(GL_VERTEX_SHADER, vertexSource);
471    unsigned fragmentShader = loadShader(GL_FRAGMENT_SHADER, fragmentSource);
472    unsigned program = m_context->createProgram();
473    if (vertexShader && fragmentShader && program) {
474        m_context->attachShader(program, vertexShader);
475        m_context->attachShader(program, fragmentShader);
476        m_context->linkProgram(program);
477
478        int linked = 0;
479        m_context->getProgramiv(program, GL_LINK_STATUS, &linked);
480        if (!linked) {
481            m_context->deleteProgram(program);
482            program = 0;
483        }
484    }
485    if (vertexShader)
486        m_context->deleteShader(vertexShader);
487    if (fragmentShader)
488        m_context->deleteShader(fragmentShader);
489
490    return program;
491}
492
493bool TestPlugin::handleInputEvent(const WebInputEvent& event, WebCursorInfo& info)
494{
495    const char* eventName = 0;
496    switch (event.type) {
497    case WebInputEvent::Undefined:           eventName = "unknown"; break;
498
499    case WebInputEvent::MouseDown:           eventName = "MouseDown"; break;
500    case WebInputEvent::MouseUp:             eventName = "MouseUp"; break;
501    case WebInputEvent::MouseMove:           eventName = "MouseMove"; break;
502    case WebInputEvent::MouseEnter:          eventName = "MouseEnter"; break;
503    case WebInputEvent::MouseLeave:          eventName = "MouseLeave"; break;
504    case WebInputEvent::ContextMenu:         eventName = "ContextMenu"; break;
505
506    case WebInputEvent::MouseWheel:          eventName = "MouseWheel"; break;
507
508    case WebInputEvent::RawKeyDown:          eventName = "RawKeyDown"; break;
509    case WebInputEvent::KeyDown:             eventName = "KeyDown"; break;
510    case WebInputEvent::KeyUp:               eventName = "KeyUp"; break;
511    case WebInputEvent::Char:                eventName = "Char"; break;
512
513    case WebInputEvent::GestureScrollBegin:  eventName = "GestureScrollBegin"; break;
514    case WebInputEvent::GestureScrollEnd:    eventName = "GestureScrollEnd"; break;
515    case WebInputEvent::GestureScrollUpdateWithoutPropagation:
516    case WebInputEvent::GestureScrollUpdate: eventName = "GestureScrollUpdate"; break;
517    case WebInputEvent::GestureFlingStart:   eventName = "GestureFlingStart"; break;
518    case WebInputEvent::GestureFlingCancel:  eventName = "GestureFlingCancel"; break;
519    case WebInputEvent::GestureTap:          eventName = "GestureTap"; break;
520    case WebInputEvent::GestureTapUnconfirmed:
521                                             eventName = "GestureTapUnconfirmed"; break;
522    case WebInputEvent::GestureTapDown:      eventName = "GestureTapDown"; break;
523    case WebInputEvent::GestureTapCancel:    eventName = "GestureTapCancel"; break;
524    case WebInputEvent::GestureDoubleTap:    eventName = "GestureDoubleTap"; break;
525    case WebInputEvent::GestureTwoFingerTap: eventName = "GestureTwoFingerTap"; break;
526    case WebInputEvent::GestureLongPress:    eventName = "GestureLongPress"; break;
527    case WebInputEvent::GestureLongTap:      eventName = "GestureLongTap"; break;
528    case WebInputEvent::GesturePinchBegin:   eventName = "GesturePinchBegin"; break;
529    case WebInputEvent::GesturePinchEnd:     eventName = "GesturePinchEnd"; break;
530    case WebInputEvent::GesturePinchUpdate:  eventName = "GesturePinchUpdate"; break;
531
532    case WebInputEvent::TouchStart:          eventName = "TouchStart"; break;
533    case WebInputEvent::TouchMove:           eventName = "TouchMove"; break;
534    case WebInputEvent::TouchEnd:            eventName = "TouchEnd"; break;
535    case WebInputEvent::TouchCancel:         eventName = "TouchCancel"; break;
536    }
537
538    m_delegate->printMessage(std::string("Plugin received event: ") + (eventName ? eventName : "unknown") + "\n");
539    if (m_printEventDetails)
540        printEventDetails(m_delegate, event);
541    if (m_printUserGestureStatus)
542        m_delegate->printMessage(std::string("* ") + (WebUserGestureIndicator::isProcessingUserGesture() ? "" : "not ") + "handling user gesture\n");
543    return false;
544}
545
546bool TestPlugin::handleDragStatusUpdate(WebDragStatus dragStatus, const WebDragData&, WebDragOperationsMask, const WebPoint& position, const WebPoint& screenPosition)
547{
548    const char* dragStatusName = 0;
549    switch (dragStatus) {
550    case WebDragStatusEnter:
551        dragStatusName = "DragEnter";
552        break;
553    case WebDragStatusOver:
554        dragStatusName = "DragOver";
555        break;
556    case WebDragStatusLeave:
557        dragStatusName = "DragLeave";
558        break;
559    case WebDragStatusDrop:
560        dragStatusName = "DragDrop";
561        break;
562    case WebDragStatusUnknown:
563        WEBKIT_ASSERT_NOT_REACHED();
564    }
565    m_delegate->printMessage(std::string("Plugin received event: ") + dragStatusName + "\n");
566    return false;
567}
568
569TestPlugin* TestPlugin::create(WebFrame* frame, const WebPluginParams& params, WebTestDelegate* delegate)
570{
571    return new TestPlugin(frame, params, delegate);
572}
573
574const WebString& TestPlugin::mimeType()
575{
576    static const WebString kMimeType = WebString::fromUTF8("application/x-webkit-test-webplugin");
577    return kMimeType;
578}
579
580}
581