1/* 2 IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in 3 consideration of your agreement to the following terms, and your use, installation, 4 modification or redistribution of this Apple software constitutes acceptance of these 5 terms. If you do not agree with these terms, please do not use, install, modify or 6 redistribute this Apple software. 7 8 In consideration of your agreement to abide by the following terms, and subject to these 9 terms, Apple grants you a personal, non-exclusive license, under Apple’s copyrights in 10 this original Apple software (the "Apple Software"), to use, reproduce, modify and 11 redistribute the Apple Software, with or without modifications, in source and/or binary 12 forms; provided that if you redistribute the Apple Software in its entirety and without 13 modifications, you must retain this notice and the following text and disclaimers in all 14 such redistributions of the Apple Software. Neither the name, trademarks, service marks 15 or logos of Apple Computer, Inc. may be used to endorse or promote products derived from 16 the Apple Software without specific prior written permission from Apple. Except as expressly 17 stated in this notice, no other rights or licenses, express or implied, are granted by Apple 18 herein, including but not limited to any patent rights that may be infringed by your 19 derivative works or by other works in which the Apple Software may be incorporated. 20 21 The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES, 22 EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, 23 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS 24 USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 25 26 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL 27 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 28 OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, 29 REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND 30 WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR 31 OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34#import <WebKit/npapi.h> 35#import <WebKit/npfunctions.h> 36#import <WebKit/npruntime.h> 37 38#import <Cocoa/Cocoa.h> 39 40// Browser function table 41static NPNetscapeFuncs* browser; 42 43// Structure for per-instance storage 44typedef struct PluginObject 45{ 46 NPP npp; 47 48 NPWindow window; 49 50 bool pluginHasFocus; 51 52 bool textFieldHasFocus; 53 NSRect textFieldRect; 54 55 NSRange selectedRange; 56 NSTextStorage *textStorage; 57 NSLayoutManager *layoutManager; 58 NSTextContainer *textContainer; 59 60} PluginObject; 61 62NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, char* argn[], char* argv[], NPSavedData* saved); 63NPError NPP_Destroy(NPP instance, NPSavedData** save); 64NPError NPP_SetWindow(NPP instance, NPWindow* window); 65NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype); 66NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason); 67int32 NPP_WriteReady(NPP instance, NPStream* stream); 68int32 NPP_Write(NPP instance, NPStream* stream, int32 offset, int32 len, void* buffer); 69void NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname); 70void NPP_Print(NPP instance, NPPrint* platformPrint); 71int16 NPP_HandleEvent(NPP instance, void* event); 72void NPP_URLNotify(NPP instance, const char* URL, NPReason reason, void* notifyData); 73NPError NPP_GetValue(NPP instance, NPPVariable variable, void* value); 74NPError NPP_SetValue(NPP instance, NPNVariable variable, void* value); 75 76#pragma export on 77// Mach-o entry points 78NPError NP_Initialize(NPNetscapeFuncs* browserFuncs); 79NPError NP_GetEntryPoints(NPPluginFuncs* pluginFuncs); 80void NP_Shutdown(void); 81#pragma export off 82 83NPError NP_Initialize(NPNetscapeFuncs* browserFuncs) 84{ 85 browser = browserFuncs; 86 return NPERR_NO_ERROR; 87} 88 89NPError NP_GetEntryPoints(NPPluginFuncs* pluginFuncs) 90{ 91 pluginFuncs->version = 11; 92 pluginFuncs->size = sizeof(pluginFuncs); 93 pluginFuncs->newp = NPP_New; 94 pluginFuncs->destroy = NPP_Destroy; 95 pluginFuncs->setwindow = NPP_SetWindow; 96 pluginFuncs->newstream = NPP_NewStream; 97 pluginFuncs->destroystream = NPP_DestroyStream; 98 pluginFuncs->asfile = NPP_StreamAsFile; 99 pluginFuncs->writeready = NPP_WriteReady; 100 pluginFuncs->write = (NPP_WriteProcPtr)NPP_Write; 101 pluginFuncs->print = NPP_Print; 102 pluginFuncs->event = NPP_HandleEvent; 103 pluginFuncs->urlnotify = NPP_URLNotify; 104 pluginFuncs->getvalue = NPP_GetValue; 105 pluginFuncs->setvalue = NPP_SetValue; 106 107 return NPERR_NO_ERROR; 108} 109 110void NP_Shutdown(void) 111{ 112} 113 114NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, char* argn[], char* argv[], NPSavedData* saved) 115{ 116 // Create per-instance storage 117 PluginObject* obj = (PluginObject*)malloc(sizeof(PluginObject)); 118 bzero(obj, sizeof(PluginObject)); 119 120 obj->npp = instance; 121 instance->pdata = obj; 122 123 // Ask the browser if it supports the CoreGraphics drawing model 124 NPBool supportsCoreGraphics; 125 if (browser->getvalue(instance, NPNVsupportsCoreGraphicsBool, &supportsCoreGraphics) != NPERR_NO_ERROR) 126 supportsCoreGraphics = FALSE; 127 128 if (!supportsCoreGraphics) 129 return NPERR_INCOMPATIBLE_VERSION_ERROR; 130 131 // If the browser supports the CoreGraphics drawing model, enable it. 132 browser->setvalue(instance, NPPVpluginDrawingModel, (void*)NPDrawingModelCoreGraphics); 133 134 // If the browser supports the Cocoa event model, enable it. 135 NPBool supportsCocoa; 136 if (browser->getvalue(instance, NPNVsupportsCocoaBool, &supportsCocoa) != NPERR_NO_ERROR) 137 supportsCocoa = FALSE; 138 139 if (!supportsCocoa) 140 return NPERR_INCOMPATIBLE_VERSION_ERROR; 141 142 browser->setvalue(instance, NPPVpluginEventModel, (void*)NPEventModelCocoa); 143 144 obj->textFieldRect = NSMakeRect(10, 10, 200, 100); 145 146 obj->textStorage = [[NSTextStorage alloc] initWithString:@""]; 147 obj->layoutManager = [[NSLayoutManager alloc] init]; 148 [obj->textStorage addLayoutManager:obj->layoutManager]; 149 150 obj->textContainer = [[NSTextContainer alloc] initWithContainerSize:obj->textFieldRect.size]; 151 [obj->layoutManager addTextContainer:obj->textContainer]; 152 153 obj->selectedRange.location = [obj->textStorage length]; 154 155 return NPERR_NO_ERROR; 156} 157 158NPError NPP_Destroy(NPP instance, NPSavedData** save) 159{ 160 // Free per-instance storage 161 PluginObject* obj = instance->pdata; 162 163 [obj->textStorage release]; 164 [obj->layoutManager release]; 165 166 free(obj); 167 168 return NPERR_NO_ERROR; 169} 170 171NPError NPP_SetWindow(NPP instance, NPWindow* window) 172{ 173 PluginObject* obj = instance->pdata; 174 obj->window = *window; 175 176 return NPERR_NO_ERROR; 177} 178 179 180NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype) 181{ 182 *stype = NP_ASFILEONLY; 183 return NPERR_NO_ERROR; 184} 185 186NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason) 187{ 188 return NPERR_NO_ERROR; 189} 190 191int32 NPP_WriteReady(NPP instance, NPStream* stream) 192{ 193 return 0; 194} 195 196int32 NPP_Write(NPP instance, NPStream* stream, int32 offset, int32 len, void* buffer) 197{ 198 return 0; 199} 200 201void NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname) 202{ 203} 204 205void NPP_Print(NPP instance, NPPrint* platformPrint) 206{ 207 208} 209 210static void handleDraw(PluginObject* obj, NPCocoaEvent *event) 211{ 212 NSGraphicsContext *oldContext = [[NSGraphicsContext currentContext] retain]; 213 214 NSGraphicsContext *context = [NSGraphicsContext graphicsContextWithGraphicsPort:event->data.draw.context 215 flipped:YES]; 216 217 218 [NSGraphicsContext setCurrentContext:context]; 219 220 NSRect rect = NSMakeRect(0, 0, obj->window.width, obj->window.height); 221 222 [[NSColor lightGrayColor] set]; 223 [NSBezierPath fillRect:rect]; 224 225 if (obj->pluginHasFocus) { 226 [[NSColor blackColor] set]; 227 [NSBezierPath strokeRect:rect]; 228 } 229 230 [[NSColor whiteColor] set]; 231 [NSBezierPath fillRect:obj->textFieldRect]; 232 233 // Draw the text 234 NSRange glyphRange = [obj->layoutManager glyphRangeForTextContainer:obj->textContainer]; 235 if (glyphRange.length > 0) { 236 [obj->layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:obj->textFieldRect.origin]; 237 [obj->layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:obj->textFieldRect.origin]; 238 } 239 240 NSBezierPath *textInputBorder = [NSBezierPath bezierPathWithRect:obj->textFieldRect]; 241 [[NSColor blackColor] set]; 242 243 if (obj->pluginHasFocus && obj->textFieldHasFocus) 244 [textInputBorder setLineWidth:2]; 245 else 246 [textInputBorder setLineWidth:1]; 247 248 [textInputBorder stroke]; 249 250 if (obj->pluginHasFocus && obj->textFieldHasFocus) { 251 NSUInteger rectCount; 252 NSRect *rectArray = [obj->layoutManager rectArrayForCharacterRange:obj->selectedRange 253 withinSelectedCharacterRange:obj->selectedRange 254 inTextContainer:obj->textContainer 255 rectCount:&rectCount]; 256 257 [[NSColor blackColor] set]; 258 for (unsigned i = 0; i < rectCount; i++) { 259 NSRect rect = rectArray[i]; 260 rect.origin.x += obj->textFieldRect.origin.x; 261 rect.origin.y += obj->textFieldRect.origin.y; 262 263 [NSBezierPath strokeRect:rect]; 264 } 265 } 266 267 [NSGraphicsContext setCurrentContext:oldContext]; 268} 269 270static void invalidatePlugin(PluginObject* obj) 271{ 272 NPRect rect; 273 rect.left = 0; 274 rect.top = 0; 275 rect.right = obj->window.width; 276 rect.bottom = obj->window.height; 277 278 browser->invalidaterect(obj->npp, &rect); 279} 280 281static void handleFocusChanged(NPCocoaEvent* cocoaEvent, PluginObject* obj) 282{ 283 obj->pluginHasFocus = cocoaEvent->data.focus.hasFocus; 284 285 invalidatePlugin(obj); 286} 287 288static void handleMouseMoved(NPCocoaEvent* cocoaEvent, PluginObject* obj) 289{ 290 NSPoint point = NSMakePoint(cocoaEvent->data.mouse.pluginX, cocoaEvent->data.mouse.pluginY); 291 292 if (NSPointInRect(point, obj->textFieldRect)) 293 [[NSCursor IBeamCursor] set]; 294 else 295 [[NSCursor arrowCursor] set]; 296} 297 298static void handleMouseDown(NPCocoaEvent* cocoaEvent, PluginObject* obj) 299{ 300 NSPoint point = NSMakePoint(cocoaEvent->data.mouse.pluginX, cocoaEvent->data.mouse.pluginY); 301 302 obj->textFieldHasFocus = NSPointInRect(point, obj->textFieldRect); 303 304 invalidatePlugin(obj); 305} 306 307static int16_t handleTextFieldKeyDown(NPCocoaEvent* event, PluginObject* obj) 308{ 309 NSString *string = (NSString *)event->data.key.charactersIgnoringModifiers; 310 311 unichar c = [string length] > 0 ? [string characterAtIndex:0] : 0; 312 313 switch (c) { 314 case NSLeftArrowFunctionKey: 315 if (obj->selectedRange.location > 0) { 316 obj->selectedRange.location--; 317 invalidatePlugin(obj); 318 } 319 return 1; 320 321 case NSRightArrowFunctionKey: 322 if (obj->selectedRange.location < [obj->textStorage length]) { 323 obj->selectedRange.location++; 324 invalidatePlugin(obj); 325 } 326 327 return 1; 328 329 default: 330 // Return 0 and let the text input system handle it. 331 return 0; 332 } 333} 334 335 336static int16_t handleTextInput(NPCocoaEvent* event, PluginObject* obj) 337{ 338 NSString *string = (NSString *)event->data.text.text; 339 NSRange range = obj->selectedRange; 340 341 [obj->textStorage replaceCharactersInRange:range withString:string]; 342 343 obj->selectedRange.location = range.location + [string length]; 344 obj->selectedRange.length = 0; 345 346 invalidatePlugin(obj); 347 348 return 1; 349} 350 351int16 NPP_HandleEvent(NPP instance, void* event) 352{ 353 PluginObject* obj = instance->pdata; 354 355 NPCocoaEvent* cocoaEvent = event; 356 357 switch (cocoaEvent->type) { 358 case NPCocoaEventDrawRect: 359 handleDraw(obj, cocoaEvent); 360 return 1; 361 case NPCocoaEventFocusChanged: 362 handleFocusChanged(cocoaEvent, obj); 363 return 1; 364 case NPCocoaEventMouseMoved: 365 handleMouseMoved(cocoaEvent, obj); 366 return 1; 367 case NPCocoaEventMouseDown: 368 handleMouseDown(cocoaEvent, obj); 369 return 1; 370 case NPCocoaEventKeyDown: 371 // If the text field has focus we ignore the event, causing it 372 // to be sent to the input manager. 373 if (obj->textFieldHasFocus) 374 return handleTextFieldKeyDown(cocoaEvent, obj); 375 else 376 return 1; 377 case NPCocoaEventTextInput: 378 return handleTextInput(cocoaEvent, obj); 379 return 1; 380 381 } 382 383 return 0; 384} 385 386void NPP_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData) 387{ 388} 389 390NPError NPP_GetValue(NPP instance, NPPVariable variable, void* value) 391{ 392 return NPERR_GENERIC_ERROR; 393} 394 395NPError NPP_SetValue(NPP instance, NPNVariable variable, void* value) 396{ 397 return NPERR_GENERIC_ERROR; 398} 399