Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "nsScriptNameSpaceManager.h"
8 : #include "nsCOMPtr.h"
9 : #include "nsIComponentManager.h"
10 : #include "nsIComponentRegistrar.h"
11 : #include "nsICategoryManager.h"
12 : #include "nsIServiceManager.h"
13 : #include "nsXPCOM.h"
14 : #include "nsISupportsPrimitives.h"
15 : #include "nsIScriptNameSpaceManager.h"
16 : #include "nsIScriptContext.h"
17 : #include "nsIInterfaceInfoManager.h"
18 : #include "nsIInterfaceInfo.h"
19 : #include "xptinfo.h"
20 : #include "nsXPIDLString.h"
21 : #include "nsReadableUtils.h"
22 : #include "nsHashKeys.h"
23 : #include "nsDOMClassInfo.h"
24 : #include "nsCRT.h"
25 : #include "nsIObserverService.h"
26 : #include "nsISimpleEnumerator.h"
27 : #include "mozilla/dom/BindingUtils.h"
28 : #include "mozilla/dom/WebIDLGlobalNameHash.h"
29 : #include "mozilla/MemoryReporting.h"
30 : #include "mozilla/Preferences.h"
31 : #include "mozilla/Services.h"
32 :
33 : #define NS_INTERFACE_PREFIX "nsI"
34 : #define NS_DOM_INTERFACE_PREFIX "nsIDOM"
35 :
36 : using namespace mozilla;
37 : using namespace mozilla::dom;
38 :
39 : static PLDHashNumber
40 3195 : GlobalNameHashHashKey(const void *key)
41 : {
42 3195 : const nsAString *str = static_cast<const nsAString *>(key);
43 3195 : return HashString(*str);
44 : }
45 :
46 : static bool
47 32 : GlobalNameHashMatchEntry(const PLDHashEntryHdr *entry, const void *key)
48 : {
49 : const GlobalNameMapEntry *e =
50 32 : static_cast<const GlobalNameMapEntry *>(entry);
51 32 : const nsAString *str = static_cast<const nsAString *>(key);
52 :
53 32 : return str->Equals(e->mKey);
54 : }
55 :
56 : static void
57 0 : GlobalNameHashClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
58 : {
59 0 : GlobalNameMapEntry *e = static_cast<GlobalNameMapEntry *>(entry);
60 :
61 : // An entry is being cleared, let the key (nsString) do its own
62 : // cleanup.
63 0 : e->mKey.~nsString();
64 :
65 : // This will set e->mGlobalName.mType to
66 : // nsGlobalNameStruct::eTypeNotInitialized
67 0 : memset(&e->mGlobalName, 0, sizeof(nsGlobalNameStruct));
68 0 : }
69 :
70 : static void
71 33 : GlobalNameHashInitEntry(PLDHashEntryHdr *entry, const void *key)
72 : {
73 33 : GlobalNameMapEntry *e = static_cast<GlobalNameMapEntry *>(entry);
74 33 : const nsAString *keyStr = static_cast<const nsAString *>(key);
75 :
76 : // Initialize the key in the entry with placement new
77 33 : new (&e->mKey) nsString(*keyStr);
78 :
79 : // This will set e->mGlobalName.mType to
80 : // nsGlobalNameStruct::eTypeNotInitialized
81 33 : memset(&e->mGlobalName, 0, sizeof(nsGlobalNameStruct));
82 33 : }
83 :
84 62 : NS_IMPL_ISUPPORTS(
85 : nsScriptNameSpaceManager,
86 : nsIObserver,
87 : nsISupportsWeakReference,
88 : nsIMemoryReporter)
89 :
90 : static const PLDHashTableOps hash_table_ops =
91 : {
92 : GlobalNameHashHashKey,
93 : GlobalNameHashMatchEntry,
94 : PLDHashTable::MoveEntryStub,
95 : GlobalNameHashClearEntry,
96 : GlobalNameHashInitEntry
97 : };
98 :
99 : #define GLOBALNAME_HASHTABLE_INITIAL_LENGTH 32
100 :
101 3 : nsScriptNameSpaceManager::nsScriptNameSpaceManager()
102 : : mGlobalNames(&hash_table_ops, sizeof(GlobalNameMapEntry),
103 3 : GLOBALNAME_HASHTABLE_INITIAL_LENGTH)
104 : {
105 3 : }
106 :
107 0 : nsScriptNameSpaceManager::~nsScriptNameSpaceManager()
108 : {
109 0 : UnregisterWeakMemoryReporter(this);
110 0 : }
111 :
112 : nsGlobalNameStruct *
113 63 : nsScriptNameSpaceManager::AddToHash(const char *aKey,
114 : const char16_t **aClassName)
115 : {
116 126 : NS_ConvertASCIItoUTF16 key(aKey);
117 63 : auto entry = static_cast<GlobalNameMapEntry*>(mGlobalNames.Add(&key, fallible));
118 63 : if (!entry) {
119 0 : return nullptr;
120 : }
121 :
122 63 : WebIDLGlobalNameHash::Remove(aKey, key.Length());
123 :
124 63 : if (aClassName) {
125 30 : *aClassName = entry->mKey.get();
126 : }
127 :
128 63 : return &entry->mGlobalName;
129 : }
130 :
131 : void
132 0 : nsScriptNameSpaceManager::RemoveFromHash(const nsAString *aKey)
133 : {
134 0 : mGlobalNames.Remove(aKey);
135 0 : }
136 :
137 : nsresult
138 9 : nsScriptNameSpaceManager::FillHash(nsICategoryManager *aCategoryManager,
139 : const char *aCategory)
140 : {
141 18 : nsCOMPtr<nsISimpleEnumerator> e;
142 9 : nsresult rv = aCategoryManager->EnumerateCategory(aCategory,
143 18 : getter_AddRefs(e));
144 9 : NS_ENSURE_SUCCESS(rv, rv);
145 :
146 18 : nsCOMPtr<nsISupports> entry;
147 15 : while (NS_SUCCEEDED(e->GetNext(getter_AddRefs(entry)))) {
148 3 : rv = AddCategoryEntryToHash(aCategoryManager, aCategory, entry);
149 3 : if (NS_FAILED(rv)) {
150 0 : return rv;
151 : }
152 : }
153 :
154 9 : return NS_OK;
155 : }
156 :
157 :
158 : nsresult
159 3 : nsScriptNameSpaceManager::Init()
160 : {
161 3 : RegisterWeakMemoryReporter(this);
162 :
163 3 : nsresult rv = NS_OK;
164 :
165 : nsCOMPtr<nsICategoryManager> cm =
166 6 : do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
167 3 : NS_ENSURE_SUCCESS(rv, rv);
168 :
169 3 : rv = FillHash(cm, JAVASCRIPT_GLOBAL_CONSTRUCTOR_CATEGORY);
170 3 : NS_ENSURE_SUCCESS(rv, rv);
171 :
172 3 : rv = FillHash(cm, JAVASCRIPT_GLOBAL_PROPERTY_CATEGORY);
173 3 : NS_ENSURE_SUCCESS(rv, rv);
174 :
175 3 : rv = FillHash(cm, JAVASCRIPT_GLOBAL_PRIVILEGED_PROPERTY_CATEGORY);
176 3 : NS_ENSURE_SUCCESS(rv, rv);
177 :
178 : // Initial filling of the has table has been done.
179 : // Now, listen for changes.
180 : nsCOMPtr<nsIObserverService> serv =
181 6 : mozilla::services::GetObserverService();
182 :
183 3 : if (serv) {
184 3 : serv->AddObserver(this, NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID, true);
185 3 : serv->AddObserver(this, NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID, true);
186 : }
187 :
188 3 : return NS_OK;
189 : }
190 :
191 : const nsGlobalNameStruct*
192 3132 : nsScriptNameSpaceManager::LookupName(const nsAString& aName,
193 : const char16_t **aClassName)
194 : {
195 3132 : auto entry = static_cast<GlobalNameMapEntry*>(mGlobalNames.Search(&aName));
196 :
197 3132 : if (entry) {
198 2 : if (aClassName) {
199 0 : *aClassName = entry->mKey.get();
200 : }
201 2 : return &entry->mGlobalName;
202 : }
203 :
204 3130 : if (aClassName) {
205 3130 : *aClassName = nullptr;
206 : }
207 3130 : return nullptr;
208 : }
209 :
210 : nsresult
211 30 : nsScriptNameSpaceManager::RegisterClassName(const char *aClassName,
212 : int32_t aDOMClassInfoID,
213 : bool aPrivileged,
214 : bool aXBLAllowed,
215 : const char16_t **aResult)
216 : {
217 30 : if (!nsCRT::IsAscii(aClassName)) {
218 0 : NS_ERROR("Trying to register a non-ASCII class name");
219 0 : return NS_OK;
220 : }
221 30 : nsGlobalNameStruct *s = AddToHash(aClassName, aResult);
222 30 : NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY);
223 :
224 30 : if (s->mType == nsGlobalNameStruct::eTypeClassConstructor) {
225 0 : return NS_OK;
226 : }
227 :
228 : // If a external constructor is already defined with aClassName we
229 : // won't overwrite it.
230 :
231 30 : if (s->mType == nsGlobalNameStruct::eTypeExternalConstructor) {
232 0 : return NS_OK;
233 : }
234 :
235 30 : NS_ASSERTION(s->mType == nsGlobalNameStruct::eTypeNotInitialized,
236 : "Whaaa, JS environment name clash!");
237 :
238 30 : s->mType = nsGlobalNameStruct::eTypeClassConstructor;
239 30 : s->mDOMClassInfoID = aDOMClassInfoID;
240 30 : s->mChromeOnly = aPrivileged;
241 30 : s->mAllowXBL = aXBLAllowed;
242 :
243 30 : return NS_OK;
244 : }
245 :
246 : nsresult
247 30 : nsScriptNameSpaceManager::RegisterClassProto(const char *aClassName,
248 : const nsIID *aConstructorProtoIID,
249 : bool *aFoundOld)
250 : {
251 30 : NS_ENSURE_ARG_POINTER(aConstructorProtoIID);
252 :
253 30 : *aFoundOld = false;
254 :
255 30 : nsGlobalNameStruct *s = AddToHash(aClassName);
256 30 : NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY);
257 :
258 30 : if (s->mType != nsGlobalNameStruct::eTypeNotInitialized) {
259 30 : *aFoundOld = true;
260 :
261 30 : return NS_OK;
262 : }
263 :
264 0 : s->mType = nsGlobalNameStruct::eTypeClassProto;
265 0 : s->mIID = *aConstructorProtoIID;
266 :
267 0 : return NS_OK;
268 : }
269 :
270 : nsresult
271 7 : nsScriptNameSpaceManager::OperateCategoryEntryHash(nsICategoryManager* aCategoryManager,
272 : const char* aCategory,
273 : nsISupports* aEntry,
274 : bool aRemove)
275 : {
276 7 : MOZ_ASSERT(aCategoryManager);
277 : // Get the type from the category name.
278 : // NOTE: we could have passed the type in FillHash() and guessed it in
279 : // Observe() but this way, we have only one place to update and this is
280 : // not performance sensitive.
281 : nsGlobalNameStruct::nametype type;
282 7 : if (strcmp(aCategory, JAVASCRIPT_GLOBAL_CONSTRUCTOR_CATEGORY) == 0) {
283 0 : type = nsGlobalNameStruct::eTypeExternalConstructor;
284 11 : } else if (strcmp(aCategory, JAVASCRIPT_GLOBAL_PROPERTY_CATEGORY) == 0 ||
285 4 : strcmp(aCategory, JAVASCRIPT_GLOBAL_PRIVILEGED_PROPERTY_CATEGORY) == 0) {
286 3 : type = nsGlobalNameStruct::eTypeProperty;
287 : } else {
288 4 : return NS_OK;
289 : }
290 :
291 6 : nsCOMPtr<nsISupportsCString> strWrapper = do_QueryInterface(aEntry);
292 :
293 3 : if (!strWrapper) {
294 0 : NS_WARNING("Category entry not an nsISupportsCString!");
295 0 : return NS_OK;
296 : }
297 :
298 6 : nsAutoCString categoryEntry;
299 3 : nsresult rv = strWrapper->GetData(categoryEntry);
300 3 : NS_ENSURE_SUCCESS(rv, rv);
301 :
302 : // We need to handle removal before calling GetCategoryEntry
303 : // because the category entry is already removed before we are
304 : // notified.
305 3 : if (aRemove) {
306 0 : NS_ConvertASCIItoUTF16 entry(categoryEntry);
307 0 : const nsGlobalNameStruct *s = LookupName(entry);
308 : // Verify mType so that this API doesn't remove names
309 : // registered by others.
310 0 : if (!s || s->mType != type) {
311 0 : return NS_OK;
312 : }
313 :
314 0 : RemoveFromHash(&entry);
315 0 : return NS_OK;
316 : }
317 :
318 6 : nsXPIDLCString contractId;
319 3 : rv = aCategoryManager->GetCategoryEntry(aCategory, categoryEntry.get(),
320 6 : getter_Copies(contractId));
321 3 : NS_ENSURE_SUCCESS(rv, rv);
322 :
323 6 : nsCOMPtr<nsIComponentRegistrar> registrar;
324 3 : rv = NS_GetComponentRegistrar(getter_AddRefs(registrar));
325 3 : NS_ENSURE_SUCCESS(rv, rv);
326 :
327 : nsCID *cidPtr;
328 3 : rv = registrar->ContractIDToCID(contractId, &cidPtr);
329 :
330 3 : if (NS_FAILED(rv)) {
331 0 : NS_WARNING("Bad contract id registed with the script namespace manager");
332 0 : return NS_OK;
333 : }
334 :
335 : // Copy CID onto the stack, so we can free it right away and avoid having
336 : // to add cleanup code at every exit point from this function.
337 3 : nsCID cid = *cidPtr;
338 3 : free(cidPtr);
339 :
340 3 : nsGlobalNameStruct *s = AddToHash(categoryEntry.get());
341 3 : NS_ENSURE_TRUE(s, NS_ERROR_OUT_OF_MEMORY);
342 :
343 3 : if (s->mType == nsGlobalNameStruct::eTypeNotInitialized) {
344 3 : s->mType = type;
345 3 : s->mCID = cid;
346 3 : s->mChromeOnly =
347 3 : strcmp(aCategory, JAVASCRIPT_GLOBAL_PRIVILEGED_PROPERTY_CATEGORY) == 0;
348 : } else {
349 0 : NS_WARNING("Global script name not overwritten!");
350 : }
351 :
352 3 : return NS_OK;
353 : }
354 :
355 : nsresult
356 7 : nsScriptNameSpaceManager::AddCategoryEntryToHash(nsICategoryManager* aCategoryManager,
357 : const char* aCategory,
358 : nsISupports* aEntry)
359 : {
360 : return OperateCategoryEntryHash(aCategoryManager, aCategory, aEntry,
361 7 : /* aRemove = */ false);
362 : }
363 :
364 : nsresult
365 0 : nsScriptNameSpaceManager::RemoveCategoryEntryFromHash(nsICategoryManager* aCategoryManager,
366 : const char* aCategory,
367 : nsISupports* aEntry)
368 : {
369 : return OperateCategoryEntryHash(aCategoryManager, aCategory, aEntry,
370 0 : /* aRemove = */ true);
371 : }
372 :
373 : NS_IMETHODIMP
374 4 : nsScriptNameSpaceManager::Observe(nsISupports* aSubject, const char* aTopic,
375 : const char16_t* aData)
376 : {
377 4 : if (!aData) {
378 0 : return NS_OK;
379 : }
380 :
381 4 : if (!strcmp(aTopic, NS_XPCOM_CATEGORY_ENTRY_ADDED_OBSERVER_ID)) {
382 : nsCOMPtr<nsICategoryManager> cm =
383 8 : do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
384 4 : if (!cm) {
385 0 : return NS_OK;
386 : }
387 :
388 8 : return AddCategoryEntryToHash(cm, NS_ConvertUTF16toUTF8(aData).get(),
389 4 : aSubject);
390 0 : } else if (!strcmp(aTopic, NS_XPCOM_CATEGORY_ENTRY_REMOVED_OBSERVER_ID)) {
391 : nsCOMPtr<nsICategoryManager> cm =
392 0 : do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
393 0 : if (!cm) {
394 0 : return NS_OK;
395 : }
396 :
397 0 : return RemoveCategoryEntryFromHash(cm, NS_ConvertUTF16toUTF8(aData).get(),
398 0 : aSubject);
399 : }
400 :
401 : // TODO: we could observe NS_XPCOM_CATEGORY_CLEARED_OBSERVER_ID
402 : // but we are safe without it. See bug 600460.
403 :
404 0 : return NS_OK;
405 : }
406 :
407 0 : MOZ_DEFINE_MALLOC_SIZE_OF(ScriptNameSpaceManagerMallocSizeOf)
408 :
409 : NS_IMETHODIMP
410 0 : nsScriptNameSpaceManager::CollectReports(
411 : nsIHandleReportCallback* aHandleReport, nsISupports* aData, bool aAnonymize)
412 : {
413 0 : MOZ_COLLECT_REPORT(
414 : "explicit/script-namespace-manager", KIND_HEAP, UNITS_BYTES,
415 : SizeOfIncludingThis(ScriptNameSpaceManagerMallocSizeOf),
416 0 : "Memory used for the script namespace manager.");
417 :
418 0 : return NS_OK;
419 : }
420 :
421 : size_t
422 0 : nsScriptNameSpaceManager::SizeOfIncludingThis(
423 : mozilla::MallocSizeOf aMallocSizeOf) const
424 : {
425 0 : size_t n = 0;
426 :
427 0 : n += mGlobalNames.ShallowSizeOfExcludingThis(aMallocSizeOf);
428 0 : for (auto iter = mGlobalNames.ConstIter(); !iter.Done(); iter.Next()) {
429 0 : auto entry = static_cast<GlobalNameMapEntry*>(iter.Get());
430 0 : n += entry->SizeOfExcludingThis(aMallocSizeOf);
431 : }
432 :
433 0 : return n;
434 : }
|