Reviewed by Kevin Decker.
[WebKit-https.git] / WebKit / Plugins / WebNetscapePluginPackage.m
1 /*
2  * Copyright (C) 2005 Apple Computer, 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  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission. 
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #import <WebKit/WebNetscapePluginPackage.h>
30
31 #import <WebKit/WebKitLogging.h>
32 #import <WebKit/WebKitNSStringExtras.h>
33 #import <WebKit/WebNSObjectExtras.h>
34
35 #import "WebNetscapeDeprecatedFunctions.h"
36
37 #import <JavaScriptCore/npruntime_impl.h>
38
39 typedef void (* FunctionPointer) (void);
40 typedef void (* TransitionVector) (void);
41 static FunctionPointer functionPointerForTVector(TransitionVector);
42 static TransitionVector tVectorForFunctionPointer(FunctionPointer);
43
44 #define PluginNameOrDescriptionStringNumber     126
45 #define MIMEDescriptionStringNumber             127
46 #define MIMEListStringStringNumber              128
47
48 #define RealPlayerAppIndentifier                @"com.RealNetworks.RealOne Player"
49 #define RealPlayerPluginFilename                @"RealPlayer Plugin"
50
51 @interface WebNetscapePluginPackage (Internal)
52 - (void)_unloadWithShutdown:(BOOL)shutdown;
53 @end
54
55 @implementation WebNetscapePluginPackage
56
57 #ifndef __LP64__
58 + (void)initialize
59 {
60     // The Shockwave plugin requires a valid file in CurApRefNum.
61     // But it doesn't seem to matter what file it is.
62     // If we're called inside a Cocoa application which won't have a
63     // CurApRefNum, we set it to point to the system resource file.
64     if (WebLMGetCurApRefNum() == -1) {
65         // To get the refNum for the system resource file, we have to do
66         // UseResFile(kSystemResFile) and then look at CurResFile().
67         short savedCurResFile = CurResFile();
68         UseResFile(kSystemResFile);
69         WebLMSetCurApRefNum(CurResFile());
70         UseResFile(savedCurResFile);
71     }
72 }
73 #endif
74
75 - (SInt16)openResourceFile
76 {
77     FSRef fref;
78     OSErr err;
79     
80     if (isBundle)
81         return CFBundleOpenBundleResourceMap(cfBundle);
82     else {
83         err = FSPathMakeRef((const UInt8 *)[path fileSystemRepresentation], &fref, NULL);
84         if (err != noErr)
85             return -1;
86         
87         return FSOpenResFile(&fref, fsRdPerm);
88     }
89 }
90
91 - (void)closeResourceFile:(SInt16)resRef
92 {
93     if (isBundle)
94         CFBundleCloseBundleResourceMap(cfBundle, resRef);
95     else
96         CloseResFile(resRef);
97 }
98
99 - (NSString *)stringForStringListID:(SInt16)stringListID andIndex:(SInt16)index
100 {
101     // Get resource, and dereference the handle.
102     Handle stringHandle = Get1Resource('STR#', stringListID);
103     if (stringHandle == NULL) {
104         return nil;
105     }
106     unsigned char *p = (unsigned char *)*stringHandle;
107     if (!p)
108         return nil;
109     
110     // Check the index against the length of the string list, then skip the length.
111     if (index < 1 || index > *(SInt16 *)p)
112         return nil;
113     p += sizeof(SInt16);
114     
115     // Skip any strings that come before the one we are looking for.
116     while (--index)
117         p += 1 + *p;
118     
119     // Convert the one we found into an NSString.
120     return [[[NSString alloc] initWithBytes:(p + 1) length:*p encoding:[NSString _web_encodingForResource:stringHandle]] autorelease];
121 }
122
123 - (BOOL)getPluginInfoFromResources
124 {
125     SInt16 resRef = [self openResourceFile];
126     if (resRef == -1)
127         return NO;
128     
129     UseResFile(resRef);
130     if (ResError() != noErr)
131         return NO;
132
133     NSString *MIME, *extensionsList, *description;
134     NSArray *extensions;
135     unsigned i;
136     
137     NSMutableDictionary *MIMEToExtensionsDictionary = [NSMutableDictionary dictionary];
138     NSMutableDictionary *MIMEToDescriptionDictionary = [NSMutableDictionary dictionary];
139
140     for (i=1; 1; i+=2) {
141         MIME = [[self stringForStringListID:MIMEListStringStringNumber
142                                    andIndex:i] lowercaseString];
143         if (!MIME)
144             break;
145
146         extensionsList = [[self stringForStringListID:MIMEListStringStringNumber andIndex:i+1] lowercaseString];
147         if (extensionsList) {
148             extensions = [extensionsList componentsSeparatedByString:@","];
149             [MIMEToExtensionsDictionary setObject:extensions forKey:MIME];
150         } else
151             // DRM and WMP claim MIMEs without extensions. Use a @"" extension in this case.
152             [MIMEToExtensionsDictionary setObject:[NSArray arrayWithObject:@""] forKey:MIME];
153         
154         description = [self stringForStringListID:MIMEDescriptionStringNumber
155                                          andIndex:[MIMEToExtensionsDictionary count]];
156         if (description)
157             [MIMEToDescriptionDictionary setObject:description forKey:MIME];
158         else
159             [MIMEToDescriptionDictionary setObject:@"" forKey:MIME];
160     }
161
162     [self setMIMEToDescriptionDictionary:MIMEToDescriptionDictionary];
163     [self setMIMEToExtensionsDictionary:MIMEToExtensionsDictionary];
164
165     NSString *filename = [self filename];
166     
167     description = [self stringForStringListID:PluginNameOrDescriptionStringNumber andIndex:1];
168     if (!description)
169         description = filename;
170     [self setPluginDescription:description];
171     
172     
173     NSString *theName = [self stringForStringListID:PluginNameOrDescriptionStringNumber andIndex:2];
174     if (!theName)
175         theName = filename;
176     [self setName:theName];
177     
178     [self closeResourceFile:resRef];
179     
180     return YES;
181 }
182
183 - (BOOL)_initWithPath:(NSString *)pluginPath
184 {
185     resourceRef = -1;
186     
187     OSType type = 0;
188
189     // Bundle
190     if (bundle) {
191         isBundle = YES;
192         CFBundleGetPackageInfo(cfBundle, &type, NULL);
193     }
194     // Single-file plug-in with resource fork
195     else {
196 #ifdef __ppc__
197         type = [[[NSFileManager defaultManager] fileAttributesAtPath:path traverseLink:YES] fileHFSTypeCode];
198         isBundle = NO;
199         isCFM = YES;
200 #else
201         return NO;
202 #endif
203     }
204     
205     if (type != FOUR_CHAR_CODE('BRPL'))
206         return NO;
207
208     // Check if the executable is Mach-O or CFM.
209     if (bundle) {
210         NSFileHandle *executableFile = [NSFileHandle fileHandleForReadingAtPath:[bundle executablePath]];
211         NSData *data = [executableFile readDataOfLength:512];
212         [executableFile closeFile];
213         // Check the length of the data before calling memcmp. We think this fixes 3782543.
214         if (data == nil || [data length] < 8)
215             return NO;
216         isCFM = memcmp([data bytes], "Joy!peff", 8) == 0;
217 #ifndef __ppc__
218         // CFM is PPC-only.
219         if (isCFM)
220             return NO;
221 #endif
222         if (![self isNativeLibraryData:data])
223             return NO;
224     }
225
226     if (![self getPluginInfoFromPLists] && ![self getPluginInfoFromResources])
227         return NO;
228     
229     return YES;
230 }
231
232 - (id)initWithPath:(NSString *)pluginPath
233 {
234     if (!(self = [super initWithPath:pluginPath]))
235         return nil;
236     
237     // Initializing a plugin package can cause it to be loaded.  If there was an error initializing the plugin package,
238     // ensure that it is unloaded before deallocating it (WebBasePluginPackage requires & asserts this).
239     if (![self _initWithPath:pluginPath]) {
240         [self _unloadWithShutdown:YES];
241         [self release];
242         return nil;
243     }
244         
245     return self;
246 }
247
248 - (WebExecutableType)executableType
249 {
250     if (isCFM)
251         return WebCFMExecutableType;
252     else
253         return WebMachOExecutableType;
254 }
255
256 - (void)launchRealPlayer
257 {
258     CFURLRef appURL = NULL;
259     OSStatus error = LSFindApplicationForInfo(kLSUnknownCreator, (CFStringRef)RealPlayerAppIndentifier, NULL, NULL, &appURL);
260     if (!error) {
261         LSLaunchURLSpec URLSpec;
262         bzero(&URLSpec, sizeof(URLSpec));
263         URLSpec.launchFlags = kLSLaunchDefaults | kLSLaunchDontSwitch;
264         URLSpec.appURL = appURL;
265         LSOpenFromURLSpec(&URLSpec, NULL);
266         CFRelease(appURL);
267     }
268 }
269
270 - (BOOL)load
271 {    
272     NP_GetEntryPointsFuncPtr NP_GetEntryPoints = NULL;
273     NP_InitializeFuncPtr NP_Initialize = NULL;
274     MainFuncPtr pluginMainFunc = NULL;
275     NPError npErr;
276
277 #if !LOG_DISABLED
278     CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
279 #endif
280     LOG(Plugins, "%f Load timing started for: %@", start, [self name]);
281
282     if (isLoaded)
283         return YES;
284     
285     if (isBundle) {
286         if (!CFBundleLoadExecutable(cfBundle))
287             goto abort;
288 #if !LOG_DISABLED
289         CFAbsoluteTime currentTime = CFAbsoluteTimeGetCurrent();
290         CFAbsoluteTime duration = currentTime - start;
291 #endif
292         LOG(Plugins, "%f CFBundleLoadExecutable took %f seconds", currentTime, duration);
293         isLoaded = YES;
294         
295         if (isCFM) {
296             pluginMainFunc = (MainFuncPtr)CFBundleGetFunctionPointerForName(cfBundle, CFSTR("main") );
297             if (!pluginMainFunc)
298                 goto abort;
299         } else {
300             NP_Initialize = (NP_InitializeFuncPtr)CFBundleGetFunctionPointerForName(cfBundle, CFSTR("NP_Initialize"));
301             NP_GetEntryPoints = (NP_GetEntryPointsFuncPtr)CFBundleGetFunctionPointerForName(cfBundle, CFSTR("NP_GetEntryPoints"));
302             NPP_Shutdown = (NPP_ShutdownProcPtr)CFBundleGetFunctionPointerForName(cfBundle, CFSTR("NP_Shutdown"));
303             if (!NP_Initialize || !NP_GetEntryPoints || !NPP_Shutdown)
304                 goto abort;
305         }
306     } else {
307 #ifdef __LP64__
308         // CFM is not supported in 64-bit
309         goto abort;
310 #else
311         // single CFM file
312         FSSpec spec;
313         FSRef fref;
314         OSErr err;
315         
316         err = FSPathMakeRef((UInt8 *)[path fileSystemRepresentation], &fref, NULL);
317         if (err != noErr) {
318             LOG_ERROR("FSPathMakeRef failed. Error=%d", err);
319             goto abort;
320         }
321         err = FSGetCatalogInfo(&fref, kFSCatInfoNone, NULL, NULL, &spec, NULL);
322         if (err != noErr) {
323             LOG_ERROR("FSGetCatalogInfo failed. Error=%d", err);
324             goto abort;
325         }
326         err = WebGetDiskFragment(&spec, 0, kCFragGoesToEOF, nil, kPrivateCFragCopy, &connID, (Ptr *)&pluginMainFunc, nil);
327         if (err != noErr) {
328             LOG_ERROR("WebGetDiskFragment failed. Error=%d", err);
329             goto abort;
330         }
331 #if !LOG_DISABLED
332         CFAbsoluteTime currentTime = CFAbsoluteTimeGetCurrent();
333         CFAbsoluteTime duration = currentTime - start;
334 #endif
335         LOG(Plugins, "%f WebGetDiskFragment took %f seconds", currentTime, duration);
336         isLoaded = YES;
337         
338         pluginMainFunc = (MainFuncPtr)functionPointerForTVector((TransitionVector)pluginMainFunc);
339         if (!pluginMainFunc) {
340             goto abort;
341         }
342
343         // NOTE: pluginMainFunc is freed after it is called. Be sure not to return before that.
344         
345         isCFM = TRUE;
346 #endif /* __LP64__ */
347     }
348     
349     // Plugins (at least QT) require that you call UseResFile on the resource file before loading it.
350     resourceRef = [self openResourceFile];
351     if (resourceRef != -1) {
352         UseResFile(resourceRef);
353     }
354     
355     // swap function tables
356     if (isCFM) {
357         browserFuncs.version = NP_VERSION_MINOR;
358         browserFuncs.size = sizeof(NPNetscapeFuncs);
359         browserFuncs.geturl = (NPN_GetURLProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_GetURL);
360         browserFuncs.posturl = (NPN_PostURLProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_PostURL);
361         browserFuncs.requestread = (NPN_RequestReadProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_RequestRead);
362         browserFuncs.newstream = (NPN_NewStreamProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_NewStream);
363         browserFuncs.write = (NPN_WriteProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_Write);
364         browserFuncs.destroystream = (NPN_DestroyStreamProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_DestroyStream);
365         browserFuncs.status = (NPN_StatusProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_Status);
366         browserFuncs.uagent = (NPN_UserAgentProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_UserAgent);
367         browserFuncs.memalloc = (NPN_MemAllocProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_MemAlloc);
368         browserFuncs.memfree = (NPN_MemFreeProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_MemFree);
369         browserFuncs.memflush = (NPN_MemFlushProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_MemFlush);
370         browserFuncs.reloadplugins = (NPN_ReloadPluginsProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_ReloadPlugins);
371         browserFuncs.geturlnotify = (NPN_GetURLNotifyProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_GetURLNotify);
372         browserFuncs.posturlnotify = (NPN_PostURLNotifyProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_PostURLNotify);
373         browserFuncs.getvalue = (NPN_GetValueProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_GetValue);
374         browserFuncs.setvalue = (NPN_SetValueProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_SetValue);
375         browserFuncs.invalidaterect = (NPN_InvalidateRectProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_InvalidateRect);
376         browserFuncs.invalidateregion = (NPN_InvalidateRegionProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_InvalidateRegion);
377         browserFuncs.forceredraw = (NPN_ForceRedrawProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_ForceRedraw);
378         browserFuncs.getJavaEnv = (NPN_GetJavaEnvProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_GetJavaEnv);
379         browserFuncs.getJavaPeer = (NPN_GetJavaPeerProcPtr)tVectorForFunctionPointer((FunctionPointer)NPN_GetJavaPeer);
380
381         browserFuncs.releasevariantvalue = (NPN_ReleaseVariantValueProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_ReleaseVariantValue);
382         browserFuncs.getstringidentifier = (NPN_GetStringIdentifierProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_GetStringIdentifier);
383         browserFuncs.getstringidentifiers = (NPN_GetStringIdentifiersProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_GetStringIdentifiers);
384         browserFuncs.getintidentifier = (NPN_GetIntIdentifierProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_GetIntIdentifier);
385         browserFuncs.identifierisstring = (NPN_IdentifierIsStringProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_IdentifierIsString);
386         browserFuncs.utf8fromidentifier = (NPN_UTF8FromIdentifierProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_UTF8FromIdentifier);
387         browserFuncs.createobject = (NPN_CreateObjectProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_CreateObject);
388         browserFuncs.retainobject = (NPN_RetainObjectProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_RetainObject);
389         browserFuncs.releaseobject = (NPN_ReleaseObjectProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_ReleaseObject);
390         browserFuncs.invoke = (NPN_InvokeProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_Invoke);
391         browserFuncs.invokeDefault = (NPN_InvokeDefaultProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_InvokeDefault);
392         browserFuncs.evaluate = (NPN_EvaluateProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_Evaluate);
393         browserFuncs.getproperty = (NPN_GetPropertyProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_GetProperty);
394         browserFuncs.setproperty = (NPN_SetPropertyProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_SetProperty);
395         browserFuncs.removeproperty = (NPN_RemovePropertyProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_RemoveProperty);
396         browserFuncs.setexception = (NPN_SetExceptionProcPtr)tVectorForFunctionPointer((FunctionPointer)_NPN_SetException);
397
398 #if !LOG_DISABLED
399         CFAbsoluteTime mainStart = CFAbsoluteTimeGetCurrent();
400 #endif
401         LOG(Plugins, "%f main timing started", mainStart);
402         NPP_ShutdownProcPtr shutdownFunction;
403         npErr = pluginMainFunc(&browserFuncs, &pluginFuncs, &shutdownFunction);
404         NPP_Shutdown = (NPP_ShutdownProcPtr)functionPointerForTVector((TransitionVector)shutdownFunction);
405         if (!isBundle)
406             // Don't free pluginMainFunc if we got it from a bundle because it is owned by CFBundle in that case.
407             free(pluginMainFunc);
408         
409         // Workaround for 3270576. The RealPlayer plug-in fails to load if its preference file is out of date.
410         // Launch the RealPlayer application to refresh the file.
411         if (npErr != NPERR_NO_ERROR) {
412             if (npErr == NPERR_MODULE_LOAD_FAILED_ERROR && [[self filename] isEqualToString:RealPlayerPluginFilename])
413                 [self launchRealPlayer];
414             goto abort;
415         }
416 #if !LOG_DISABLED
417         CFAbsoluteTime currentTime = CFAbsoluteTimeGetCurrent();
418         CFAbsoluteTime duration = currentTime - mainStart;
419 #endif
420         LOG(Plugins, "%f main took %f seconds", currentTime, duration);
421         
422         pluginSize = pluginFuncs.size;
423         pluginVersion = pluginFuncs.version;
424         LOG(Plugins, "pluginMainFunc: %d, size=%d, version=%d", npErr, pluginSize, pluginVersion);
425         
426         NPP_New = (NPP_NewProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.newp);
427         NPP_Destroy = (NPP_DestroyProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.destroy);
428         NPP_SetWindow = (NPP_SetWindowProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.setwindow);
429         NPP_NewStream = (NPP_NewStreamProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.newstream);
430         NPP_DestroyStream = (NPP_DestroyStreamProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.destroystream);
431         NPP_StreamAsFile = (NPP_StreamAsFileProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.asfile);
432         NPP_WriteReady = (NPP_WriteReadyProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.writeready);
433         NPP_Write = (NPP_WriteProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.write);
434         NPP_Print = (NPP_PrintProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.print);
435         NPP_HandleEvent = (NPP_HandleEventProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.event);
436         NPP_URLNotify = (NPP_URLNotifyProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.urlnotify);
437         NPP_GetValue = (NPP_GetValueProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.getvalue);
438         NPP_SetValue = (NPP_SetValueProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.setvalue);
439
440         // LiveConnect support
441         NPP_GetJavaClass = (NPP_GetJavaClassProcPtr)functionPointerForTVector((TransitionVector)pluginFuncs.javaClass);
442         if (NPP_GetJavaClass) {
443             LOG(LiveConnect, "%@:  CFM entry point for NPP_GetJavaClass = %p", [self name], NPP_GetJavaClass);
444         } else {
445             LOG(LiveConnect, "%@:  no entry point for NPP_GetJavaClass", [self name]);
446         }
447
448     } else {
449         // no function pointer conversion necessary for mach-o
450         browserFuncs.version = NP_VERSION_MINOR;
451         browserFuncs.size = sizeof(NPNetscapeFuncs);
452         browserFuncs.geturl = NPN_GetURL;
453         browserFuncs.posturl = NPN_PostURL;
454         browserFuncs.requestread = NPN_RequestRead;
455         browserFuncs.newstream = NPN_NewStream;
456         browserFuncs.write = NPN_Write;
457         browserFuncs.destroystream = NPN_DestroyStream;
458         browserFuncs.status = NPN_Status;
459         browserFuncs.uagent = NPN_UserAgent;
460         browserFuncs.memalloc = NPN_MemAlloc;
461         browserFuncs.memfree = NPN_MemFree;
462         browserFuncs.memflush = NPN_MemFlush;
463         browserFuncs.reloadplugins = NPN_ReloadPlugins;
464         browserFuncs.geturlnotify = NPN_GetURLNotify;
465         browserFuncs.posturlnotify = NPN_PostURLNotify;
466         browserFuncs.getvalue = NPN_GetValue;
467         browserFuncs.setvalue = NPN_SetValue;
468         browserFuncs.invalidaterect = NPN_InvalidateRect;
469         browserFuncs.invalidateregion = NPN_InvalidateRegion;
470         browserFuncs.forceredraw = NPN_ForceRedraw;
471         browserFuncs.getJavaEnv = NPN_GetJavaEnv;
472         browserFuncs.getJavaPeer = NPN_GetJavaPeer;
473
474         browserFuncs.releasevariantvalue = _NPN_ReleaseVariantValue;
475         browserFuncs.getstringidentifier = _NPN_GetStringIdentifier;
476         browserFuncs.getstringidentifiers = _NPN_GetStringIdentifiers;
477         browserFuncs.getintidentifier = _NPN_GetIntIdentifier;
478         browserFuncs.identifierisstring = _NPN_IdentifierIsString;
479         browserFuncs.utf8fromidentifier = _NPN_UTF8FromIdentifier;
480         browserFuncs.createobject = _NPN_CreateObject;
481         browserFuncs.retainobject = _NPN_RetainObject;
482         browserFuncs.releaseobject = _NPN_ReleaseObject;
483         browserFuncs.invoke = _NPN_Invoke;
484         browserFuncs.invokeDefault = _NPN_InvokeDefault;
485         browserFuncs.evaluate = _NPN_Evaluate;
486         browserFuncs.getproperty = _NPN_GetProperty;
487         browserFuncs.setproperty = _NPN_SetProperty;
488         browserFuncs.removeproperty = _NPN_RemoveProperty;
489         browserFuncs.setexception = _NPN_SetException;
490
491 #if !LOG_DISABLED
492         CFAbsoluteTime initializeStart = CFAbsoluteTimeGetCurrent();
493 #endif
494         LOG(Plugins, "%f NP_Initialize timing started", initializeStart);
495         npErr = NP_Initialize(&browserFuncs);
496         if (npErr != NPERR_NO_ERROR)
497             goto abort;
498 #if !LOG_DISABLED
499         CFAbsoluteTime currentTime = CFAbsoluteTimeGetCurrent();
500         CFAbsoluteTime duration = currentTime - initializeStart;
501 #endif
502         LOG(Plugins, "%f NP_Initialize took %f seconds", currentTime, duration);
503
504         npErr = NP_GetEntryPoints(&pluginFuncs);
505         if (npErr != NPERR_NO_ERROR)
506             goto abort;
507         
508         pluginSize = pluginFuncs.size;
509         pluginVersion = pluginFuncs.version;
510         
511         NPP_New = pluginFuncs.newp;
512         NPP_Destroy = pluginFuncs.destroy;
513         NPP_SetWindow = pluginFuncs.setwindow;
514         NPP_NewStream = pluginFuncs.newstream;
515         NPP_DestroyStream = pluginFuncs.destroystream;
516         NPP_StreamAsFile = pluginFuncs.asfile;
517         NPP_WriteReady = pluginFuncs.writeready;
518         NPP_Write = pluginFuncs.write;
519         NPP_Print = pluginFuncs.print;
520         NPP_HandleEvent = pluginFuncs.event;
521         NPP_URLNotify = pluginFuncs.urlnotify;
522         NPP_GetValue = pluginFuncs.getvalue;
523         NPP_SetValue = pluginFuncs.setvalue;
524
525         // LiveConnect support
526         NPP_GetJavaClass = pluginFuncs.javaClass;
527         if (NPP_GetJavaClass){
528             LOG(LiveConnect, "%@:  mach-o entry point for NPP_GetJavaClass = %p", [self name], NPP_GetJavaClass);
529         } else {
530             LOG(LiveConnect, "%@:  no entry point for NPP_GetJavaClass", [self name]);
531         }
532     }
533
534 #if !LOG_DISABLED
535     CFAbsoluteTime currentTime = CFAbsoluteTimeGetCurrent();
536     CFAbsoluteTime duration = currentTime - start;
537 #endif
538     LOG(Plugins, "%f Total load time: %f seconds", currentTime, duration);
539
540     return [super load];
541
542 abort:
543     [self _unloadWithShutdown:NO];
544     return NO;
545 }
546
547 - (NPP_SetWindowProcPtr)NPP_SetWindow
548 {
549     return NPP_SetWindow;
550 }
551
552 - (NPP_NewProcPtr)NPP_New
553 {
554     return NPP_New;
555 }
556
557 - (NPP_DestroyProcPtr)NPP_Destroy
558 {
559     return NPP_Destroy;
560 }
561
562 - (NPP_NewStreamProcPtr)NPP_NewStream
563 {
564     return NPP_NewStream;
565 }
566
567 - (NPP_StreamAsFileProcPtr)NPP_StreamAsFile
568 {
569     return NPP_StreamAsFile;
570 }
571 - (NPP_DestroyStreamProcPtr)NPP_DestroyStream
572 {
573     return NPP_DestroyStream;
574 }
575
576 - (NPP_WriteReadyProcPtr)NPP_WriteReady
577 {
578     return NPP_WriteReady;
579 }
580 - (NPP_WriteProcPtr)NPP_Write
581 {
582     return NPP_Write;
583 }
584
585 - (NPP_HandleEventProcPtr)NPP_HandleEvent
586 {
587     return NPP_HandleEvent;
588 }
589
590 -(NPP_URLNotifyProcPtr)NPP_URLNotify
591 {
592     return NPP_URLNotify;
593 }
594
595 -(NPP_GetValueProcPtr)NPP_GetValue
596 {
597     return NPP_GetValue;
598 }
599
600 -(NPP_SetValueProcPtr)NPP_SetValue
601 {
602     return NPP_SetValue;
603 }
604
605 -(NPP_PrintProcPtr)NPP_Print
606 {
607     return NPP_Print;
608 }
609
610 - (void)wasRemovedFromPluginDatabase:(WebPluginDatabase *)database
611 {
612     [super wasRemovedFromPluginDatabase:database];
613     
614     // Unload when removed from final plug-in database
615     if ([pluginDatabases count] == 0)
616         [self _unloadWithShutdown:YES];
617 }
618
619 - (void)open
620 {
621     instanceCount++;
622     
623     // Handle the case where all instances close a plug-in package, but another
624     // instance opens the package before it is unloaded (which only happens when
625     // the plug-in database is refreshed)
626     needsUnload = NO;
627     
628     if (!isLoaded) {
629         // Should load when the first instance opens the plug-in package
630         ASSERT(instanceCount == 1);
631         [self load];
632     }
633 }
634
635 - (void)close
636 {
637     ASSERT(instanceCount > 0);
638     instanceCount--;
639     if (instanceCount == 0 && needsUnload)
640         [self _unloadWithShutdown:YES];
641 }
642
643 @end
644
645
646 // function pointer converters
647
648 FunctionPointer functionPointerForTVector(TransitionVector tvp)
649 {
650     const uint32 temp[6] = {0x3D800000, 0x618C0000, 0x800C0000, 0x804C0004, 0x7C0903A6, 0x4E800420};
651     uint32 *newGlue = NULL;
652
653     if (tvp != NULL) {
654         newGlue = (uint32 *)malloc(sizeof(temp));
655         if (newGlue != NULL) {
656             unsigned i;
657             for (i = 0; i < 6; i++) newGlue[i] = temp[i];
658             newGlue[0] |= ((uintptr_t)tvp >> 16);
659             newGlue[1] |= ((uintptr_t)tvp & 0xFFFF);
660             MakeDataExecutable(newGlue, sizeof(temp));
661         }
662     }
663     
664     return (FunctionPointer)newGlue;
665 }
666
667 TransitionVector tVectorForFunctionPointer(FunctionPointer fp)
668 {
669     FunctionPointer *newGlue = NULL;
670     if (fp != NULL) {
671         newGlue = (FunctionPointer *)malloc(2 * sizeof(FunctionPointer));
672         if (newGlue != NULL) {
673             newGlue[0] = fp;
674             newGlue[1] = NULL;
675         }
676     }
677     return (TransitionVector)newGlue;
678 }
679
680 @implementation WebNetscapePluginPackage (Internal)
681
682 - (void)_unloadWithShutdown:(BOOL)shutdown
683 {
684     if (!isLoaded)
685         return;
686     
687     LOG(Plugins, "Unloading %@...", name);
688
689     // Cannot unload a plug-in package while an instance is still using it
690     if (instanceCount > 0) {
691         needsUnload = YES;
692         return;
693     }
694
695     if (shutdown && NPP_Shutdown)
696         NPP_Shutdown();
697
698     if (resourceRef != -1)
699         [self closeResourceFile:resourceRef];
700
701     if (isBundle)
702         CFBundleUnloadExecutable(cfBundle);
703 #ifndef __LP64__
704     else {
705         // CFM is not supported in 64-bit
706         WebCloseConnection(&connID);
707     }
708 #endif
709
710     LOG(Plugins, "Plugin Unloaded");
711     isLoaded = NO;
712 }
713
714 @end