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.
5 #include "content/shell/renderer/test_runner/TestPlugin.h"
7 #include "base/basictypes.h"
8 #include "content/shell/renderer/test_runner/TestCommon.h"
9 #include "content/shell/renderer/test_runner/WebTestDelegate.h"
10 #include "third_party/WebKit/public/platform/Platform.h"
11 #include "third_party/WebKit/public/platform/WebCompositorSupport.h"
12 #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
13 #include "third_party/WebKit/public/web/WebFrame.h"
14 #include "third_party/WebKit/public/web/WebInputEvent.h"
15 #include "third_party/WebKit/public/web/WebKit.h"
16 #include "third_party/WebKit/public/web/WebPluginParams.h"
17 #include "third_party/WebKit/public/web/WebTouchPoint.h"
18 #include "third_party/WebKit/public/web/WebUserGestureIndicator.h"
20 using namespace blink;
23 namespace WebTestRunner {
27 // GLenum values copied from gl2.h.
31 #define GL_TRIANGLES 0x0004
32 #define GL_ONE_MINUS_SRC_ALPHA 0x0303
33 #define GL_DEPTH_TEST 0x0B71
34 #define GL_BLEND 0x0BE2
35 #define GL_SCISSOR_TEST 0x0B90
36 #define GL_TEXTURE_2D 0x0DE1
37 #define GL_FLOAT 0x1406
38 #define GL_RGBA 0x1908
39 #define GL_UNSIGNED_BYTE 0x1401
40 #define GL_TEXTURE_MAG_FILTER 0x2800
41 #define GL_TEXTURE_MIN_FILTER 0x2801
42 #define GL_TEXTURE_WRAP_S 0x2802
43 #define GL_TEXTURE_WRAP_T 0x2803
44 #define GL_NEAREST 0x2600
45 #define GL_COLOR_BUFFER_BIT 0x4000
46 #define GL_CLAMP_TO_EDGE 0x812F
47 #define GL_ARRAY_BUFFER 0x8892
48 #define GL_STATIC_DRAW 0x88E4
49 #define GL_FRAGMENT_SHADER 0x8B30
50 #define GL_VERTEX_SHADER 0x8B31
51 #define GL_COMPILE_STATUS 0x8B81
52 #define GL_LINK_STATUS 0x8B82
53 #define GL_COLOR_ATTACHMENT0 0x8CE0
54 #define GL_FRAMEBUFFER_COMPLETE 0x8CD5
55 #define GL_FRAMEBUFFER 0x8D40
57 void premultiplyAlpha(const unsigned colorIn[3], float alpha, float colorOut[4])
59 for (int i = 0; i < 3; ++i)
60 colorOut[i] = (colorIn[i] / 255.0f) * alpha;
65 const char* pointState(WebTouchPoint::State state)
68 case WebTouchPoint::StateReleased:
70 case WebTouchPoint::StatePressed:
72 case WebTouchPoint::StateMoved:
74 case WebTouchPoint::StateCancelled:
80 BLINK_ASSERT_NOT_REACHED();
84 void printTouchList(WebTestDelegate* delegate, const WebTouchPoint* points, int length)
86 for (int i = 0; i < length; ++i) {
88 snprintf(buffer, sizeof(buffer), "* %d, %d: %s\n", static_cast<int>(points[i].position.x), static_cast<int>(points[i].position.y), pointState(points[i].state));
89 delegate->printMessage(buffer);
93 void printEventDetails(WebTestDelegate* delegate, const WebInputEvent& event)
95 if (WebInputEvent::isTouchEventType(event.type)) {
96 const WebTouchEvent& touch = static_cast<const WebTouchEvent&>(event);
97 printTouchList(delegate, touch.touches, touch.touchesLength);
98 printTouchList(delegate, touch.changedTouches, touch.changedTouchesLength);
99 printTouchList(delegate, touch.targetTouches, touch.targetTouchesLength);
100 } else if (WebInputEvent::isMouseEventType(event.type) || event.type == WebInputEvent::MouseWheel) {
101 const WebMouseEvent& mouse = static_cast<const WebMouseEvent&>(event);
103 snprintf(buffer, sizeof(buffer), "* %d, %d\n", mouse.x, mouse.y);
104 delegate->printMessage(buffer);
105 } else if (WebInputEvent::isGestureEventType(event.type)) {
106 const WebGestureEvent& gesture = static_cast<const WebGestureEvent&>(event);
108 snprintf(buffer, sizeof(buffer), "* %d, %d\n", gesture.x, gesture.y);
109 delegate->printMessage(buffer);
113 WebPluginContainer::TouchEventRequestType parseTouchEventRequestType(const WebString& string)
115 if (string == WebString::fromUTF8("raw"))
116 return WebPluginContainer::TouchEventRequestTypeRaw;
117 if (string == WebString::fromUTF8("synthetic"))
118 return WebPluginContainer::TouchEventRequestTypeSynthesizedMouse;
119 return WebPluginContainer::TouchEventRequestTypeNone;
122 void deferredDelete(void* context)
124 TestPlugin* plugin = static_cast<TestPlugin*>(context);
130 TestPlugin::TestPlugin(WebFrame* frame, const WebPluginParams& params, WebTestDelegate* delegate)
132 , m_delegate(delegate)
136 , m_mailboxChanged(false)
138 , m_touchEventRequest(WebPluginContainer::TouchEventRequestTypeNone)
139 , m_reRequestTouchEvents(false)
140 , m_printEventDetails(false)
141 , m_printUserGestureStatus(false)
142 , m_canProcessDrag(false)
143 , m_isPersistent(params.mimeType == pluginPersistsMimeType())
144 , m_canCreateWithoutRenderer(params.mimeType == canCreateWithoutRendererMimeType())
146 const CR_DEFINE_STATIC_LOCAL(WebString, kAttributePrimitive, ("primitive"));
147 const CR_DEFINE_STATIC_LOCAL(WebString, kAttributeBackgroundColor, ("background-color"));
148 const CR_DEFINE_STATIC_LOCAL(WebString, kAttributePrimitiveColor, ("primitive-color"));
149 const CR_DEFINE_STATIC_LOCAL(WebString, kAttributeOpacity, ("opacity"));
150 const CR_DEFINE_STATIC_LOCAL(WebString, kAttributeAcceptsTouch, ("accepts-touch"));
151 const CR_DEFINE_STATIC_LOCAL(WebString, kAttributeReRequestTouchEvents, ("re-request-touch"));
152 const CR_DEFINE_STATIC_LOCAL(WebString, kAttributePrintEventDetails, ("print-event-details"));
153 const CR_DEFINE_STATIC_LOCAL(WebString, kAttributeCanProcessDrag, ("can-process-drag"));
154 const CR_DEFINE_STATIC_LOCAL(WebString, kAttributePrintUserGestureStatus, ("print-user-gesture-status"));
156 BLINK_ASSERT(params.attributeNames.size() == params.attributeValues.size());
157 size_t size = params.attributeNames.size();
158 for (size_t i = 0; i < size; ++i) {
159 const WebString& attributeName = params.attributeNames[i];
160 const WebString& attributeValue = params.attributeValues[i];
162 if (attributeName == kAttributePrimitive)
163 m_scene.primitive = parsePrimitive(attributeValue);
164 else if (attributeName == kAttributeBackgroundColor)
165 parseColor(attributeValue, m_scene.backgroundColor);
166 else if (attributeName == kAttributePrimitiveColor)
167 parseColor(attributeValue, m_scene.primitiveColor);
168 else if (attributeName == kAttributeOpacity)
169 m_scene.opacity = parseOpacity(attributeValue);
170 else if (attributeName == kAttributeAcceptsTouch)
171 m_touchEventRequest = parseTouchEventRequestType(attributeValue);
172 else if (attributeName == kAttributeReRequestTouchEvents)
173 m_reRequestTouchEvents = parseBoolean(attributeValue);
174 else if (attributeName == kAttributePrintEventDetails)
175 m_printEventDetails = parseBoolean(attributeValue);
176 else if (attributeName == kAttributeCanProcessDrag)
177 m_canProcessDrag = parseBoolean(attributeValue);
178 else if (attributeName == kAttributePrintUserGestureStatus)
179 m_printUserGestureStatus = parseBoolean(attributeValue);
181 if (m_canCreateWithoutRenderer)
182 m_delegate->printMessage(std::string("TestPlugin: canCreateWithoutRenderer\n"));
185 TestPlugin::~TestPlugin()
189 bool TestPlugin::initialize(WebPluginContainer* container)
191 WebGraphicsContext3D::Attributes attrs;
192 m_context = Platform::current()->createOffscreenGraphicsContext3D(attrs);
196 if (!m_context->makeContextCurrent())
202 m_layer = scoped_ptr<WebExternalTextureLayer>(Platform::current()->compositorSupport()->createExternalTextureLayer(this));
203 m_container = container;
204 m_container->setWebLayer(m_layer->layer());
205 if (m_reRequestTouchEvents) {
206 m_container->requestTouchEventType(WebPluginContainer::TouchEventRequestTypeSynthesizedMouse);
207 m_container->requestTouchEventType(WebPluginContainer::TouchEventRequestTypeRaw);
209 m_container->requestTouchEventType(m_touchEventRequest);
210 m_container->setWantsWheelEvents(true);
214 void TestPlugin::destroy()
217 m_layer->clearTexture();
219 m_container->setWebLayer(0);
229 Platform::current()->callOnMainThread(deferredDelete, this);
232 NPObject* TestPlugin::scriptableObject()
237 bool TestPlugin::canProcessDrag() const
239 return m_canProcessDrag;
242 void TestPlugin::updateGeometry(const WebRect& frameRect, const WebRect& clipRect, const WebVector<WebRect>& cutOutsRects, bool isVisible)
244 if (clipRect == m_rect)
247 if (m_rect.isEmpty())
250 m_context->viewport(0, 0, m_rect.width, m_rect.height);
252 m_context->bindTexture(GL_TEXTURE_2D, m_colorTexture);
253 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
254 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
255 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
256 m_context->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
257 m_context->texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_rect.width, m_rect.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
258 m_context->bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
259 m_context->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorTexture, 0);
263 m_context->genMailboxCHROMIUM(m_mailbox.name);
264 m_context->produceTextureCHROMIUM(GL_TEXTURE_2D, m_mailbox.name);
267 m_layer->layer()->invalidate();
268 m_mailboxChanged = true;
271 bool TestPlugin::acceptsInputEvents()
276 bool TestPlugin::isPlaceholder()
281 blink::WebGraphicsContext3D* TestPlugin::context()
286 bool TestPlugin::prepareMailbox(blink::WebExternalTextureMailbox* mailbox, blink::WebExternalBitmap*)
288 if (!m_mailboxChanged)
290 *mailbox = m_mailbox;
291 m_mailboxChanged = false;
295 void TestPlugin::mailboxReleased(const blink::WebExternalTextureMailbox&)
299 TestPlugin::Primitive TestPlugin::parsePrimitive(const WebString& string)
301 const CR_DEFINE_STATIC_LOCAL(WebString, kPrimitiveNone, ("none"));
302 const CR_DEFINE_STATIC_LOCAL(WebString, kPrimitiveTriangle, ("triangle"));
304 Primitive primitive = PrimitiveNone;
305 if (string == kPrimitiveNone)
306 primitive = PrimitiveNone;
307 else if (string == kPrimitiveTriangle)
308 primitive = PrimitiveTriangle;
310 BLINK_ASSERT_NOT_REACHED();
314 // FIXME: This method should already exist. Use it.
315 // For now just parse primary colors.
316 void TestPlugin::parseColor(const WebString& string, unsigned color[3])
318 color[0] = color[1] = color[2] = 0;
319 if (string == "black")
324 else if (string == "green")
326 else if (string == "blue")
329 BLINK_ASSERT_NOT_REACHED();
332 float TestPlugin::parseOpacity(const WebString& string)
334 return static_cast<float>(atof(string.utf8().data()));
337 bool TestPlugin::parseBoolean(const WebString& string)
339 const CR_DEFINE_STATIC_LOCAL(WebString, kPrimitiveTrue, ("true"));
340 return string == kPrimitiveTrue;
343 bool TestPlugin::initScene()
346 premultiplyAlpha(m_scene.backgroundColor, m_scene.opacity, color);
348 m_colorTexture = m_context->createTexture();
349 m_framebuffer = m_context->createFramebuffer();
351 m_context->viewport(0, 0, m_rect.width, m_rect.height);
352 m_context->disable(GL_DEPTH_TEST);
353 m_context->disable(GL_SCISSOR_TEST);
355 m_context->clearColor(color[0], color[1], color[2], color[3]);
357 m_context->enable(GL_BLEND);
358 m_context->blendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
360 return m_scene.primitive != PrimitiveNone ? initProgram() && initPrimitive() : true;
363 void TestPlugin::drawScene()
365 m_context->viewport(0, 0, m_rect.width, m_rect.height);
366 m_context->clear(GL_COLOR_BUFFER_BIT);
368 if (m_scene.primitive != PrimitiveNone)
372 void TestPlugin::destroyScene()
374 if (m_scene.program) {
375 m_context->deleteProgram(m_scene.program);
379 m_context->deleteBuffer(m_scene.vbo);
384 m_context->deleteFramebuffer(m_framebuffer);
388 if (m_colorTexture) {
389 m_context->deleteTexture(m_colorTexture);
394 bool TestPlugin::initProgram()
396 const string vertexSource(
397 "attribute vec4 position; \n"
399 " gl_Position = position; \n"
403 const string fragmentSource(
404 "precision mediump float; \n"
405 "uniform vec4 color; \n"
407 " gl_FragColor = color; \n"
411 m_scene.program = loadProgram(vertexSource, fragmentSource);
412 if (!m_scene.program)
415 m_scene.colorLocation = m_context->getUniformLocation(m_scene.program, "color");
416 m_scene.positionLocation = m_context->getAttribLocation(m_scene.program, "position");
420 bool TestPlugin::initPrimitive()
422 BLINK_ASSERT(m_scene.primitive == PrimitiveTriangle);
424 m_scene.vbo = m_context->createBuffer();
428 const float vertices[] = {
432 m_context->bindBuffer(GL_ARRAY_BUFFER, m_scene.vbo);
433 m_context->bufferData(GL_ARRAY_BUFFER, sizeof(vertices), 0, GL_STATIC_DRAW);
434 m_context->bufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
438 void TestPlugin::drawPrimitive()
440 BLINK_ASSERT(m_scene.primitive == PrimitiveTriangle);
441 BLINK_ASSERT(m_scene.vbo);
442 BLINK_ASSERT(m_scene.program);
444 m_context->useProgram(m_scene.program);
446 // Bind primitive color.
448 premultiplyAlpha(m_scene.primitiveColor, m_scene.opacity, color);
449 m_context->uniform4f(m_scene.colorLocation, color[0], color[1], color[2], color[3]);
451 // Bind primitive vertices.
452 m_context->bindBuffer(GL_ARRAY_BUFFER, m_scene.vbo);
453 m_context->enableVertexAttribArray(m_scene.positionLocation);
454 m_context->vertexAttribPointer(m_scene.positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
455 m_context->drawArrays(GL_TRIANGLES, 0, 3);
458 unsigned TestPlugin::loadShader(unsigned type, const string& source)
460 unsigned shader = m_context->createShader(type);
462 m_context->shaderSource(shader, source.data());
463 m_context->compileShader(shader);
466 m_context->getShaderiv(shader, GL_COMPILE_STATUS, &compiled);
468 m_context->deleteShader(shader);
475 unsigned TestPlugin::loadProgram(const string& vertexSource, const string& fragmentSource)
477 unsigned vertexShader = loadShader(GL_VERTEX_SHADER, vertexSource);
478 unsigned fragmentShader = loadShader(GL_FRAGMENT_SHADER, fragmentSource);
479 unsigned program = m_context->createProgram();
480 if (vertexShader && fragmentShader && program) {
481 m_context->attachShader(program, vertexShader);
482 m_context->attachShader(program, fragmentShader);
483 m_context->linkProgram(program);
486 m_context->getProgramiv(program, GL_LINK_STATUS, &linked);
488 m_context->deleteProgram(program);
493 m_context->deleteShader(vertexShader);
495 m_context->deleteShader(fragmentShader);
500 bool TestPlugin::handleInputEvent(const WebInputEvent& event, WebCursorInfo& info)
502 const char* eventName = 0;
503 switch (event.type) {
504 case WebInputEvent::Undefined: eventName = "unknown"; break;
506 case WebInputEvent::MouseDown: eventName = "MouseDown"; break;
507 case WebInputEvent::MouseUp: eventName = "MouseUp"; break;
508 case WebInputEvent::MouseMove: eventName = "MouseMove"; break;
509 case WebInputEvent::MouseEnter: eventName = "MouseEnter"; break;
510 case WebInputEvent::MouseLeave: eventName = "MouseLeave"; break;
511 case WebInputEvent::ContextMenu: eventName = "ContextMenu"; break;
513 case WebInputEvent::MouseWheel: eventName = "MouseWheel"; break;
515 case WebInputEvent::RawKeyDown: eventName = "RawKeyDown"; break;
516 case WebInputEvent::KeyDown: eventName = "KeyDown"; break;
517 case WebInputEvent::KeyUp: eventName = "KeyUp"; break;
518 case WebInputEvent::Char: eventName = "Char"; break;
520 case WebInputEvent::GestureScrollBegin: eventName = "GestureScrollBegin"; break;
521 case WebInputEvent::GestureScrollEnd: eventName = "GestureScrollEnd"; break;
522 case WebInputEvent::GestureScrollUpdateWithoutPropagation:
523 case WebInputEvent::GestureScrollUpdate: eventName = "GestureScrollUpdate"; break;
524 case WebInputEvent::GestureFlingStart: eventName = "GestureFlingStart"; break;
525 case WebInputEvent::GestureFlingCancel: eventName = "GestureFlingCancel"; break;
526 case WebInputEvent::GestureTap: eventName = "GestureTap"; break;
527 case WebInputEvent::GestureTapUnconfirmed:
528 eventName = "GestureTapUnconfirmed"; break;
529 case WebInputEvent::GestureTapDown: eventName = "GestureTapDown"; break;
530 case WebInputEvent::GestureShowPress: eventName = "GestureShowPress"; break;
531 case WebInputEvent::GestureTapCancel: eventName = "GestureTapCancel"; break;
532 case WebInputEvent::GestureDoubleTap: eventName = "GestureDoubleTap"; break;
533 case WebInputEvent::GestureTwoFingerTap: eventName = "GestureTwoFingerTap"; break;
534 case WebInputEvent::GestureLongPress: eventName = "GestureLongPress"; break;
535 case WebInputEvent::GestureLongTap: eventName = "GestureLongTap"; break;
536 case WebInputEvent::GesturePinchBegin: eventName = "GesturePinchBegin"; break;
537 case WebInputEvent::GesturePinchEnd: eventName = "GesturePinchEnd"; break;
538 case WebInputEvent::GesturePinchUpdate: eventName = "GesturePinchUpdate"; break;
540 case WebInputEvent::TouchStart: eventName = "TouchStart"; break;
541 case WebInputEvent::TouchMove: eventName = "TouchMove"; break;
542 case WebInputEvent::TouchEnd: eventName = "TouchEnd"; break;
543 case WebInputEvent::TouchCancel: eventName = "TouchCancel"; break;
546 m_delegate->printMessage(std::string("Plugin received event: ") + (eventName ? eventName : "unknown") + "\n");
547 if (m_printEventDetails)
548 printEventDetails(m_delegate, event);
549 if (m_printUserGestureStatus)
550 m_delegate->printMessage(std::string("* ") + (WebUserGestureIndicator::isProcessingUserGesture() ? "" : "not ") + "handling user gesture\n");
552 m_delegate->printMessage(std::string("TestPlugin: isPersistent\n"));
556 bool TestPlugin::handleDragStatusUpdate(WebDragStatus dragStatus, const WebDragData&, WebDragOperationsMask, const WebPoint& position, const WebPoint& screenPosition)
558 const char* dragStatusName = 0;
559 switch (dragStatus) {
560 case WebDragStatusEnter:
561 dragStatusName = "DragEnter";
563 case WebDragStatusOver:
564 dragStatusName = "DragOver";
566 case WebDragStatusLeave:
567 dragStatusName = "DragLeave";
569 case WebDragStatusDrop:
570 dragStatusName = "DragDrop";
572 case WebDragStatusUnknown:
573 BLINK_ASSERT_NOT_REACHED();
575 m_delegate->printMessage(std::string("Plugin received event: ") + dragStatusName + "\n");
579 TestPlugin* TestPlugin::create(WebFrame* frame, const WebPluginParams& params, WebTestDelegate* delegate)
581 return new TestPlugin(frame, params, delegate);
584 const WebString& TestPlugin::mimeType()
586 const CR_DEFINE_STATIC_LOCAL(WebString, kMimeType, ("application/x-webkit-test-webplugin"));
590 const WebString& TestPlugin::canCreateWithoutRendererMimeType()
592 const CR_DEFINE_STATIC_LOCAL(WebString, kCanCreateWithoutRendererMimeType, ("application/x-webkit-test-webplugin-can-create-without-renderer"));
593 return kCanCreateWithoutRendererMimeType;
596 const WebString& TestPlugin::pluginPersistsMimeType()
598 const CR_DEFINE_STATIC_LOCAL(WebString, kPluginPersistsMimeType, ("application/x-webkit-test-webplugin-persistent"));
599 return kPluginPersistsMimeType;
602 bool TestPlugin::isSupportedMimeType(const WebString& mimeType)
604 return mimeType == TestPlugin::mimeType()
605 || mimeType == pluginPersistsMimeType()
606 || mimeType == canCreateWithoutRendererMimeType();