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