1/*
2 * Copyright (C) 2005, 2006, 2007 Apple 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#if ENABLE(NETSCAPE_PLUGIN_API)
30#import "WebNetscapePluginPackage.h"
31
32#import "WebTypesInternal.h"
33#import "WebKitLogging.h"
34#import "WebKitNSStringExtras.h"
35#import "WebNSFileManagerExtras.h"
36#import "WebNSObjectExtras.h"
37#import <WebCore/npruntime_impl.h>
38#import <wtf/RetainPtr.h>
39
40#if USE(PLUGIN_HOST_PROCESS)
41#import "NetscapePluginHostManager.h"
42
43using namespace WebKit;
44#endif
45
46using namespace WebCore;
47
48#define PluginNameOrDescriptionStringNumber     126
49#define MIMEDescriptionStringNumber             127
50#define MIMEListStringStringNumber              128
51
52@interface WebNetscapePluginPackage (Internal)
53- (void)_unloadWithShutdown:(BOOL)shutdown;
54@end
55
56@implementation WebNetscapePluginPackage
57
58- (ResFileRefNum)openResourceFile
59{
60    return CFBundleOpenBundleResourceMap(cfBundle.get());
61}
62
63- (void)closeResourceFile:(ResFileRefNum)resRef
64{
65    CFBundleCloseBundleResourceMap(cfBundle.get(), resRef);
66}
67
68#if COMPILER(CLANG)
69#pragma clang diagnostic push
70#pragma clang diagnostic ignored "-Wdeprecated-declarations"
71#endif
72
73- (NSString *)stringForStringListID:(SInt16)stringListID andIndex:(SInt16)index
74{
75    // Get resource, and dereference the handle.
76    Handle stringHandle = Get1Resource('STR#', stringListID);
77    if (stringHandle == NULL) {
78        return nil;
79    }
80    unsigned char *p = (unsigned char *)*stringHandle;
81    if (!p)
82        return nil;
83
84    // Check the index against the length of the string list, then skip the length.
85    if (index < 1 || index > *(SInt16 *)p)
86        return nil;
87    p += sizeof(SInt16);
88
89    // Skip any strings that come before the one we are looking for.
90    while (--index)
91        p += 1 + *p;
92
93    // Convert the one we found into an NSString.
94    return [[[NSString alloc] initWithBytes:(p + 1) length:*p encoding:[NSString _web_encodingForResource:stringHandle]] autorelease];
95}
96
97- (BOOL)getPluginInfoFromResources
98{
99    SInt16 resRef = [self openResourceFile];
100    if (resRef == -1)
101        return NO;
102
103    UseResFile(resRef);
104    if (ResError() != noErr)
105        return NO;
106
107    NSString *MIME, *extensionsList, *description;
108    NSArray *extensions;
109    unsigned i;
110
111    for (i=1; 1; i+=2) {
112        MIME = [[self stringForStringListID:MIMEListStringStringNumber
113                                   andIndex:i] lowercaseString];
114        if (!MIME)
115            break;
116
117        MimeClassInfo mimeClassInfo;
118        mimeClassInfo.type = String(MIME).lower();
119
120        extensionsList = [[self stringForStringListID:MIMEListStringStringNumber andIndex:i+1] lowercaseString];
121        if (extensionsList) {
122            extensions = [extensionsList componentsSeparatedByString:@","];
123            for (NSUInteger j = 0; j < [extensions count]; ++j)
124                mimeClassInfo.extensions.append((NSString *)[extensions objectAtIndex:j]);
125        }
126
127        description = [self stringForStringListID:MIMEDescriptionStringNumber
128                                         andIndex:pluginInfo.mimes.size() + 1];
129        mimeClassInfo.desc = description;
130
131        pluginInfo.mimes.append(mimeClassInfo);
132    }
133
134    NSString *filename = [(NSString *)path lastPathComponent];
135    pluginInfo.file = filename;
136
137    description = [self stringForStringListID:PluginNameOrDescriptionStringNumber andIndex:1];
138    if (!description)
139        description = filename;
140    pluginInfo.desc = description;
141
142
143    NSString *theName = [self stringForStringListID:PluginNameOrDescriptionStringNumber andIndex:2];
144    if (!theName)
145        theName = filename;
146    pluginInfo.name = theName;
147
148    pluginInfo.isApplicationPlugin = false;
149
150    [self closeResourceFile:resRef];
151
152    return YES;
153}
154#if COMPILER(CLANG)
155#pragma clang diagnostic pop
156#endif
157
158- (BOOL)_initWithPath:(NSString *)pluginPath
159{
160    resourceRef = -1;
161
162    OSType type = 0;
163
164    if (!cfBundle)
165        return NO;
166
167    CFBundleGetPackageInfo(cfBundle.get(), &type, NULL);
168
169    if (type != FOUR_CHAR_CODE('BRPL'))
170        return NO;
171
172#if USE(PLUGIN_HOST_PROCESS)
173    RetainPtr<CFArrayRef> archs = adoptCF(CFBundleCopyExecutableArchitectures(cfBundle.get()));
174
175    if ([(NSArray *)archs.get() containsObject:[NSNumber numberWithInteger:NSBundleExecutableArchitectureX86_64]])
176        pluginHostArchitecture = CPU_TYPE_X86_64;
177    else if ([(NSArray *)archs.get() containsObject:[NSNumber numberWithInteger:NSBundleExecutableArchitectureI386]])
178        pluginHostArchitecture = CPU_TYPE_X86;
179    else
180        return NO;
181#else
182    RetainPtr<CFURLRef> executableURL = adoptCF(CFBundleCopyExecutableURL(cfBundle.get()));
183    if (!executableURL)
184        return NO;
185    NSFileHandle *executableFile = [NSFileHandle fileHandleForReadingAtPath:[(NSURL *)executableURL.get() path]];
186    NSData *data = [executableFile readDataOfLength:512];
187    [executableFile closeFile];
188
189     if (![self isNativeLibraryData:data])
190         return NO;
191
192#endif
193
194    if (![self getPluginInfoFromPLists] && ![self getPluginInfoFromResources])
195        return NO;
196
197    return YES;
198}
199
200- (id)initWithPath:(NSString *)pluginPath
201{
202    if (!(self = [super initWithPath:pluginPath]))
203        return nil;
204
205    // Initializing a plugin package can cause it to be loaded.  If there was an error initializing the plugin package,
206    // ensure that it is unloaded before deallocating it (WebBasePluginPackage requires & asserts this).
207    if (![self _initWithPath:pluginPath]) {
208        [self _unloadWithShutdown:YES];
209        [self release];
210        return nil;
211    }
212
213    return self;
214}
215
216#if USE(PLUGIN_HOST_PROCESS)
217- (cpu_type_t)pluginHostArchitecture
218{
219    return pluginHostArchitecture;
220}
221
222- (void)createPropertyListFile
223{
224    NetscapePluginHostManager::shared().createPropertyListFile(path, pluginHostArchitecture, [self bundleIdentifier]);
225}
226
227#endif
228
229- (void)unload
230{
231    [self _unloadWithShutdown:YES];
232}
233
234- (BOOL)_tryLoad
235{
236    NP_GetEntryPointsFuncPtr NP_GetEntryPoints = NULL;
237    NP_InitializeFuncPtr NP_Initialize = NULL;
238    NPError npErr;
239
240#if !LOG_DISABLED
241    CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
242    CFAbsoluteTime currentTime;
243    CFAbsoluteTime duration;
244#endif
245    LOG(Plugins, "%f Load timing started for: %@", start, (NSString *)[self pluginInfo].name);
246
247    if (isLoaded)
248        return YES;
249
250    if (!CFBundleLoadExecutable(cfBundle.get()))
251        return NO;
252#if !LOG_DISABLED
253    currentTime = CFAbsoluteTimeGetCurrent();
254    duration = currentTime - start;
255#endif
256    LOG(Plugins, "%f CFBundleLoadExecutable took %f seconds", currentTime, duration);
257    isLoaded = YES;
258
259    NP_Initialize = (NP_InitializeFuncPtr)CFBundleGetFunctionPointerForName(cfBundle.get(), CFSTR("NP_Initialize"));
260    NP_GetEntryPoints = (NP_GetEntryPointsFuncPtr)CFBundleGetFunctionPointerForName(cfBundle.get(), CFSTR("NP_GetEntryPoints"));
261    NP_Shutdown = (NPP_ShutdownProcPtr)CFBundleGetFunctionPointerForName(cfBundle.get(), CFSTR("NP_Shutdown"));
262    if (!NP_Initialize || !NP_GetEntryPoints || !NP_Shutdown)
263        return NO;
264
265#if COMPILER(CLANG)
266#pragma clang diagnostic push
267#pragma clang diagnostic ignored "-Wdeprecated-declarations"
268#endif
269    // Plugins (at least QT) require that you call UseResFile on the resource file before loading it.
270    resourceRef = [self openResourceFile];
271    if (resourceRef != -1) {
272        UseResFile(resourceRef);
273    }
274#if COMPILER(CLANG)
275#pragma clang diagnostic pop
276#endif
277
278    browserFuncs.version = NP_VERSION_MINOR;
279    browserFuncs.size = sizeof(NPNetscapeFuncs);
280    browserFuncs.geturl = NPN_GetURL;
281    browserFuncs.posturl = NPN_PostURL;
282    browserFuncs.requestread = NPN_RequestRead;
283    browserFuncs.newstream = NPN_NewStream;
284    browserFuncs.write = NPN_Write;
285    browserFuncs.destroystream = NPN_DestroyStream;
286    browserFuncs.status = NPN_Status;
287    browserFuncs.uagent = NPN_UserAgent;
288    browserFuncs.memalloc = NPN_MemAlloc;
289    browserFuncs.memfree = NPN_MemFree;
290    browserFuncs.memflush = NPN_MemFlush;
291    browserFuncs.reloadplugins = NPN_ReloadPlugins;
292    browserFuncs.geturlnotify = NPN_GetURLNotify;
293    browserFuncs.posturlnotify = NPN_PostURLNotify;
294    browserFuncs.getvalue = NPN_GetValue;
295    browserFuncs.setvalue = NPN_SetValue;
296    browserFuncs.invalidaterect = NPN_InvalidateRect;
297    browserFuncs.invalidateregion = NPN_InvalidateRegion;
298    browserFuncs.forceredraw = NPN_ForceRedraw;
299    browserFuncs.getJavaEnv = NPN_GetJavaEnv;
300    browserFuncs.getJavaPeer = NPN_GetJavaPeer;
301    browserFuncs.pushpopupsenabledstate = NPN_PushPopupsEnabledState;
302    browserFuncs.poppopupsenabledstate = NPN_PopPopupsEnabledState;
303    browserFuncs.pluginthreadasynccall = NPN_PluginThreadAsyncCall;
304    browserFuncs.getvalueforurl = NPN_GetValueForURL;
305    browserFuncs.setvalueforurl = NPN_SetValueForURL;
306    browserFuncs.getauthenticationinfo = NPN_GetAuthenticationInfo;
307    browserFuncs.scheduletimer = NPN_ScheduleTimer;
308    browserFuncs.unscheduletimer = NPN_UnscheduleTimer;
309    browserFuncs.popupcontextmenu = NPN_PopUpContextMenu;
310    browserFuncs.convertpoint = NPN_ConvertPoint;
311
312    browserFuncs.releasevariantvalue = _NPN_ReleaseVariantValue;
313    browserFuncs.getstringidentifier = _NPN_GetStringIdentifier;
314    browserFuncs.getstringidentifiers = _NPN_GetStringIdentifiers;
315    browserFuncs.getintidentifier = _NPN_GetIntIdentifier;
316    browserFuncs.identifierisstring = _NPN_IdentifierIsString;
317    browserFuncs.utf8fromidentifier = _NPN_UTF8FromIdentifier;
318    browserFuncs.intfromidentifier = _NPN_IntFromIdentifier;
319    browserFuncs.createobject = _NPN_CreateObject;
320    browserFuncs.retainobject = _NPN_RetainObject;
321    browserFuncs.releaseobject = _NPN_ReleaseObject;
322    browserFuncs.hasmethod = _NPN_HasMethod;
323    browserFuncs.invoke = _NPN_Invoke;
324    browserFuncs.invokeDefault = _NPN_InvokeDefault;
325    browserFuncs.evaluate = _NPN_Evaluate;
326    browserFuncs.hasproperty = _NPN_HasProperty;
327    browserFuncs.getproperty = _NPN_GetProperty;
328    browserFuncs.setproperty = _NPN_SetProperty;
329    browserFuncs.removeproperty = _NPN_RemoveProperty;
330    browserFuncs.setexception = _NPN_SetException;
331    browserFuncs.enumerate = _NPN_Enumerate;
332    browserFuncs.construct = _NPN_Construct;
333
334#if !LOG_DISABLED
335    CFAbsoluteTime initializeStart = CFAbsoluteTimeGetCurrent();
336#endif
337    LOG(Plugins, "%f NP_Initialize timing started", initializeStart);
338    npErr = NP_Initialize(&browserFuncs);
339    if (npErr != NPERR_NO_ERROR)
340        return NO;
341#if !LOG_DISABLED
342    currentTime = CFAbsoluteTimeGetCurrent();
343    duration = currentTime - initializeStart;
344#endif
345    LOG(Plugins, "%f NP_Initialize took %f seconds", currentTime, duration);
346
347    pluginFuncs.size = sizeof(NPPluginFuncs);
348
349    npErr = NP_GetEntryPoints(&pluginFuncs);
350    if (npErr != NPERR_NO_ERROR)
351        return NO;
352
353    pluginSize = pluginFuncs.size;
354    pluginVersion = pluginFuncs.version;
355
356    if (pluginFuncs.javaClass)
357        LOG(LiveConnect, "%@:  mach-o entry point for NPP_GetJavaClass = %p", (NSString *)[self pluginInfo].name, pluginFuncs.javaClass);
358    else
359        LOG(LiveConnect, "%@:  no entry point for NPP_GetJavaClass", (NSString *)[self pluginInfo].name);
360
361#if !LOG_DISABLED
362    currentTime = CFAbsoluteTimeGetCurrent();
363    duration = currentTime - start;
364#endif
365    LOG(Plugins, "%f Total load time: %f seconds", currentTime, duration);
366
367    return YES;
368}
369
370- (BOOL)load
371{
372    if ([self _tryLoad])
373        return [super load];
374
375    [self _unloadWithShutdown:NO];
376    return NO;
377}
378
379- (NPPluginFuncs *)pluginFuncs
380{
381    return &pluginFuncs;
382}
383
384- (NPNetscapeFuncs *)browserFuncs
385{
386    return &browserFuncs;
387}
388
389- (void)wasRemovedFromPluginDatabase:(WebPluginDatabase *)database
390{
391    [super wasRemovedFromPluginDatabase:database];
392
393    // Unload when removed from final plug-in database
394    if ([pluginDatabases count] == 0)
395        [self _unloadWithShutdown:YES];
396}
397
398- (void)open
399{
400    instanceCount++;
401
402    // Handle the case where all instances close a plug-in package, but another
403    // instance opens the package before it is unloaded (which only happens when
404    // the plug-in database is refreshed)
405    needsUnload = NO;
406
407    if (!isLoaded) {
408        // Should load when the first instance opens the plug-in package
409        ASSERT(instanceCount == 1);
410        [self load];
411    }
412}
413
414- (void)close
415{
416    ASSERT(instanceCount > 0);
417    instanceCount--;
418    if (instanceCount == 0 && needsUnload)
419        [self _unloadWithShutdown:YES];
420}
421
422
423- (BOOL)supportsSnapshotting
424{
425    if ([self bundleIdentifier] != "com.macromedia.Flash Player.plugin")
426        return YES;
427
428    // Flash has a bogus Info.plist entry for CFBundleVersionString, so use CFBundleShortVersionString.
429    NSString *versionString = (NSString *)CFDictionaryGetValue(CFBundleGetInfoDictionary(cfBundle.get()), CFSTR("CFBundleShortVersionString"));
430
431    if (![versionString hasPrefix:@"10.1"])
432        return YES;
433
434    // Some prerelease versions of Flash 10.1 crash when sent a drawRect event using the CA drawing model: <rdar://problem/7739922>
435    return CFStringCompare((CFStringRef)versionString, CFSTR("10.1.53.60"), kCFCompareNumerically) != kCFCompareLessThan;
436}
437
438@end
439
440@implementation WebNetscapePluginPackage (Internal)
441
442- (void)_unloadWithShutdown:(BOOL)shutdown
443{
444    if (!isLoaded)
445        return;
446
447    LOG(Plugins, "Unloading %@...", (NSString *)pluginInfo.name);
448
449    // Cannot unload a plug-in package while an instance is still using it
450    if (instanceCount > 0) {
451        needsUnload = YES;
452        return;
453    }
454
455    if (shutdown && NP_Shutdown)
456        NP_Shutdown();
457
458    if (resourceRef != -1)
459        [self closeResourceFile:resourceRef];
460
461    LOG(Plugins, "Plugin Unloaded");
462    isLoaded = NO;
463}
464
465@end
466#endif
467