1/* ***** BEGIN LICENSE BLOCK *****
2 *
3 * Copyright (c) 2008, Mozilla Corporation
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * * Redistributions of source code must retain the above copyright notice, this
10 *   list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright notice,
12 *   this list of conditions and the following disclaimer in the documentation
13 *   and/or other materials provided with the distribution.
14 * * Neither the name of the Mozilla Corporation nor the names of its
15 *   contributors may be used to endorse or promote products derived from this
16 *   software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
22 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 * Contributor(s):
30 *   Josh Aas <josh@mozilla.com>
31 *
32 * ***** END LICENSE BLOCK ***** */
33
34#include "nptest_platform.h"
35#include "nsAlgorithm.h"
36#include <CoreServices/CoreServices.h>
37#include <algorithm>
38
39using namespace std;
40
41bool
42pluginSupportsWindowMode()
43{
44  return false;
45}
46
47bool
48pluginSupportsWindowlessMode()
49{
50  return true;
51}
52
53NPError
54pluginInstanceInit(InstanceData* instanceData)
55{
56  NPP npp = instanceData->npp;
57
58  NPBool supportsCoreGraphics = false;
59  if ((NPN_GetValue(npp, NPNVsupportsCoreGraphicsBool, &supportsCoreGraphics) == NPERR_NO_ERROR) &&
60      supportsCoreGraphics) {
61    NPN_SetValue(npp, NPPVpluginDrawingModel, (void*)NPDrawingModelCoreGraphics);
62  } else {
63    printf("CoreGraphics drawing model not supported, can't create a plugin instance.\n");
64    return NPERR_INCOMPATIBLE_VERSION_ERROR;
65  }
66
67  NPBool supportsCocoaEvents = false;
68  if ((NPN_GetValue(npp, NPNVsupportsCocoaBool, &supportsCocoaEvents) == NPERR_NO_ERROR) &&
69      supportsCocoaEvents) {
70    NPN_SetValue(npp, NPPVpluginEventModel, (void*)NPEventModelCocoa);
71    instanceData->eventModel = NPEventModelCocoa;
72  } else {
73    printf("Cocoa event model not supported, can't create a plugin instance.\n");
74    return NPERR_INCOMPATIBLE_VERSION_ERROR;
75  }
76
77  return NPERR_NO_ERROR;
78}
79
80void
81pluginInstanceShutdown(InstanceData* instanceData)
82{
83}
84
85static bool
86RectEquals(const NPRect& r1, const NPRect& r2)
87{
88  return r1.left == r2.left && r1.top == r2.top &&
89         r1.right == r2.right && r1.bottom == r2.bottom;
90}
91
92void
93pluginDoSetWindow(InstanceData* instanceData, NPWindow* newWindow)
94{
95  // Ugh. Due to a terrible Gecko bug, we have to ignore position changes
96  // when the clip rect doesn't change; the position can be wrong
97  // when set by a path other than nsPluginFrame::FixUpPluginWindow.
98  int32_t oldX = instanceData->window.x;
99  int32_t oldY = instanceData->window.y;
100  bool clipChanged =
101    !RectEquals(instanceData->window.clipRect, newWindow->clipRect);
102  instanceData->window = *newWindow;
103  if (!clipChanged) {
104    instanceData->window.x = oldX;
105    instanceData->window.y = oldY;
106  }
107}
108
109void
110pluginWidgetInit(InstanceData* instanceData, void* oldWindow)
111{
112  // Should never be called since we don't support window mode
113}
114
115static void
116GetColorsFromRGBA(uint32_t rgba, float* r, float* g, float* b, float* a)
117{
118  *b = (rgba & 0xFF) / 255.0;
119  *g = ((rgba & 0xFF00) >> 8) / 255.0;
120  *r = ((rgba & 0xFF0000) >> 16) / 255.0;
121  *a = ((rgba & 0xFF000000) >> 24) / 255.0;
122}
123
124static void
125pluginDraw(InstanceData* instanceData, NPCocoaEvent* event)
126{
127  if (!instanceData)
128    return;
129
130  notifyDidPaint(instanceData);
131
132  NPP npp = instanceData->npp;
133  if (!npp)
134    return;
135
136  const char* uaString = NPN_UserAgent(npp);
137  if (!uaString)
138    return;
139
140  NPWindow window = instanceData->window;
141
142  CGContextRef cgContext = event->data.draw.context;
143
144  float windowWidth = window.width;
145  float windowHeight = window.height;
146
147  switch(instanceData->scriptableObject->drawMode) {
148  case DM_DEFAULT: {
149    CFStringRef uaCFString = CFStringCreateWithCString(kCFAllocatorDefault, uaString, kCFStringEncodingASCII);
150    // save the cgcontext gstate
151    CGContextSaveGState(cgContext);
152
153    // we get a flipped context
154    CGContextTranslateCTM(cgContext, 0.0, windowHeight);
155    CGContextScaleCTM(cgContext, 1.0, -1.0);
156
157    // draw a gray background for the plugin
158    CGContextAddRect(cgContext, CGRectMake(0, 0, windowWidth, windowHeight));
159    CGContextSetGrayFillColor(cgContext, 0.5, 1.0);
160    CGContextDrawPath(cgContext, kCGPathFill);
161
162    // draw a black frame around the plugin
163    CGContextAddRect(cgContext, CGRectMake(0, 0, windowWidth, windowHeight));
164    CGContextSetGrayStrokeColor(cgContext, 0.0, 1.0);
165    CGContextSetLineWidth(cgContext, 6.0);
166    CGContextStrokePath(cgContext);
167
168    // draw the UA string using Core Text
169    CGContextSetTextMatrix(cgContext, CGAffineTransformIdentity);
170
171    // Initialize a rectangular path.
172    CGMutablePathRef path = CGPathCreateMutable();
173    CGRect bounds = CGRectMake(10.0, 10.0, std::max(0.0, windowWidth - 20.0),
174                               std::max(0.0, windowHeight - 20.0));
175    CGPathAddRect(path, NULL, bounds);
176
177    // Initialize an attributed string.
178    CFMutableAttributedStringRef attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
179    CFAttributedStringReplaceString(attrString, CFRangeMake(0, 0), uaCFString);
180
181    // Create a color and add it as an attribute to the string.
182    CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
183    CGFloat components[] = { 0.0, 0.0, 0.0, 1.0 };
184    CGColorRef red = CGColorCreate(rgbColorSpace, components);
185    CGColorSpaceRelease(rgbColorSpace);
186    CFAttributedStringSetAttribute(attrString, CFRangeMake(0, 50), kCTForegroundColorAttributeName, red);
187
188    // Create the framesetter with the attributed string.
189    CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString);
190    CFRelease(attrString);
191
192    // Create the frame and draw it into the graphics context
193    CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL);
194    CFRelease(framesetter);
195    if (frame) {
196      CTFrameDraw(frame, cgContext);
197      CFRelease(frame);
198    }
199
200    // restore the cgcontext gstate
201    CGContextRestoreGState(cgContext);
202    break;
203  }
204  case DM_SOLID_COLOR: {
205    // save the cgcontext gstate
206    CGContextSaveGState(cgContext);
207
208    // we get a flipped context
209    CGContextTranslateCTM(cgContext, 0.0, windowHeight);
210    CGContextScaleCTM(cgContext, 1.0, -1.0);
211
212    // draw a solid background for the plugin
213    CGContextAddRect(cgContext, CGRectMake(0, 0, windowWidth, windowHeight));
214
215    float r,g,b,a;
216    GetColorsFromRGBA(instanceData->scriptableObject->drawColor, &r, &g, &b, &a);
217    CGContextSetRGBFillColor(cgContext, r, g, b, a);
218    CGContextDrawPath(cgContext, kCGPathFill);
219
220    // restore the cgcontext gstate
221    CGContextRestoreGState(cgContext);
222    break;
223  }
224  }
225}
226
227int16_t
228pluginHandleEvent(InstanceData* instanceData, void* event)
229{
230  NPCocoaEvent* cocoaEvent = (NPCocoaEvent*)event;
231  if (!cocoaEvent)
232    return kNPEventNotHandled;
233
234  switch (cocoaEvent->type) {
235    case NPCocoaEventDrawRect:
236      pluginDraw(instanceData, cocoaEvent);
237      break;
238    case NPCocoaEventMouseDown:
239    case NPCocoaEventMouseUp:
240    case NPCocoaEventMouseMoved:
241    case NPCocoaEventMouseDragged:
242      instanceData->lastMouseX = (int32_t)cocoaEvent->data.mouse.pluginX;
243      instanceData->lastMouseY = (int32_t)cocoaEvent->data.mouse.pluginY;
244      if (cocoaEvent->type == NPCocoaEventMouseUp) {
245        instanceData->mouseUpEventCount++;
246      }
247      break;
248    case NPCocoaEventWindowFocusChanged:
249      instanceData->topLevelWindowActivationState = cocoaEvent->data.focus.hasFocus ?
250        ACTIVATION_STATE_ACTIVATED : ACTIVATION_STATE_DEACTIVATED;
251      instanceData->topLevelWindowActivationEventCount = instanceData->topLevelWindowActivationEventCount + 1;
252      break;
253    case NPCocoaEventFocusChanged:
254      instanceData->focusState = cocoaEvent->data.focus.hasFocus ?
255      ACTIVATION_STATE_ACTIVATED : ACTIVATION_STATE_DEACTIVATED;
256      instanceData->focusEventCount = instanceData->focusEventCount + 1;
257      break;
258    default:
259      return kNPEventNotHandled;
260  }
261
262  return kNPEventHandled;
263}
264
265int32_t pluginGetEdge(InstanceData* instanceData, RectEdge edge)
266{
267  NPWindow* w = &instanceData->window;
268  switch (edge) {
269  case EDGE_LEFT:
270    return w->x;
271  case EDGE_TOP:
272    return w->y;
273  case EDGE_RIGHT:
274    return w->x + w->width;
275  case EDGE_BOTTOM:
276    return w->y + w->height;
277  }
278  MOZ_CRASH("Unexpected RectEdge?!");
279}
280
281int32_t pluginGetClipRegionRectCount(InstanceData* instanceData)
282{
283  return 1;
284}
285
286int32_t pluginGetClipRegionRectEdge(InstanceData* instanceData,
287    int32_t rectIndex, RectEdge edge)
288{
289  if (rectIndex != 0)
290    return NPTEST_INT32_ERROR;
291
292  // We have to add the Cocoa titlebar height here since the clip rect
293  // is being returned relative to that
294  static const int COCOA_TITLEBAR_HEIGHT = 22;
295
296  NPWindow* w = &instanceData->window;
297  switch (edge) {
298  case EDGE_LEFT:
299    return w->clipRect.left;
300  case EDGE_TOP:
301    return w->clipRect.top + COCOA_TITLEBAR_HEIGHT;
302  case EDGE_RIGHT:
303    return w->clipRect.right;
304  case EDGE_BOTTOM:
305    return w->clipRect.bottom + COCOA_TITLEBAR_HEIGHT;
306  }
307  MOZ_CRASH("Unexpected RectEdge?!");
308}
309
310void pluginDoInternalConsistencyCheck(InstanceData* instanceData, string& error)
311{
312}
313