2 * Copyright (c) 2013 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 Copyright (c) 1999-2013, Apple Inc. All rights reserved.
26 Responsibility: Tony Parker
29 #include "CFBundle_Internal.h"
30 #include "CFInternal.h"
33 static void _registerFactory(const void *key
, const void *val
, void *context
) {
34 CFStringRef factoryIDStr
= (CFStringRef
)key
;
35 CFStringRef factoryFuncStr
= (CFStringRef
)val
;
36 CFBundleRef bundle
= (CFBundleRef
)context
;
37 CFUUIDRef factoryID
= (CFGetTypeID(factoryIDStr
) == CFStringGetTypeID()) ? CFUUIDCreateFromString(kCFAllocatorSystemDefault
, factoryIDStr
) : NULL
;
38 if (!factoryID
) factoryID
= (CFUUIDRef
)CFRetain(factoryIDStr
);
39 if (CFGetTypeID(factoryFuncStr
) != CFStringGetTypeID() || CFStringGetLength(factoryFuncStr
) <= 0) factoryFuncStr
= NULL
;
40 CFPlugInRegisterFactoryFunctionByName(factoryID
, bundle
, factoryFuncStr
);
41 if (factoryID
) CFRelease(factoryID
);
44 static void _registerType(const void *key
, const void *val
, void *context
) {
45 CFStringRef typeIDStr
= (CFStringRef
)key
;
46 CFArrayRef factoryIDStrArray
= (CFArrayRef
)val
;
47 CFBundleRef bundle
= (CFBundleRef
)context
;
48 SInt32 i
, c
= (CFGetTypeID(factoryIDStrArray
) == CFArrayGetTypeID()) ? CFArrayGetCount(factoryIDStrArray
) : 0;
49 CFStringRef curFactoryIDStr
;
50 CFUUIDRef typeID
= (CFGetTypeID(typeIDStr
) == CFStringGetTypeID()) ? CFUUIDCreateFromString(kCFAllocatorSystemDefault
, typeIDStr
) : NULL
;
51 CFUUIDRef curFactoryID
;
52 if (!typeID
) typeID
= (CFUUIDRef
)CFRetain(typeIDStr
);
53 if (0 == c
&& CFGetTypeID(factoryIDStrArray
) != CFArrayGetTypeID()) {
54 curFactoryIDStr
= (CFStringRef
)val
;
55 curFactoryID
= (CFGetTypeID(curFactoryIDStr
) == CFStringGetTypeID()) ? CFUUIDCreateFromString(CFGetAllocator(bundle
), curFactoryIDStr
) : NULL
;
56 if (!curFactoryID
) curFactoryID
= (CFUUIDRef
)CFRetain(curFactoryIDStr
);
57 CFPlugInRegisterPlugInType(curFactoryID
, typeID
);
58 if (curFactoryID
) CFRelease(curFactoryID
);
59 } else for (i
= 0; i
< c
; i
++) {
60 curFactoryIDStr
= (CFStringRef
)CFArrayGetValueAtIndex(factoryIDStrArray
, i
);
61 curFactoryID
= (CFGetTypeID(curFactoryIDStr
) == CFStringGetTypeID()) ? CFUUIDCreateFromString(CFGetAllocator(bundle
), curFactoryIDStr
) : NULL
;
62 if (!curFactoryID
) curFactoryID
= (CFUUIDRef
)CFRetain(curFactoryIDStr
);
63 CFPlugInRegisterPlugInType(curFactoryID
, typeID
);
64 if (curFactoryID
) CFRelease(curFactoryID
);
66 if (typeID
) CFRelease(typeID
);
69 CF_PRIVATE Boolean
_CFBundleNeedsInitPlugIn(CFBundleRef bundle
) {
70 Boolean result
= false;
71 CFDictionaryRef infoDict
= CFBundleGetInfoDictionary(bundle
), factoryDict
;
74 factoryDict
= (CFDictionaryRef
)CFDictionaryGetValue(infoDict
, kCFPlugInFactoriesKey
);
75 if (factoryDict
&& CFGetTypeID(factoryDict
) == CFDictionaryGetTypeID()) result
= true;
76 tempStr
= (CFStringRef
)CFDictionaryGetValue(infoDict
, kCFPlugInDynamicRegistrationKey
);
77 if (tempStr
&& CFGetTypeID(tempStr
) == CFStringGetTypeID() && CFStringCompare(tempStr
, CFSTR("YES"), kCFCompareCaseInsensitive
) == kCFCompareEqualTo
) result
= true;
82 CF_PRIVATE
void _CFBundleInitPlugIn(CFBundleRef bundle
) {
83 CFArrayCallBacks _pluginFactoryArrayCallbacks
= {0, NULL
, NULL
, NULL
, NULL
};
84 Boolean doDynamicReg
= false;
85 CFDictionaryRef infoDict
;
86 CFDictionaryRef factoryDict
;
87 CFDictionaryRef typeDict
;
90 infoDict
= CFBundleGetInfoDictionary(bundle
);
91 if (!infoDict
) return;
93 factoryDict
= (CFDictionaryRef
)CFDictionaryGetValue(infoDict
, kCFPlugInFactoriesKey
);
94 if (factoryDict
&& CFGetTypeID(factoryDict
) != CFDictionaryGetTypeID()) factoryDict
= NULL
;
95 tempStr
= (CFStringRef
)CFDictionaryGetValue(infoDict
, kCFPlugInDynamicRegistrationKey
);
96 if (tempStr
&& CFGetTypeID(tempStr
) == CFStringGetTypeID() && CFStringCompare(tempStr
, CFSTR("YES"), kCFCompareCaseInsensitive
) == kCFCompareEqualTo
) doDynamicReg
= true;
97 if (!factoryDict
&& !doDynamicReg
) return; // This is not a plug-in.
99 /* loadOnDemand is true by default if the plugIn does not do dynamic registration. It is false, by default if it does do dynamic registration. The dynamic register function can set this. */
100 __CFBundleGetPlugInData(bundle
)->_isPlugIn
= true;
101 __CFBundleGetPlugInData(bundle
)->_loadOnDemand
= true;
102 __CFBundleGetPlugInData(bundle
)->_isDoingDynamicRegistration
= false;
103 __CFBundleGetPlugInData(bundle
)->_instanceCount
= 0;
105 __CFBundleGetPlugInData(bundle
)->_factories
= CFArrayCreateMutable(CFGetAllocator(bundle
), 0, &_pluginFactoryArrayCallbacks
);
107 /* Now do the registration */
109 /* First do static registrations, if any. */
110 if (factoryDict
) CFDictionaryApplyFunction(factoryDict
, _registerFactory
, bundle
);
111 typeDict
= (CFDictionaryRef
)CFDictionaryGetValue(infoDict
, kCFPlugInTypesKey
);
112 if (typeDict
&& CFGetTypeID(typeDict
) != CFDictionaryGetTypeID()) typeDict
= NULL
;
113 if (typeDict
) CFDictionaryApplyFunction(typeDict
, _registerType
, bundle
);
115 /* Now set key for dynamic registration if necessary */
117 CFDictionarySetValue((CFMutableDictionaryRef
)infoDict
, CFSTR("CFPlugInNeedsDynamicRegistration"), CFSTR("YES"));
118 if (CFBundleIsExecutableLoaded(bundle
)) _CFBundlePlugInLoaded(bundle
);
122 CF_PRIVATE
void _CFBundlePlugInLoaded(CFBundleRef bundle
) {
123 CFDictionaryRef infoDict
= CFBundleGetInfoDictionary(bundle
);
125 CFPlugInDynamicRegisterFunction func
= NULL
;
127 if (!__CFBundleGetPlugInData(bundle
)->_isPlugIn
|| __CFBundleGetPlugInData(bundle
)->_isDoingDynamicRegistration
|| !infoDict
|| !CFBundleIsExecutableLoaded(bundle
)) return;
129 tempStr
= (CFStringRef
)CFDictionaryGetValue(infoDict
, CFSTR("CFPlugInNeedsDynamicRegistration"));
130 if (tempStr
&& CFGetTypeID(tempStr
) == CFStringGetTypeID() && CFStringCompare(tempStr
, CFSTR("YES"), kCFCompareCaseInsensitive
) == kCFCompareEqualTo
) {
131 CFDictionaryRemoveValue((CFMutableDictionaryRef
)infoDict
, CFSTR("CFPlugInNeedsDynamicRegistration"));
132 tempStr
= (CFStringRef
)CFDictionaryGetValue(infoDict
, kCFPlugInDynamicRegisterFunctionKey
);
133 if (!tempStr
|| CFGetTypeID(tempStr
) != CFStringGetTypeID() || CFStringGetLength(tempStr
) <= 0) tempStr
= CFSTR("CFPlugInDynamicRegister");
134 __CFBundleGetPlugInData(bundle
)->_loadOnDemand
= false;
135 __CFBundleGetPlugInData(bundle
)->_isDoingDynamicRegistration
= true;
137 /* Find the symbol and call it. */
138 func
= (CFPlugInDynamicRegisterFunction
)CFBundleGetFunctionPointerForName(bundle
, tempStr
);
141 // MF:!!! Unload function is never called. Need to deal with this!
144 __CFBundleGetPlugInData(bundle
)->_isDoingDynamicRegistration
= false;
145 if (__CFBundleGetPlugInData(bundle
)->_loadOnDemand
&& __CFBundleGetPlugInData(bundle
)->_instanceCount
== 0) CFBundleUnloadExecutable(bundle
); // Unload now if we can/should.
147 CFDictionaryRemoveValue((CFMutableDictionaryRef
)infoDict
, CFSTR("CFPlugInNeedsDynamicRegistration"));
151 CF_PRIVATE
void _CFBundleDeallocatePlugIn(CFBundleRef bundle
) {
152 if (__CFBundleGetPlugInData(bundle
)->_isPlugIn
) {
155 /* Go through factories disabling them. Disabling these factories should cause them to dealloc since we wouldn't be deallocating if any of the factories had outstanding instances. So go backwards. */
156 c
= CFArrayGetCount(__CFBundleGetPlugInData(bundle
)->_factories
);
157 while (c
-- > 0) _CFPFactoryDisable((_CFPFactoryRef
)CFArrayGetValueAtIndex(__CFBundleGetPlugInData(bundle
)->_factories
, c
));
158 CFRelease(__CFBundleGetPlugInData(bundle
)->_factories
);
160 __CFBundleGetPlugInData(bundle
)->_isPlugIn
= false;
164 CFTypeID
CFPlugInGetTypeID(void) {
165 return CFBundleGetTypeID();
168 CFPlugInRef
CFPlugInCreate(CFAllocatorRef allocator
, CFURLRef plugInURL
) {
169 CFBundleRef bundle
= CFBundleCreate(allocator
, plugInURL
);
170 return (CFPlugInRef
)bundle
;
173 CFBundleRef
CFPlugInGetBundle(CFPlugInRef plugIn
) {
174 return (CFBundleRef
)plugIn
;
177 void CFPlugInSetLoadOnDemand(CFPlugInRef plugIn
, Boolean flag
) {
178 if (__CFBundleGetPlugInData(plugIn
)->_isPlugIn
) {
179 __CFBundleGetPlugInData(plugIn
)->_loadOnDemand
= flag
;
180 if (__CFBundleGetPlugInData(plugIn
)->_loadOnDemand
&& !__CFBundleGetPlugInData(plugIn
)->_isDoingDynamicRegistration
&& __CFBundleGetPlugInData(plugIn
)->_instanceCount
== 0) {
181 /* Unload now if we can/should. */
182 /* If we are doing dynamic registration currently, do not unload. The unloading will happen when dynamic registration is done, if necessary. */
183 CFBundleUnloadExecutable(plugIn
);
184 } else if (!__CFBundleGetPlugInData(plugIn
)->_loadOnDemand
) {
185 /* Make sure we're loaded now. */
186 CFBundleLoadExecutable(plugIn
);
191 Boolean
CFPlugInIsLoadOnDemand(CFPlugInRef plugIn
) {
192 if (__CFBundleGetPlugInData(plugIn
)->_isPlugIn
) {
193 return __CFBundleGetPlugInData(plugIn
)->_loadOnDemand
;
199 CF_PRIVATE
void _CFPlugInWillUnload(CFPlugInRef plugIn
) {
200 if (__CFBundleGetPlugInData(plugIn
)->_isPlugIn
) {
201 SInt32 c
= CFArrayGetCount(__CFBundleGetPlugInData(plugIn
)->_factories
);
202 /* First, flush all the function pointers that may be cached by our factories. */
203 while (c
-- > 0) _CFPFactoryFlushFunctionCache((_CFPFactoryRef
)CFArrayGetValueAtIndex(__CFBundleGetPlugInData(plugIn
)->_factories
, c
));
207 CF_PRIVATE
void _CFPlugInAddPlugInInstance(CFPlugInRef plugIn
) {
208 if (__CFBundleGetPlugInData(plugIn
)->_isPlugIn
) {
209 if (__CFBundleGetPlugInData(plugIn
)->_instanceCount
== 0 && __CFBundleGetPlugInData(plugIn
)->_loadOnDemand
) _CFBundleUnscheduleForUnloading(CFPlugInGetBundle(plugIn
)); // Make sure we are not scheduled for unloading
210 __CFBundleGetPlugInData(plugIn
)->_instanceCount
++;
211 /* Instances also retain the CFBundle */
216 CF_PRIVATE
void _CFPlugInRemovePlugInInstance(CFPlugInRef plugIn
) {
217 if (__CFBundleGetPlugInData(plugIn
)->_isPlugIn
) {
218 /* MF:!!! Assert that instanceCount > 0. */
219 __CFBundleGetPlugInData(plugIn
)->_instanceCount
--;
220 if (__CFBundleGetPlugInData(plugIn
)->_instanceCount
== 0 && __CFBundleGetPlugInData(plugIn
)->_loadOnDemand
) {
221 // We unload the code lazily because the code that caused this function to be called is probably code from the plugin itself. If we unload now, we will hose things.
222 //CFBundleUnloadExecutable(plugIn);
223 _CFBundleScheduleForUnloading(CFPlugInGetBundle(plugIn
));
225 /* Instances also retain the CFPlugIn */
226 /* MF:!!! This will cause immediate unloading if it was the last ref on the plugin. */
231 CF_PRIVATE
void _CFPlugInAddFactory(CFPlugInRef plugIn
, _CFPFactoryRef factory
) {
232 if (__CFBundleGetPlugInData(plugIn
)->_isPlugIn
) CFArrayAppendValue(__CFBundleGetPlugInData(plugIn
)->_factories
, factory
);
235 CF_PRIVATE
void _CFPlugInRemoveFactory(CFPlugInRef plugIn
, _CFPFactoryRef factory
) {
236 if (__CFBundleGetPlugInData(plugIn
)->_isPlugIn
) {
237 SInt32 idx
= CFArrayGetFirstIndexOfValue(__CFBundleGetPlugInData(plugIn
)->_factories
, CFRangeMake(0, CFArrayGetCount(__CFBundleGetPlugInData(plugIn
)->_factories
)), factory
);
238 if (idx
>= 0) CFArrayRemoveValueAtIndex(__CFBundleGetPlugInData(plugIn
)->_factories
, idx
);