1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=2 sw=2 et tw=79: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla Communicator client code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Original Author: David W. Hyatt (hyatt@netscape.com)
25 : * Alec Flett <alecf@netscape.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "nsCOMPtr.h"
42 : #include "nsIXBLService.h"
43 : #include "nsIInputStream.h"
44 : #include "nsIURI.h"
45 : #include "nsIURL.h"
46 : #include "nsIChannel.h"
47 : #include "nsXPIDLString.h"
48 : #include "nsIParser.h"
49 : #include "nsParserCIID.h"
50 : #include "nsNetUtil.h"
51 : #include "plstr.h"
52 : #include "nsIContent.h"
53 : #include "nsIDOMElement.h"
54 : #include "nsIDocument.h"
55 : #include "mozilla/FunctionTimer.h"
56 : #include "nsContentUtils.h"
57 : #include "nsIPresShell.h"
58 : #include "nsIXMLContentSink.h"
59 : #include "nsContentCID.h"
60 : #include "nsXMLDocument.h"
61 : #include "nsIStreamListener.h"
62 :
63 : #include "nsXBLBinding.h"
64 : #include "nsXBLPrototypeBinding.h"
65 : #include "nsXBLDocumentInfo.h"
66 : #include "nsXBLInsertionPoint.h"
67 :
68 : #include "nsIStyleRuleProcessor.h"
69 : #include "nsRuleProcessorData.h"
70 : #include "nsIWeakReference.h"
71 :
72 : #include "jsapi.h"
73 : #include "nsIXPConnect.h"
74 : #include "nsDOMCID.h"
75 : #include "nsIDOMScriptObjectFactory.h"
76 : #include "nsIScriptGlobalObject.h"
77 : #include "nsTHashtable.h"
78 :
79 : #include "nsIScriptContext.h"
80 : #include "nsBindingManager.h"
81 :
82 : #include "nsThreadUtils.h"
83 : #include "dombindings.h"
84 :
85 : // ==================================================================
86 : // = nsAnonymousContentList
87 : // ==================================================================
88 :
89 : #define NS_ANONYMOUS_CONTENT_LIST_IID \
90 : { 0xbfb5d8e7, 0xf718, 0x4a46, \
91 : { 0xb2, 0x2b, 0x22, 0x4a, 0x44, 0x4c, 0xb9, 0x77 } }
92 :
93 : class nsAnonymousContentList : public nsINodeList
94 : {
95 : public:
96 : nsAnonymousContentList(nsIContent *aContent, nsInsertionPointList* aElements);
97 : virtual ~nsAnonymousContentList();
98 :
99 0 : NS_DECL_CYCLE_COLLECTING_ISUPPORTS
100 1464 : NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsAnonymousContentList)
101 : // nsIDOMNodeList interface
102 : NS_DECL_NSIDOMNODELIST
103 :
104 : // nsINodeList interface
105 : virtual PRInt32 IndexOf(nsIContent* aContent);
106 0 : virtual nsINode *GetParentObject()
107 : {
108 0 : return mContent;
109 : }
110 :
111 0 : PRInt32 GetInsertionPointCount() { return mElements->Length(); }
112 :
113 0 : nsXBLInsertionPoint* GetInsertionPointAt(PRInt32 i) { return static_cast<nsXBLInsertionPoint*>(mElements->ElementAt(i)); }
114 : void RemoveInsertionPointAt(PRInt32 i) { mElements->RemoveElementAt(i); }
115 :
116 0 : virtual JSObject* WrapObject(JSContext *cx, XPCWrappedNativeScope *scope,
117 : bool *triedToWrap)
118 : {
119 : return mozilla::dom::binding::NodeList::create(cx, scope, this,
120 0 : triedToWrap);
121 : }
122 :
123 : NS_DECLARE_STATIC_IID_ACCESSOR(NS_ANONYMOUS_CONTENT_LIST_IID)
124 : private:
125 : nsCOMPtr<nsIContent> mContent;
126 : nsInsertionPointList* mElements;
127 : };
128 :
129 : NS_DEFINE_STATIC_IID_ACCESSOR(nsAnonymousContentList,
130 : NS_ANONYMOUS_CONTENT_LIST_IID)
131 :
132 0 : nsAnonymousContentList::nsAnonymousContentList(nsIContent *aContent,
133 : nsInsertionPointList* aElements)
134 : : mContent(aContent),
135 0 : mElements(aElements)
136 : {
137 0 : MOZ_COUNT_CTOR(nsAnonymousContentList);
138 :
139 : // We don't reference count our Anonymous reference (to avoid circular
140 : // references). We'll be told when the Anonymous goes away.
141 0 : SetIsProxy();
142 0 : }
143 :
144 0 : nsAnonymousContentList::~nsAnonymousContentList()
145 : {
146 0 : MOZ_COUNT_DTOR(nsAnonymousContentList);
147 0 : delete mElements;
148 0 : }
149 :
150 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsAnonymousContentList)
151 :
152 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAnonymousContentList)
153 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAnonymousContentList)
154 :
155 0 : NS_INTERFACE_TABLE_HEAD(nsAnonymousContentList)
156 0 : NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
157 : NS_NODELIST_OFFSET_AND_INTERFACE_TABLE_BEGIN(nsAnonymousContentList)
158 : NS_INTERFACE_TABLE_ENTRY(nsAnonymousContentList, nsINodeList)
159 : NS_INTERFACE_TABLE_ENTRY(nsAnonymousContentList, nsIDOMNodeList)
160 : NS_INTERFACE_TABLE_ENTRY(nsAnonymousContentList, nsAnonymousContentList)
161 0 : NS_OFFSET_AND_INTERFACE_TABLE_END
162 0 : NS_OFFSET_AND_INTERFACE_TABLE_TO_MAP_SEGUE
163 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(NodeList)
164 0 : NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsAnonymousContentList)
165 0 : NS_INTERFACE_MAP_END
166 :
167 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsAnonymousContentList)
168 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mContent)
169 0 : tmp->mElements->Clear();
170 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
171 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
172 :
173 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsAnonymousContentList)
174 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mContent)
175 : {
176 0 : PRInt32 i, count = tmp->mElements->Length();
177 0 : for (i = 0; i < count; ++i) {
178 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mElements->ElementAt(i),
179 : nsXBLInsertionPoint);
180 : }
181 : }
182 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
183 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
184 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsAnonymousContentList)
185 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
186 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
187 :
188 : NS_IMETHODIMP
189 0 : nsAnonymousContentList::GetLength(PRUint32* aLength)
190 : {
191 0 : NS_ASSERTION(aLength != nsnull, "null ptr");
192 0 : if (! aLength)
193 0 : return NS_ERROR_NULL_POINTER;
194 :
195 0 : PRInt32 cnt = mElements->Length();
196 :
197 0 : *aLength = 0;
198 0 : for (PRInt32 i = 0; i < cnt; i++)
199 0 : *aLength += static_cast<nsXBLInsertionPoint*>(mElements->ElementAt(i))->ChildCount();
200 :
201 0 : return NS_OK;
202 : }
203 :
204 : NS_IMETHODIMP
205 0 : nsAnonymousContentList::Item(PRUint32 aIndex, nsIDOMNode** aReturn)
206 : {
207 0 : nsINode* item = GetNodeAt(aIndex);
208 0 : if (!item)
209 0 : return NS_ERROR_FAILURE;
210 :
211 0 : return CallQueryInterface(item, aReturn);
212 : }
213 :
214 : nsIContent*
215 0 : nsAnonymousContentList::GetNodeAt(PRUint32 aIndex)
216 : {
217 0 : PRInt32 cnt = mElements->Length();
218 0 : PRUint32 pointCount = 0;
219 :
220 0 : for (PRInt32 i = 0; i < cnt; i++) {
221 0 : aIndex -= pointCount;
222 :
223 0 : nsXBLInsertionPoint* point = static_cast<nsXBLInsertionPoint*>(mElements->ElementAt(i));
224 0 : pointCount = point->ChildCount();
225 :
226 0 : if (aIndex < pointCount) {
227 0 : return point->ChildAt(aIndex);
228 : }
229 : }
230 :
231 0 : return nsnull;
232 : }
233 :
234 : PRInt32
235 0 : nsAnonymousContentList::IndexOf(nsIContent* aContent)
236 : {
237 0 : PRInt32 cnt = mElements->Length();
238 0 : PRInt32 lengthSoFar = 0;
239 :
240 0 : for (PRInt32 i = 0; i < cnt; ++i) {
241 : nsXBLInsertionPoint* point =
242 0 : static_cast<nsXBLInsertionPoint*>(mElements->ElementAt(i));
243 0 : PRInt32 idx = point->IndexOf(aContent);
244 0 : if (idx != -1) {
245 0 : return idx + lengthSoFar;
246 : }
247 :
248 0 : lengthSoFar += point->ChildCount();
249 : }
250 :
251 : // Didn't find it anywhere
252 0 : return -1;
253 : }
254 :
255 : //
256 : // Generic pldhash table stuff for mapping one nsISupports to another
257 : //
258 : // These values are never null - a null value implies that this
259 : // whole key should be removed (See SetOrRemoveObject)
260 : class ObjectEntry : public PLDHashEntryHdr
261 : {
262 : public:
263 :
264 : // note that these are allocated within the PLDHashTable, but we
265 : // want to keep track of them anyway
266 0 : ObjectEntry() { MOZ_COUNT_CTOR(ObjectEntry); }
267 0 : ~ObjectEntry() { MOZ_COUNT_DTOR(ObjectEntry); }
268 :
269 0 : nsISupports* GetValue() { return mValue; }
270 0 : nsISupports* GetKey() { return mKey; }
271 0 : void SetValue(nsISupports* aValue) { mValue = aValue; }
272 0 : void SetKey(nsISupports* aKey) { mKey = aKey; }
273 :
274 : private:
275 : nsCOMPtr<nsISupports> mKey;
276 : nsCOMPtr<nsISupports> mValue;
277 : };
278 :
279 : static void
280 0 : ClearObjectEntry(PLDHashTable* table, PLDHashEntryHdr *entry)
281 : {
282 0 : ObjectEntry* objEntry = static_cast<ObjectEntry*>(entry);
283 0 : objEntry->~ObjectEntry();
284 0 : }
285 :
286 : static bool
287 0 : InitObjectEntry(PLDHashTable* table, PLDHashEntryHdr* entry, const void* key)
288 : {
289 0 : new (entry) ObjectEntry;
290 0 : return true;
291 : }
292 :
293 :
294 :
295 : static PLDHashTableOps ObjectTableOps = {
296 : PL_DHashAllocTable,
297 : PL_DHashFreeTable,
298 : PL_DHashVoidPtrKeyStub,
299 : PL_DHashMatchEntryStub,
300 : PL_DHashMoveEntryStub,
301 : ClearObjectEntry,
302 : PL_DHashFinalizeStub,
303 : InitObjectEntry
304 : };
305 :
306 : // helper routine for adding a new entry
307 : static nsresult
308 0 : AddObjectEntry(PLDHashTable& table, nsISupports* aKey, nsISupports* aValue)
309 : {
310 0 : NS_ASSERTION(aKey, "key must be non-null");
311 0 : if (!aKey) return NS_ERROR_INVALID_ARG;
312 :
313 : ObjectEntry *entry =
314 : static_cast<ObjectEntry*>
315 0 : (PL_DHashTableOperate(&table, aKey, PL_DHASH_ADD));
316 :
317 0 : if (!entry)
318 0 : return NS_ERROR_OUT_OF_MEMORY;
319 :
320 : // only add the key if the entry is new
321 0 : if (!entry->GetKey())
322 0 : entry->SetKey(aKey);
323 :
324 : // now attach the new entry - note that entry->mValue could possibly
325 : // have a value already, this will release that.
326 0 : entry->SetValue(aValue);
327 :
328 0 : return NS_OK;
329 : }
330 :
331 : // helper routine for looking up an existing entry. Note that the
332 : // return result is NOT addreffed
333 : static nsISupports*
334 0 : LookupObject(PLDHashTable& table, nsIContent* aKey)
335 : {
336 0 : if (aKey && aKey->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
337 : ObjectEntry *entry =
338 : static_cast<ObjectEntry*>
339 0 : (PL_DHashTableOperate(&table, aKey, PL_DHASH_LOOKUP));
340 :
341 0 : if (PL_DHASH_ENTRY_IS_BUSY(entry))
342 0 : return entry->GetValue();
343 : }
344 :
345 0 : return nsnull;
346 : }
347 :
348 : inline void
349 0 : RemoveObjectEntry(PLDHashTable& table, nsISupports* aKey)
350 : {
351 0 : PL_DHashTableOperate(&table, aKey, PL_DHASH_REMOVE);
352 0 : }
353 :
354 : static nsresult
355 0 : SetOrRemoveObject(PLDHashTable& table, nsIContent* aKey, nsISupports* aValue)
356 : {
357 0 : if (aValue) {
358 : // lazily create the table, but only when adding elements
359 0 : if (!table.ops &&
360 : !PL_DHashTableInit(&table, &ObjectTableOps, nsnull,
361 0 : sizeof(ObjectEntry), 16)) {
362 0 : table.ops = nsnull;
363 0 : return NS_ERROR_OUT_OF_MEMORY;
364 : }
365 0 : aKey->SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
366 0 : return AddObjectEntry(table, aKey, aValue);
367 : }
368 :
369 : // no value, so remove the key from the table
370 0 : if (table.ops) {
371 : ObjectEntry* entry =
372 : static_cast<ObjectEntry*>
373 0 : (PL_DHashTableOperate(&table, aKey, PL_DHASH_LOOKUP));
374 0 : if (entry && PL_DHASH_ENTRY_IS_BUSY(entry)) {
375 : // Keep key and value alive while removing the entry.
376 0 : nsCOMPtr<nsISupports> key = entry->GetKey();
377 0 : nsCOMPtr<nsISupports> value = entry->GetValue();
378 0 : RemoveObjectEntry(table, aKey);
379 : }
380 : }
381 0 : return NS_OK;
382 : }
383 :
384 : // Implementation /////////////////////////////////////////////////////////////////
385 :
386 : // Static member variable initialization
387 :
388 : // Implement our nsISupports methods
389 :
390 1271 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsBindingManager)
391 1271 : tmp->mDestroyed = true;
392 :
393 1271 : if (tmp->mBindingTable.IsInitialized())
394 0 : tmp->mBindingTable.Clear();
395 :
396 1271 : if (tmp->mDocumentTable.IsInitialized())
397 0 : tmp->mDocumentTable.Clear();
398 :
399 1271 : if (tmp->mLoadingDocTable.IsInitialized())
400 0 : tmp->mLoadingDocTable.Clear();
401 :
402 1271 : if (tmp->mContentListTable.ops)
403 0 : PL_DHashTableFinish(&(tmp->mContentListTable));
404 1271 : tmp->mContentListTable.ops = nsnull;
405 :
406 1271 : if (tmp->mAnonymousNodesTable.ops)
407 0 : PL_DHashTableFinish(&(tmp->mAnonymousNodesTable));
408 1271 : tmp->mAnonymousNodesTable.ops = nsnull;
409 :
410 1271 : if (tmp->mInsertionParentTable.ops)
411 0 : PL_DHashTableFinish(&(tmp->mInsertionParentTable));
412 1271 : tmp->mInsertionParentTable.ops = nsnull;
413 :
414 1271 : if (tmp->mWrapperTable.ops)
415 0 : PL_DHashTableFinish(&(tmp->mWrapperTable));
416 1271 : tmp->mWrapperTable.ops = nsnull;
417 :
418 1271 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mAttachedStack)
419 :
420 1271 : if (tmp->mProcessAttachedQueueEvent) {
421 0 : tmp->mProcessAttachedQueueEvent->Revoke();
422 : }
423 1271 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
424 :
425 :
426 : static PLDHashOperator
427 0 : DocumentInfoHashtableTraverser(nsIURI* key,
428 : nsXBLDocumentInfo* di,
429 : void* userArg)
430 : {
431 : nsCycleCollectionTraversalCallback *cb =
432 0 : static_cast<nsCycleCollectionTraversalCallback*>(userArg);
433 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mDocumentTable value");
434 0 : cb->NoteXPCOMChild(static_cast<nsIScriptGlobalObjectOwner*>(di));
435 0 : return PL_DHASH_NEXT;
436 : }
437 :
438 : static PLDHashOperator
439 0 : LoadingDocHashtableTraverser(nsIURI* key,
440 : nsIStreamListener* sl,
441 : void* userArg)
442 : {
443 : nsCycleCollectionTraversalCallback *cb =
444 0 : static_cast<nsCycleCollectionTraversalCallback*>(userArg);
445 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mLoadingDocTable value");
446 0 : cb->NoteXPCOMChild(sl);
447 0 : return PL_DHASH_NEXT;
448 : }
449 :
450 1279 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsBindingManager)
451 : // The hashes keyed on nsIContent are traversed from the nsIContent itself.
452 1279 : if (tmp->mDocumentTable.IsInitialized())
453 0 : tmp->mDocumentTable.EnumerateRead(&DocumentInfoHashtableTraverser, &cb);
454 1279 : if (tmp->mLoadingDocTable.IsInitialized())
455 0 : tmp->mLoadingDocTable.EnumerateRead(&LoadingDocHashtableTraverser, &cb);
456 1279 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSTARRAY_MEMBER(mAttachedStack,
457 : nsXBLBinding)
458 : // No need to traverse mProcessAttachedQueueEvent, since it'll just
459 : // fire at some point or become revoke and drop its ref to us.
460 1279 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
461 :
462 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsBindingManager)
463 :
464 6379 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsBindingManager)
465 0 : NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
466 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
467 0 : NS_INTERFACE_MAP_END
468 :
469 2544 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsBindingManager)
470 3813 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsBindingManager)
471 :
472 : // Constructors/Destructors
473 1273 : nsBindingManager::nsBindingManager(nsIDocument* aDocument)
474 : : mProcessingAttachedStack(false),
475 : mDestroyed(false),
476 : mAttachedStackSizeOnOutermost(0),
477 1273 : mDocument(aDocument)
478 : {
479 1273 : mContentListTable.ops = nsnull;
480 1273 : mAnonymousNodesTable.ops = nsnull;
481 1273 : mInsertionParentTable.ops = nsnull;
482 1273 : mWrapperTable.ops = nsnull;
483 1273 : }
484 :
485 2542 : nsBindingManager::~nsBindingManager(void)
486 : {
487 1271 : mDestroyed = true;
488 :
489 1271 : if (mContentListTable.ops)
490 0 : PL_DHashTableFinish(&mContentListTable);
491 1271 : if (mAnonymousNodesTable.ops)
492 0 : PL_DHashTableFinish(&mAnonymousNodesTable);
493 1271 : NS_ASSERTION(!mInsertionParentTable.ops || !mInsertionParentTable.entryCount,
494 : "Insertion parent table isn't empty!");
495 1271 : if (mInsertionParentTable.ops)
496 0 : PL_DHashTableFinish(&mInsertionParentTable);
497 1271 : if (mWrapperTable.ops)
498 0 : PL_DHashTableFinish(&mWrapperTable);
499 1271 : }
500 :
501 : PLDHashOperator
502 0 : RemoveInsertionParentCB(PLDHashTable* aTable, PLDHashEntryHdr* aEntry,
503 : PRUint32 aNumber, void* aArg)
504 : {
505 0 : return (static_cast<ObjectEntry*>(aEntry)->GetValue() ==
506 0 : static_cast<nsISupports*>(aArg)) ? PL_DHASH_REMOVE : PL_DHASH_NEXT;
507 : }
508 :
509 : static void
510 0 : RemoveInsertionParentForNodeList(nsIDOMNodeList* aList, nsIContent* aParent)
511 : {
512 0 : nsAnonymousContentList* list = nsnull;
513 0 : if (aList) {
514 0 : CallQueryInterface(aList, &list);
515 : }
516 0 : if (list) {
517 0 : PRInt32 count = list->GetInsertionPointCount();
518 0 : for (PRInt32 i = 0; i < count; ++i) {
519 0 : nsRefPtr<nsXBLInsertionPoint> currPoint = list->GetInsertionPointAt(i);
520 0 : currPoint->UnbindDefaultContent();
521 : #ifdef DEBUG
522 0 : nsCOMPtr<nsIContent> parent = currPoint->GetInsertionParent();
523 0 : NS_ASSERTION(!parent || parent == aParent, "Wrong insertion parent!");
524 : #endif
525 0 : currPoint->ClearInsertionParent();
526 : }
527 0 : NS_RELEASE(list);
528 : }
529 0 : }
530 :
531 : void
532 0 : nsBindingManager::RemoveInsertionParent(nsIContent* aParent)
533 : {
534 0 : RemoveInsertionParentForNodeList(GetContentListFor(aParent), aParent);
535 :
536 0 : RemoveInsertionParentForNodeList(GetAnonymousNodesFor(aParent), aParent);
537 :
538 0 : if (mInsertionParentTable.ops) {
539 : PL_DHashTableEnumerate(&mInsertionParentTable, RemoveInsertionParentCB,
540 0 : static_cast<nsISupports*>(aParent));
541 : }
542 0 : }
543 :
544 : nsXBLBinding*
545 39024 : nsBindingManager::GetBinding(nsIContent* aContent)
546 : {
547 39024 : if (aContent && aContent->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) &&
548 0 : mBindingTable.IsInitialized()) {
549 0 : return mBindingTable.GetWeak(aContent);
550 : }
551 :
552 39024 : return nsnull;
553 : }
554 :
555 : nsresult
556 0 : nsBindingManager::SetBinding(nsIContent* aContent, nsXBLBinding* aBinding)
557 : {
558 0 : if (!mBindingTable.IsInitialized()) {
559 0 : if (!mBindingTable.Init())
560 0 : return NS_ERROR_OUT_OF_MEMORY;
561 : }
562 :
563 : // After this point, aBinding will be the most-derived binding for aContent.
564 : // If we already have a binding for aContent in our table, make sure to
565 : // remove it from the attached stack. Otherwise we might end up firing its
566 : // constructor twice (if aBinding inherits from it) or firing its constructor
567 : // after aContent has been deleted (if aBinding is null and the content node
568 : // dies before we process mAttachedStack).
569 0 : nsRefPtr<nsXBLBinding> oldBinding = GetBinding(aContent);
570 0 : if (oldBinding) {
571 0 : if (aContent->HasFlag(NODE_IS_INSERTION_PARENT)) {
572 : nsRefPtr<nsXBLBinding> parentBinding =
573 0 : GetBinding(aContent->GetBindingParent());
574 : // Clear insertion parent only if we don't have a parent binding which
575 : // marked content to be an insertion parent. See also ChangeDocumentFor().
576 0 : if (!parentBinding || !parentBinding->HasInsertionParent(aContent)) {
577 0 : RemoveInsertionParent(aContent);
578 0 : aContent->UnsetFlags(NODE_IS_INSERTION_PARENT);
579 : }
580 : }
581 : // Don't remove items here as that could mess up an executing
582 : // ProcessAttachedQueue
583 0 : PRUint32 index = mAttachedStack.IndexOf(oldBinding);
584 0 : if (index != mAttachedStack.NoIndex) {
585 0 : mAttachedStack[index] = nsnull;
586 : }
587 : }
588 :
589 0 : bool result = true;
590 :
591 0 : if (aBinding) {
592 0 : aContent->SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
593 0 : result = mBindingTable.Put(aContent, aBinding);
594 : } else {
595 0 : mBindingTable.Remove(aContent);
596 :
597 : // The death of the bindings means the death of the JS wrapper,
598 : // and the flushing of our explicit and anonymous insertion point
599 : // lists.
600 0 : SetWrappedJS(aContent, nsnull);
601 0 : SetContentListFor(aContent, nsnull);
602 0 : SetAnonymousNodesFor(aContent, nsnull);
603 0 : if (oldBinding) {
604 0 : oldBinding->SetBoundElement(nsnull);
605 : }
606 : }
607 :
608 0 : return result ? NS_OK : NS_ERROR_FAILURE;
609 : }
610 :
611 : nsIContent*
612 4605 : nsBindingManager::GetInsertionParent(nsIContent* aContent)
613 : {
614 4605 : if (mInsertionParentTable.ops) {
615 : return static_cast<nsIContent*>
616 0 : (LookupObject(mInsertionParentTable, aContent));
617 : }
618 :
619 4605 : return nsnull;
620 : }
621 :
622 : nsresult
623 0 : nsBindingManager::SetInsertionParent(nsIContent* aContent, nsIContent* aParent)
624 : {
625 0 : NS_ASSERTION(!aParent || aParent->HasFlag(NODE_IS_INSERTION_PARENT),
626 : "Insertion parent should have NODE_IS_INSERTION_PARENT flag!");
627 :
628 0 : if (mDestroyed) {
629 0 : return NS_OK;
630 : }
631 :
632 0 : return SetOrRemoveObject(mInsertionParentTable, aContent, aParent);
633 : }
634 :
635 : nsIXPConnectWrappedJS*
636 0 : nsBindingManager::GetWrappedJS(nsIContent* aContent)
637 : {
638 0 : if (mWrapperTable.ops) {
639 0 : return static_cast<nsIXPConnectWrappedJS*>(LookupObject(mWrapperTable, aContent));
640 : }
641 :
642 0 : return nsnull;
643 : }
644 :
645 : nsresult
646 0 : nsBindingManager::SetWrappedJS(nsIContent* aContent, nsIXPConnectWrappedJS* aWrappedJS)
647 : {
648 0 : if (mDestroyed) {
649 0 : return NS_OK;
650 : }
651 :
652 0 : return SetOrRemoveObject(mWrapperTable, aContent, aWrappedJS);
653 : }
654 :
655 : void
656 0 : nsBindingManager::RemovedFromDocumentInternal(nsIContent* aContent,
657 : nsIDocument* aOldDocument)
658 : {
659 0 : NS_PRECONDITION(aOldDocument != nsnull, "no old document");
660 :
661 0 : if (mDestroyed)
662 0 : return;
663 :
664 : // Hold a ref to the binding so it won't die when we remove it from our
665 : // table.
666 0 : nsRefPtr<nsXBLBinding> binding = GetBinding(aContent);
667 0 : if (aContent->HasFlag(NODE_IS_INSERTION_PARENT)) {
668 0 : nsRefPtr<nsXBLBinding> parentBinding = GetBinding(aContent->GetBindingParent());
669 0 : if (parentBinding) {
670 0 : parentBinding->RemoveInsertionParent(aContent);
671 : // Clear insertion parent only if we don't have a binding which
672 : // marked content to be an insertion parent. See also SetBinding().
673 0 : if (!binding || !binding->HasInsertionParent(aContent)) {
674 0 : RemoveInsertionParent(aContent);
675 0 : aContent->UnsetFlags(NODE_IS_INSERTION_PARENT);
676 : }
677 : }
678 : }
679 :
680 0 : if (binding) {
681 0 : binding->PrototypeBinding()->BindingDetached(binding->GetBoundElement());
682 0 : binding->ChangeDocument(aOldDocument, nsnull);
683 0 : SetBinding(aContent, nsnull);
684 : }
685 :
686 : // Clear out insertion parents and content lists.
687 0 : SetInsertionParent(aContent, nsnull);
688 0 : SetContentListFor(aContent, nsnull);
689 0 : SetAnonymousNodesFor(aContent, nsnull);
690 : }
691 :
692 : nsIAtom*
693 0 : nsBindingManager::ResolveTag(nsIContent* aContent, PRInt32* aNameSpaceID)
694 : {
695 0 : nsXBLBinding *binding = GetBinding(aContent);
696 :
697 0 : if (binding) {
698 0 : nsIAtom* base = binding->GetBaseTag(aNameSpaceID);
699 :
700 0 : if (base) {
701 0 : return base;
702 : }
703 : }
704 :
705 0 : *aNameSpaceID = aContent->GetNameSpaceID();
706 0 : return aContent->Tag();
707 : }
708 :
709 : nsresult
710 0 : nsBindingManager::GetContentListFor(nsIContent* aContent, nsIDOMNodeList** aResult)
711 : {
712 0 : NS_IF_ADDREF(*aResult = GetContentListFor(aContent));
713 0 : return NS_OK;
714 : }
715 :
716 : nsINodeList*
717 0 : nsBindingManager::GetContentListFor(nsIContent* aContent)
718 : {
719 0 : nsINodeList* result = nsnull;
720 :
721 0 : if (mContentListTable.ops) {
722 : result = static_cast<nsAnonymousContentList*>
723 0 : (LookupObject(mContentListTable, aContent));
724 : }
725 :
726 0 : if (!result) {
727 0 : result = aContent->GetChildNodesList();
728 : }
729 :
730 0 : return result;
731 : }
732 :
733 : nsresult
734 0 : nsBindingManager::SetContentListFor(nsIContent* aContent,
735 : nsInsertionPointList* aList)
736 : {
737 0 : if (mDestroyed) {
738 0 : return NS_OK;
739 : }
740 :
741 0 : nsAnonymousContentList* contentList = nsnull;
742 0 : if (aList) {
743 0 : contentList = new nsAnonymousContentList(aContent, aList);
744 0 : if (!contentList) {
745 0 : delete aList;
746 0 : return NS_ERROR_OUT_OF_MEMORY;
747 : }
748 : }
749 :
750 0 : return SetOrRemoveObject(mContentListTable, aContent, contentList);
751 : }
752 :
753 : bool
754 0 : nsBindingManager::HasContentListFor(nsIContent* aContent)
755 : {
756 0 : return mContentListTable.ops && LookupObject(mContentListTable, aContent);
757 : }
758 :
759 : nsINodeList*
760 0 : nsBindingManager::GetAnonymousNodesInternal(nsIContent* aContent,
761 : bool* aIsAnonymousContentList)
762 : {
763 0 : nsINodeList* result = nsnull;
764 0 : if (mAnonymousNodesTable.ops) {
765 : result = static_cast<nsAnonymousContentList*>
766 0 : (LookupObject(mAnonymousNodesTable, aContent));
767 : }
768 :
769 0 : if (!result) {
770 0 : *aIsAnonymousContentList = false;
771 0 : nsXBLBinding *binding = GetBinding(aContent);
772 0 : if (binding) {
773 0 : result = binding->GetAnonymousNodes();
774 : }
775 : } else
776 0 : *aIsAnonymousContentList = true;
777 :
778 0 : return result;
779 : }
780 :
781 : nsresult
782 0 : nsBindingManager::GetAnonymousNodesFor(nsIContent* aContent,
783 : nsIDOMNodeList** aResult)
784 : {
785 : bool dummy;
786 0 : NS_IF_ADDREF(*aResult = GetAnonymousNodesInternal(aContent, &dummy));
787 0 : return NS_OK;
788 : }
789 :
790 : nsINodeList*
791 0 : nsBindingManager::GetAnonymousNodesFor(nsIContent* aContent)
792 : {
793 : bool dummy;
794 0 : return GetAnonymousNodesInternal(aContent, &dummy);
795 : }
796 :
797 : nsresult
798 0 : nsBindingManager::SetAnonymousNodesFor(nsIContent* aContent,
799 : nsInsertionPointList* aList)
800 : {
801 0 : if (mDestroyed) {
802 0 : return NS_OK;
803 : }
804 :
805 0 : nsAnonymousContentList* contentList = nsnull;
806 0 : if (aList) {
807 0 : contentList = new nsAnonymousContentList(aContent, aList);
808 0 : if (!contentList) {
809 0 : delete aList;
810 0 : return NS_ERROR_OUT_OF_MEMORY;
811 : }
812 : }
813 :
814 0 : return SetOrRemoveObject(mAnonymousNodesTable, aContent, contentList);
815 : }
816 :
817 : nsINodeList*
818 0 : nsBindingManager::GetXBLChildNodesInternal(nsIContent* aContent,
819 : bool* aIsAnonymousContentList)
820 : {
821 : PRUint32 length;
822 :
823 : // Retrieve the anonymous content that we should build.
824 : nsINodeList* result = GetAnonymousNodesInternal(aContent,
825 0 : aIsAnonymousContentList);
826 0 : if (result) {
827 0 : result->GetLength(&length);
828 0 : if (length == 0)
829 0 : result = nsnull;
830 : }
831 :
832 : // We may have an altered list of children from XBL insertion points.
833 : // If we don't have any anonymous kids, we next check to see if we have
834 : // insertion points.
835 0 : if (!result) {
836 0 : if (mContentListTable.ops) {
837 : result = static_cast<nsAnonymousContentList*>
838 0 : (LookupObject(mContentListTable, aContent));
839 0 : *aIsAnonymousContentList = true;
840 : }
841 : }
842 :
843 0 : return result;
844 : }
845 :
846 : nsresult
847 0 : nsBindingManager::GetXBLChildNodesFor(nsIContent* aContent, nsIDOMNodeList** aResult)
848 : {
849 0 : NS_IF_ADDREF(*aResult = GetXBLChildNodesFor(aContent));
850 0 : return NS_OK;
851 : }
852 :
853 : nsINodeList*
854 0 : nsBindingManager::GetXBLChildNodesFor(nsIContent* aContent)
855 : {
856 : bool dummy;
857 0 : return GetXBLChildNodesInternal(aContent, &dummy);
858 : }
859 :
860 : nsIContent*
861 0 : nsBindingManager::GetInsertionPoint(nsIContent* aParent,
862 : const nsIContent* aChild,
863 : PRUint32* aIndex)
864 : {
865 0 : nsXBLBinding *binding = GetBinding(aParent);
866 0 : return binding ? binding->GetInsertionPoint(aChild, aIndex) : nsnull;
867 : }
868 :
869 : nsIContent*
870 0 : nsBindingManager::GetSingleInsertionPoint(nsIContent* aParent,
871 : PRUint32* aIndex,
872 : bool* aMultipleInsertionPoints)
873 : {
874 0 : nsXBLBinding *binding = GetBinding(aParent);
875 0 : if (binding)
876 0 : return binding->GetSingleInsertionPoint(aIndex, aMultipleInsertionPoints);
877 :
878 0 : *aMultipleInsertionPoints = false;
879 0 : return nsnull;
880 : }
881 :
882 : nsresult
883 0 : nsBindingManager::AddLayeredBinding(nsIContent* aContent, nsIURI* aURL,
884 : nsIPrincipal* aOriginPrincipal)
885 : {
886 : // First we need to load our binding.
887 : nsresult rv;
888 : nsCOMPtr<nsIXBLService> xblService =
889 0 : do_GetService("@mozilla.org/xbl;1", &rv);
890 0 : if (!xblService)
891 0 : return rv;
892 :
893 : // Load the bindings.
894 0 : nsRefPtr<nsXBLBinding> binding;
895 : bool dummy;
896 0 : xblService->LoadBindings(aContent, aURL, aOriginPrincipal, true,
897 0 : getter_AddRefs(binding), &dummy);
898 0 : if (binding) {
899 0 : AddToAttachedQueue(binding);
900 0 : ProcessAttachedQueue();
901 : }
902 :
903 0 : return NS_OK;
904 : }
905 :
906 : nsresult
907 0 : nsBindingManager::RemoveLayeredBinding(nsIContent* aContent, nsIURI* aURL)
908 : {
909 : // Hold a ref to the binding so it won't die when we remove it from our table
910 0 : nsRefPtr<nsXBLBinding> binding = GetBinding(aContent);
911 :
912 0 : if (!binding) {
913 0 : return NS_OK;
914 : }
915 :
916 : // For now we can only handle removing a binding if it's the only one
917 0 : NS_ENSURE_FALSE(binding->GetBaseBinding(), NS_ERROR_FAILURE);
918 :
919 : // Make sure that the binding has the URI that is requested to be removed
920 0 : if (!binding->PrototypeBinding()->CompareBindingURI(aURL)) {
921 0 : return NS_OK;
922 : }
923 :
924 : // Make sure it isn't a style binding
925 0 : if (binding->IsStyleBinding()) {
926 0 : return NS_OK;
927 : }
928 :
929 : // Hold strong ref in case removing the binding tries to close the
930 : // window or something.
931 : // XXXbz should that be ownerdoc? Wouldn't we need a ref to the
932 : // currentdoc too? What's the one that should be passed to
933 : // ChangeDocument?
934 0 : nsCOMPtr<nsIDocument> doc = aContent->OwnerDoc();
935 :
936 : // Finally remove the binding...
937 : // XXXbz this doesn't remove the implementation! Should fix! Until
938 : // then we need the explicit UnhookEventHandlers here.
939 0 : binding->UnhookEventHandlers();
940 0 : binding->ChangeDocument(doc, nsnull);
941 0 : SetBinding(aContent, nsnull);
942 0 : binding->MarkForDeath();
943 :
944 : // ...and recreate its frames. We need to do this since the frames may have
945 : // been removed and style may have changed due to the removal of the
946 : // anonymous children.
947 : // XXXbz this should be using the current doc (if any), not the owner doc.
948 0 : nsIPresShell *presShell = doc->GetShell();
949 0 : NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
950 :
951 0 : return presShell->RecreateFramesFor(aContent);;
952 : }
953 :
954 : nsresult
955 0 : nsBindingManager::LoadBindingDocument(nsIDocument* aBoundDoc,
956 : nsIURI* aURL,
957 : nsIPrincipal* aOriginPrincipal)
958 : {
959 0 : NS_PRECONDITION(aURL, "Must have a URI to load!");
960 :
961 : // First we need to load our binding.
962 : nsresult rv;
963 : nsCOMPtr<nsIXBLService> xblService =
964 0 : do_GetService("@mozilla.org/xbl;1", &rv);
965 0 : if (!xblService)
966 0 : return rv;
967 :
968 : // Load the binding doc.
969 0 : nsRefPtr<nsXBLDocumentInfo> info;
970 0 : xblService->LoadBindingDocumentInfo(nsnull, aBoundDoc, aURL,
971 : aOriginPrincipal, true,
972 0 : getter_AddRefs(info));
973 0 : if (!info)
974 0 : return NS_ERROR_FAILURE;
975 :
976 0 : return NS_OK;
977 : }
978 :
979 : nsresult
980 0 : nsBindingManager::AddToAttachedQueue(nsXBLBinding* aBinding)
981 : {
982 0 : if (!mAttachedStack.AppendElement(aBinding))
983 0 : return NS_ERROR_OUT_OF_MEMORY;
984 :
985 : // If we're in the middle of processing our queue already, don't
986 : // bother posting the event.
987 0 : if (!mProcessingAttachedStack && !mProcessAttachedQueueEvent) {
988 0 : PostProcessAttachedQueueEvent();
989 : }
990 :
991 : // Make sure that flushes will flush out the new items as needed.
992 0 : mDocument->SetNeedStyleFlush();
993 :
994 0 : return NS_OK;
995 :
996 : }
997 :
998 : void
999 0 : nsBindingManager::PostProcessAttachedQueueEvent()
1000 : {
1001 : mProcessAttachedQueueEvent =
1002 0 : NS_NewRunnableMethod(this, &nsBindingManager::DoProcessAttachedQueue);
1003 0 : nsresult rv = NS_DispatchToCurrentThread(mProcessAttachedQueueEvent);
1004 0 : if (NS_SUCCEEDED(rv) && mDocument) {
1005 0 : mDocument->BlockOnload();
1006 : }
1007 0 : }
1008 :
1009 : void
1010 0 : nsBindingManager::DoProcessAttachedQueue()
1011 : {
1012 0 : if (!mProcessingAttachedStack) {
1013 0 : ProcessAttachedQueue();
1014 :
1015 0 : NS_ASSERTION(mAttachedStack.Length() == 0,
1016 : "Shouldn't have pending bindings!");
1017 :
1018 0 : mProcessAttachedQueueEvent = nsnull;
1019 : } else {
1020 : // Someone's doing event processing from inside a constructor.
1021 : // They're evil, but we'll fight back! Just poll on them being
1022 : // done and repost the attached queue event.
1023 0 : PostProcessAttachedQueueEvent();
1024 : }
1025 :
1026 : // No matter what, unblock onload for the event that's fired.
1027 0 : if (mDocument) {
1028 : // Hold a strong reference while calling UnblockOnload since that might
1029 : // run script.
1030 0 : nsCOMPtr<nsIDocument> doc = mDocument;
1031 0 : doc->UnblockOnload(true);
1032 : }
1033 0 : }
1034 :
1035 : void
1036 4890 : nsBindingManager::ProcessAttachedQueue(PRUint32 aSkipSize)
1037 : {
1038 4890 : if (mProcessingAttachedStack || mAttachedStack.Length() <= aSkipSize)
1039 4890 : return;
1040 :
1041 : NS_TIME_FUNCTION;
1042 :
1043 0 : mProcessingAttachedStack = true;
1044 :
1045 : // Excute constructors. Do this from high index to low
1046 0 : while (mAttachedStack.Length() > aSkipSize) {
1047 0 : PRUint32 lastItem = mAttachedStack.Length() - 1;
1048 0 : nsRefPtr<nsXBLBinding> binding = mAttachedStack.ElementAt(lastItem);
1049 0 : mAttachedStack.RemoveElementAt(lastItem);
1050 0 : if (binding) {
1051 0 : binding->ExecuteAttachedHandler();
1052 : }
1053 : }
1054 :
1055 : // If NodeWillBeDestroyed has run we don't want to clobber
1056 : // mProcessingAttachedStack set there.
1057 0 : if (mDocument) {
1058 0 : mProcessingAttachedStack = false;
1059 : }
1060 :
1061 0 : NS_ASSERTION(mAttachedStack.Length() == aSkipSize, "How did we get here?");
1062 :
1063 0 : mAttachedStack.Compact();
1064 : }
1065 :
1066 : // Keep bindings and bound elements alive while executing detached handlers.
1067 : struct BindingTableReadClosure
1068 0 : {
1069 : nsCOMArray<nsIContent> mBoundElements;
1070 : nsBindingList mBindings;
1071 : };
1072 :
1073 : static PLDHashOperator
1074 0 : AccumulateBindingsToDetach(nsISupports *aKey, nsXBLBinding *aBinding,
1075 : void* aClosure)
1076 : {
1077 : BindingTableReadClosure* closure =
1078 0 : static_cast<BindingTableReadClosure*>(aClosure);
1079 0 : if (aBinding && closure->mBindings.AppendElement(aBinding)) {
1080 0 : if (!closure->mBoundElements.AppendObject(aBinding->GetBoundElement())) {
1081 0 : closure->mBindings.RemoveElementAt(closure->mBindings.Length() - 1);
1082 : }
1083 : }
1084 0 : return PL_DHASH_NEXT;
1085 : }
1086 :
1087 : void
1088 0 : nsBindingManager::ExecuteDetachedHandlers()
1089 : {
1090 : // Walk our hashtable of bindings.
1091 0 : if (mBindingTable.IsInitialized()) {
1092 0 : BindingTableReadClosure closure;
1093 0 : mBindingTable.EnumerateRead(AccumulateBindingsToDetach, &closure);
1094 0 : PRUint32 i, count = closure.mBindings.Length();
1095 0 : for (i = 0; i < count; ++i) {
1096 0 : closure.mBindings[i]->ExecuteDetachedHandler();
1097 : }
1098 : }
1099 0 : }
1100 :
1101 : nsresult
1102 0 : nsBindingManager::PutXBLDocumentInfo(nsXBLDocumentInfo* aDocumentInfo)
1103 : {
1104 0 : NS_PRECONDITION(aDocumentInfo, "Must have a non-null documentinfo!");
1105 :
1106 0 : NS_ENSURE_TRUE(mDocumentTable.IsInitialized() || mDocumentTable.Init(16),
1107 : NS_ERROR_OUT_OF_MEMORY);
1108 :
1109 0 : NS_ENSURE_TRUE(mDocumentTable.Put(aDocumentInfo->DocumentURI(),
1110 : aDocumentInfo),
1111 : NS_ERROR_OUT_OF_MEMORY);
1112 :
1113 0 : return NS_OK;
1114 : }
1115 :
1116 : void
1117 0 : nsBindingManager::RemoveXBLDocumentInfo(nsXBLDocumentInfo* aDocumentInfo)
1118 : {
1119 0 : if (mDocumentTable.IsInitialized()) {
1120 0 : mDocumentTable.Remove(aDocumentInfo->DocumentURI());
1121 : }
1122 0 : }
1123 :
1124 : nsXBLDocumentInfo*
1125 0 : nsBindingManager::GetXBLDocumentInfo(nsIURI* aURL)
1126 : {
1127 0 : if (!mDocumentTable.IsInitialized())
1128 0 : return nsnull;
1129 :
1130 0 : return mDocumentTable.GetWeak(aURL);
1131 : }
1132 :
1133 : nsresult
1134 0 : nsBindingManager::PutLoadingDocListener(nsIURI* aURL, nsIStreamListener* aListener)
1135 : {
1136 0 : NS_PRECONDITION(aListener, "Must have a non-null listener!");
1137 :
1138 0 : NS_ENSURE_TRUE(mLoadingDocTable.IsInitialized() || mLoadingDocTable.Init(16),
1139 : NS_ERROR_OUT_OF_MEMORY);
1140 :
1141 0 : NS_ENSURE_TRUE(mLoadingDocTable.Put(aURL, aListener),
1142 : NS_ERROR_OUT_OF_MEMORY);
1143 :
1144 0 : return NS_OK;
1145 : }
1146 :
1147 : nsIStreamListener*
1148 0 : nsBindingManager::GetLoadingDocListener(nsIURI* aURL)
1149 : {
1150 0 : if (!mLoadingDocTable.IsInitialized())
1151 0 : return nsnull;
1152 :
1153 0 : return mLoadingDocTable.GetWeak(aURL);
1154 : }
1155 :
1156 : void
1157 0 : nsBindingManager::RemoveLoadingDocListener(nsIURI* aURL)
1158 : {
1159 0 : if (mLoadingDocTable.IsInitialized()) {
1160 0 : mLoadingDocTable.Remove(aURL);
1161 : }
1162 0 : }
1163 :
1164 : static PLDHashOperator
1165 0 : MarkForDeath(nsISupports *aKey, nsXBLBinding *aBinding, void* aClosure)
1166 : {
1167 0 : if (aBinding->MarkedForDeath())
1168 0 : return PL_DHASH_NEXT; // Already marked for death.
1169 :
1170 0 : nsCAutoString path;
1171 0 : aBinding->PrototypeBinding()->DocURI()->GetPath(path);
1172 :
1173 0 : if (!strncmp(path.get(), "/skin", 5))
1174 0 : aBinding->MarkForDeath();
1175 :
1176 0 : return PL_DHASH_NEXT;
1177 : }
1178 :
1179 : void
1180 0 : nsBindingManager::FlushSkinBindings()
1181 : {
1182 0 : if (mBindingTable.IsInitialized())
1183 0 : mBindingTable.EnumerateRead(MarkForDeath, nsnull);
1184 0 : }
1185 :
1186 : // Used below to protect from recurring in QI calls through XPConnect.
1187 : struct AntiRecursionData {
1188 : nsIContent* element;
1189 : REFNSIID iid;
1190 : AntiRecursionData* next;
1191 :
1192 0 : AntiRecursionData(nsIContent* aElement,
1193 : REFNSIID aIID,
1194 : AntiRecursionData* aNext)
1195 0 : : element(aElement), iid(aIID), next(aNext) {}
1196 : };
1197 :
1198 : nsresult
1199 993 : nsBindingManager::GetBindingImplementation(nsIContent* aContent, REFNSIID aIID,
1200 : void** aResult)
1201 : {
1202 993 : *aResult = nsnull;
1203 993 : nsXBLBinding *binding = GetBinding(aContent);
1204 993 : if (binding) {
1205 : // The binding should not be asked for nsISupports
1206 0 : NS_ASSERTION(!aIID.Equals(NS_GET_IID(nsISupports)), "Asking a binding for nsISupports");
1207 0 : if (binding->ImplementsInterface(aIID)) {
1208 0 : nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS = GetWrappedJS(aContent);
1209 :
1210 0 : if (wrappedJS) {
1211 : // Protect from recurring in QI calls through XPConnect.
1212 : // This can happen when a second binding is being resolved.
1213 : // At that point a wrappedJS exists, but it doesn't yet know about
1214 : // the iid we are asking for. So, without this protection,
1215 : // AggregatedQueryInterface would end up recurring back into itself
1216 : // through this code.
1217 : //
1218 : // With this protection, when we detect the recursion we return
1219 : // NS_NOINTERFACE in the inner call. The outer call will then fall
1220 : // through (see below) and build a new chained wrappedJS for the iid.
1221 : //
1222 : // We're careful to not assume that only one direct nesting can occur
1223 : // because there is a call into JS in the middle and we can't assume
1224 : // that this code won't be reached by some more complex nesting path.
1225 : //
1226 : // NOTE: We *assume* this is single threaded, so we can use a
1227 : // static linked list to do the check.
1228 :
1229 : static AntiRecursionData* list = nsnull;
1230 :
1231 0 : for (AntiRecursionData* p = list; p; p = p->next) {
1232 0 : if (p->element == aContent && p->iid.Equals(aIID)) {
1233 0 : *aResult = nsnull;
1234 0 : return NS_NOINTERFACE;
1235 : }
1236 : }
1237 :
1238 0 : AntiRecursionData item(aContent, aIID, list);
1239 0 : list = &item;
1240 :
1241 0 : nsresult rv = wrappedJS->AggregatedQueryInterface(aIID, aResult);
1242 :
1243 0 : list = item.next;
1244 :
1245 0 : if (*aResult)
1246 0 : return rv;
1247 :
1248 : // No result was found, so this must be another XBL interface.
1249 : // Fall through to create a new wrapper.
1250 : }
1251 :
1252 : // We have never made a wrapper for this implementation.
1253 : // Create an XPC wrapper for the script object and hand it back.
1254 :
1255 0 : nsIDocument* doc = aContent->OwnerDoc();
1256 :
1257 0 : nsIScriptGlobalObject *global = doc->GetScriptGlobalObject();
1258 0 : if (!global)
1259 0 : return NS_NOINTERFACE;
1260 :
1261 0 : nsIScriptContext *context = global->GetContext();
1262 0 : if (!context)
1263 0 : return NS_NOINTERFACE;
1264 :
1265 0 : JSContext* jscontext = context->GetNativeContext();
1266 0 : if (!jscontext)
1267 0 : return NS_NOINTERFACE;
1268 :
1269 0 : nsIXPConnect *xpConnect = nsContentUtils::XPConnect();
1270 :
1271 0 : nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
1272 : xpConnect->GetWrappedNativeOfNativeObject(jscontext,
1273 0 : global->GetGlobalJSObject(),
1274 : aContent,
1275 : NS_GET_IID(nsISupports),
1276 0 : getter_AddRefs(wrapper));
1277 0 : NS_ENSURE_TRUE(wrapper, NS_NOINTERFACE);
1278 :
1279 0 : JSObject* jsobj = nsnull;
1280 :
1281 0 : wrapper->GetJSObject(&jsobj);
1282 0 : NS_ENSURE_TRUE(jsobj, NS_NOINTERFACE);
1283 :
1284 : nsresult rv = xpConnect->WrapJSAggregatedToNative(aContent, jscontext,
1285 0 : jsobj, aIID, aResult);
1286 0 : if (NS_FAILED(rv))
1287 0 : return rv;
1288 :
1289 : // We successfully created a wrapper. We will own this wrapper for as long as the binding remains
1290 : // alive. At the time the binding is cleared out of the bindingManager, we will remove the wrapper
1291 : // from the bindingManager as well.
1292 0 : nsISupports* supp = static_cast<nsISupports*>(*aResult);
1293 0 : wrappedJS = do_QueryInterface(supp);
1294 0 : SetWrappedJS(aContent, wrappedJS);
1295 :
1296 0 : return rv;
1297 : }
1298 : }
1299 :
1300 993 : *aResult = nsnull;
1301 993 : return NS_NOINTERFACE;
1302 : }
1303 :
1304 : nsresult
1305 0 : nsBindingManager::WalkRules(nsIStyleRuleProcessor::EnumFunc aFunc,
1306 : RuleProcessorData* aData,
1307 : bool* aCutOffInheritance)
1308 : {
1309 0 : *aCutOffInheritance = false;
1310 :
1311 0 : NS_ASSERTION(aData->mElement, "How did that happen?");
1312 :
1313 : // Walk the binding scope chain, starting with the binding attached to our
1314 : // content, up till we run out of scopes or we get cut off.
1315 0 : nsIContent *content = aData->mElement;
1316 :
1317 0 : do {
1318 0 : nsXBLBinding *binding = GetBinding(content);
1319 0 : if (binding) {
1320 0 : aData->mTreeMatchContext.mScopedRoot = content;
1321 0 : binding->WalkRules(aFunc, aData);
1322 : // If we're not looking at our original content, allow the binding to cut
1323 : // off style inheritance
1324 0 : if (content != aData->mElement) {
1325 0 : if (!binding->InheritsStyle()) {
1326 : // Go no further; we're not inheriting style from anything above here
1327 0 : break;
1328 : }
1329 : }
1330 : }
1331 :
1332 0 : if (content->IsRootOfNativeAnonymousSubtree()) {
1333 0 : break; // Deliberately cut off style inheritance here.
1334 : }
1335 :
1336 0 : content = content->GetBindingParent();
1337 : } while (content);
1338 :
1339 : // If "content" is non-null that means we cut off inheritance at some point
1340 : // in the loop.
1341 0 : *aCutOffInheritance = (content != nsnull);
1342 :
1343 : // Null out the scoped root that we set repeatedly
1344 0 : aData->mTreeMatchContext.mScopedRoot = nsnull;
1345 :
1346 0 : return NS_OK;
1347 : }
1348 :
1349 : typedef nsTHashtable<nsVoidPtrHashKey> RuleProcessorSet;
1350 :
1351 : static PLDHashOperator
1352 0 : EnumRuleProcessors(nsISupports *aKey, nsXBLBinding *aBinding, void* aClosure)
1353 : {
1354 0 : RuleProcessorSet *set = static_cast<RuleProcessorSet*>(aClosure);
1355 0 : for (nsXBLBinding *binding = aBinding; binding;
1356 : binding = binding->GetBaseBinding()) {
1357 : nsIStyleRuleProcessor *ruleProc =
1358 0 : binding->PrototypeBinding()->GetRuleProcessor();
1359 0 : if (ruleProc) {
1360 0 : if (!set->IsInitialized() && !set->Init(16))
1361 0 : return PL_DHASH_STOP;
1362 0 : set->PutEntry(ruleProc);
1363 : }
1364 : }
1365 0 : return PL_DHASH_NEXT;
1366 : }
1367 :
1368 : struct WalkAllRulesData {
1369 : nsIStyleRuleProcessor::EnumFunc mFunc;
1370 : RuleProcessorData* mData;
1371 : };
1372 :
1373 : static PLDHashOperator
1374 0 : EnumWalkAllRules(nsVoidPtrHashKey *aKey, void* aClosure)
1375 : {
1376 : nsIStyleRuleProcessor *ruleProcessor =
1377 0 : static_cast<nsIStyleRuleProcessor*>(const_cast<void*>(aKey->GetKey()));
1378 0 : WalkAllRulesData *data = static_cast<WalkAllRulesData*>(aClosure);
1379 :
1380 0 : (*(data->mFunc))(ruleProcessor, data->mData);
1381 :
1382 0 : return PL_DHASH_NEXT;
1383 : }
1384 :
1385 : void
1386 0 : nsBindingManager::WalkAllRules(nsIStyleRuleProcessor::EnumFunc aFunc,
1387 : RuleProcessorData* aData)
1388 : {
1389 0 : if (!mBindingTable.IsInitialized())
1390 0 : return;
1391 :
1392 0 : RuleProcessorSet set;
1393 0 : mBindingTable.EnumerateRead(EnumRuleProcessors, &set);
1394 0 : if (!set.IsInitialized())
1395 : return;
1396 :
1397 0 : WalkAllRulesData data = { aFunc, aData };
1398 0 : set.EnumerateEntries(EnumWalkAllRules, &data);
1399 : }
1400 :
1401 : struct MediumFeaturesChangedData {
1402 : nsPresContext *mPresContext;
1403 : bool *mRulesChanged;
1404 : };
1405 :
1406 : static PLDHashOperator
1407 0 : EnumMediumFeaturesChanged(nsVoidPtrHashKey *aKey, void* aClosure)
1408 : {
1409 : nsIStyleRuleProcessor *ruleProcessor =
1410 0 : static_cast<nsIStyleRuleProcessor*>(const_cast<void*>(aKey->GetKey()));
1411 : MediumFeaturesChangedData *data =
1412 0 : static_cast<MediumFeaturesChangedData*>(aClosure);
1413 :
1414 0 : bool thisChanged = ruleProcessor->MediumFeaturesChanged(data->mPresContext);
1415 0 : *data->mRulesChanged = *data->mRulesChanged || thisChanged;
1416 :
1417 0 : return PL_DHASH_NEXT;
1418 : }
1419 :
1420 : nsresult
1421 0 : nsBindingManager::MediumFeaturesChanged(nsPresContext* aPresContext,
1422 : bool* aRulesChanged)
1423 : {
1424 0 : *aRulesChanged = false;
1425 0 : if (!mBindingTable.IsInitialized())
1426 0 : return NS_OK;
1427 :
1428 0 : RuleProcessorSet set;
1429 0 : mBindingTable.EnumerateRead(EnumRuleProcessors, &set);
1430 0 : if (!set.IsInitialized())
1431 0 : return NS_OK;
1432 :
1433 0 : MediumFeaturesChangedData data = { aPresContext, aRulesChanged };
1434 0 : set.EnumerateEntries(EnumMediumFeaturesChanged, &data);
1435 0 : return NS_OK;
1436 : }
1437 :
1438 : static PLDHashOperator
1439 0 : EnumAppendAllSheets(nsISupports *aKey, nsXBLBinding *aBinding, void* aClosure)
1440 : {
1441 : nsTArray<nsCSSStyleSheet*>* array =
1442 0 : static_cast<nsTArray<nsCSSStyleSheet*>*>(aClosure);
1443 0 : for (nsXBLBinding *binding = aBinding; binding;
1444 : binding = binding->GetBaseBinding()) {
1445 : nsXBLPrototypeResources::sheet_array_type* sheets =
1446 0 : binding->PrototypeBinding()->GetStyleSheets();
1447 0 : if (sheets) {
1448 : // Copy from nsTArray<nsRefPtr<nsCSSStyleSheet> > to
1449 : // nsTArray<nsCSSStyleSheet*>.
1450 0 : array->AppendElements(*sheets);
1451 : }
1452 : }
1453 0 : return PL_DHASH_NEXT;
1454 : }
1455 :
1456 : void
1457 0 : nsBindingManager::AppendAllSheets(nsTArray<nsCSSStyleSheet*>& aArray)
1458 : {
1459 0 : if (!mBindingTable.IsInitialized())
1460 0 : return;
1461 :
1462 0 : mBindingTable.EnumerateRead(EnumAppendAllSheets, &aArray);
1463 : }
1464 :
1465 : nsIContent*
1466 0 : nsBindingManager::GetNestedInsertionPoint(nsIContent* aParent,
1467 : const nsIContent* aChild)
1468 : {
1469 : // Check to see if the content is anonymous.
1470 0 : if (aChild->GetBindingParent() == aParent)
1471 0 : return nsnull; // It is anonymous. Don't use the insertion point, since that's only
1472 : // for the explicit kids.
1473 :
1474 : PRUint32 index;
1475 0 : nsIContent *insertionElement = GetInsertionPoint(aParent, aChild, &index);
1476 0 : if (insertionElement && insertionElement != aParent) {
1477 : // See if we nest even further in.
1478 0 : nsIContent* nestedPoint = GetNestedInsertionPoint(insertionElement, aChild);
1479 0 : if (nestedPoint)
1480 0 : insertionElement = nestedPoint;
1481 : }
1482 :
1483 0 : return insertionElement;
1484 : }
1485 :
1486 : nsIContent*
1487 0 : nsBindingManager::GetNestedSingleInsertionPoint(nsIContent* aParent,
1488 : bool* aMultipleInsertionPoints)
1489 : {
1490 0 : *aMultipleInsertionPoints = false;
1491 :
1492 : PRUint32 index;
1493 : nsIContent *insertionElement =
1494 0 : GetSingleInsertionPoint(aParent, &index, aMultipleInsertionPoints);
1495 0 : if (*aMultipleInsertionPoints) {
1496 0 : return nsnull;
1497 : }
1498 0 : if (insertionElement && insertionElement != aParent) {
1499 : // See if we nest even further in.
1500 : nsIContent* nestedPoint =
1501 : GetNestedSingleInsertionPoint(insertionElement,
1502 0 : aMultipleInsertionPoints);
1503 0 : if (nestedPoint)
1504 0 : insertionElement = nestedPoint;
1505 : }
1506 :
1507 0 : return insertionElement;
1508 : }
1509 :
1510 : nsXBLInsertionPoint*
1511 0 : nsBindingManager::FindInsertionPointAndIndex(nsIContent* aContainer,
1512 : nsIContent* aInsertionParent,
1513 : PRUint32 aIndexInContainer,
1514 : PRInt32 aAppend,
1515 : PRInt32* aInsertionIndex)
1516 : {
1517 : bool isAnonymousContentList;
1518 : nsINodeList* nodeList =
1519 0 : GetXBLChildNodesInternal(aInsertionParent, &isAnonymousContentList);
1520 0 : if (!nodeList || !isAnonymousContentList) {
1521 0 : return nsnull;
1522 : }
1523 :
1524 : // Find a non-pseudo-insertion point and just jam ourselves in. This is
1525 : // not 100% correct, since there might be multiple insertion points under
1526 : // this insertion parent, and we should really be using the one that
1527 : // matches our content... Hack city, baby.
1528 : nsAnonymousContentList* contentList =
1529 0 : static_cast<nsAnonymousContentList*>(nodeList);
1530 :
1531 0 : PRInt32 count = contentList->GetInsertionPointCount();
1532 0 : for (PRInt32 i = 0; i < count; i++) {
1533 0 : nsXBLInsertionPoint* point = contentList->GetInsertionPointAt(i);
1534 0 : if (point->GetInsertionIndex() != -1) {
1535 : // We're real. Jam the kid in.
1536 :
1537 : // Find the right insertion spot. Can't just insert in the insertion
1538 : // point at aIndexInContainer since the point may contain anonymous
1539 : // content, not all of aContainer's kids, etc. So find the last
1540 : // child of aContainer that comes before aIndexInContainer and is in
1541 : // the insertion point and insert right after it.
1542 0 : PRInt32 pointSize = point->ChildCount();
1543 0 : for (PRInt32 parentIndex = aIndexInContainer - 1; parentIndex >= 0;
1544 : --parentIndex) {
1545 0 : nsIContent* currentSibling = aContainer->GetChildAt(parentIndex);
1546 0 : for (PRInt32 pointIndex = pointSize - 1; pointIndex >= 0;
1547 : --pointIndex) {
1548 0 : if (point->ChildAt(pointIndex) == currentSibling) {
1549 0 : *aInsertionIndex = pointIndex + 1;
1550 0 : return point;
1551 : }
1552 : }
1553 : }
1554 :
1555 : // None of our previous siblings are in here... just stick
1556 : // ourselves in at the end of the insertion point if we're
1557 : // appending, and at the beginning otherwise.
1558 : // XXXbz if we ever start doing the filter thing right, this may be no
1559 : // good, since we may _still_ have anonymous kids in there and may need
1560 : // to get the ordering with those right. In fact, this is even wrong
1561 : // without the filter thing for nested insertion points, since they might
1562 : // contain anonymous content that needs to come after all explicit
1563 : // kids... but we have no way to know that here easily.
1564 0 : if (aAppend) {
1565 0 : *aInsertionIndex = pointSize;
1566 : } else {
1567 0 : *aInsertionIndex = 0;
1568 : }
1569 0 : return point;
1570 : }
1571 : }
1572 :
1573 0 : return nsnull;
1574 : }
1575 :
1576 : void
1577 2366 : nsBindingManager::ContentAppended(nsIDocument* aDocument,
1578 : nsIContent* aContainer,
1579 : nsIContent* aFirstNewContent,
1580 : PRInt32 aNewIndexInContainer)
1581 : {
1582 2366 : if (aNewIndexInContainer != -1 &&
1583 : (mContentListTable.ops || mAnonymousNodesTable.ops)) {
1584 : // It's not anonymous.
1585 0 : NS_ASSERTION(aNewIndexInContainer >= 0, "Bogus index");
1586 :
1587 : bool multiple;
1588 0 : nsIContent* ins = GetNestedSingleInsertionPoint(aContainer, &multiple);
1589 :
1590 0 : if (multiple) {
1591 : // Do each kid individually
1592 0 : PRInt32 childCount = aContainer->GetChildCount();
1593 0 : for (PRInt32 idx = aNewIndexInContainer; idx < childCount; ++idx) {
1594 0 : HandleChildInsertion(aContainer, aContainer->GetChildAt(idx),
1595 0 : idx, true);
1596 : }
1597 : }
1598 0 : else if (ins) {
1599 : PRInt32 insertionIndex;
1600 : nsXBLInsertionPoint* point =
1601 : FindInsertionPointAndIndex(aContainer, ins, aNewIndexInContainer,
1602 0 : true, &insertionIndex);
1603 0 : if (point) {
1604 0 : PRInt32 childCount = aContainer->GetChildCount();
1605 0 : for (PRInt32 j = aNewIndexInContainer; j < childCount;
1606 : j++, insertionIndex++) {
1607 0 : nsIContent* child = aContainer->GetChildAt(j);
1608 0 : point->InsertChildAt(insertionIndex, child);
1609 0 : SetInsertionParent(child, ins);
1610 : }
1611 : }
1612 : }
1613 : }
1614 2366 : }
1615 :
1616 : void
1617 1295 : nsBindingManager::ContentInserted(nsIDocument* aDocument,
1618 : nsIContent* aContainer,
1619 : nsIContent* aChild,
1620 : PRInt32 aIndexInContainer)
1621 : {
1622 1295 : if (aIndexInContainer != -1 &&
1623 : (mContentListTable.ops || mAnonymousNodesTable.ops)) {
1624 : // It's not anonymous.
1625 0 : NS_ASSERTION(aIndexInContainer >= 0, "Bogus index");
1626 0 : HandleChildInsertion(aContainer, aChild, aIndexInContainer, false);
1627 : }
1628 1295 : }
1629 :
1630 : static void
1631 0 : RemoveChildFromInsertionPoint(nsAnonymousContentList* aInsertionPointList,
1632 : nsIContent* aChild,
1633 : bool aRemoveFromPseudoPoints)
1634 : {
1635 : // We need to find the insertion point that contains aChild and remove it
1636 : // from that insertion point. Sadly, we don't know which point it is, or
1637 : // when we've hit it, but just trying to remove from all the pseudo or
1638 : // non-pseudo insertion points, depending on the value of
1639 : // aRemoveFromPseudoPoints, should work.
1640 :
1641 : // XXXbz nsXBLInsertionPoint::RemoveChild could return whether it
1642 : // removed something. Wouldn't that let us short-circuit the walk?
1643 : // Or can a child be in multiple insertion points? I wouldn't think
1644 : // so...
1645 0 : PRInt32 count = aInsertionPointList->GetInsertionPointCount();
1646 0 : for (PRInt32 i = 0; i < count; i++) {
1647 : nsXBLInsertionPoint* point =
1648 0 : aInsertionPointList->GetInsertionPointAt(i);
1649 0 : if ((point->GetInsertionIndex() == -1) == aRemoveFromPseudoPoints) {
1650 0 : point->RemoveChild(aChild);
1651 : }
1652 : }
1653 0 : }
1654 :
1655 : void
1656 239 : nsBindingManager::ContentRemoved(nsIDocument* aDocument,
1657 : nsIContent* aContainer,
1658 : nsIContent* aChild,
1659 : PRInt32 aIndexInContainer,
1660 : nsIContent* aPreviousSibling)
1661 : {
1662 239 : if (aContainer && aIndexInContainer != -1 &&
1663 : (mContentListTable.ops || mAnonymousNodesTable.ops)) {
1664 : // It's not anonymous
1665 0 : nsCOMPtr<nsIContent> point = GetNestedInsertionPoint(aContainer, aChild);
1666 :
1667 0 : if (point) {
1668 : bool isAnonymousContentList;
1669 : nsCOMPtr<nsIDOMNodeList> nodeList =
1670 0 : GetXBLChildNodesInternal(point, &isAnonymousContentList);
1671 :
1672 0 : if (nodeList && isAnonymousContentList) {
1673 : // Find a non-pseudo-insertion point and remove ourselves.
1674 : RemoveChildFromInsertionPoint(static_cast<nsAnonymousContentList*>
1675 : (static_cast<nsIDOMNodeList*>
1676 0 : (nodeList)),
1677 : aChild,
1678 0 : false);
1679 0 : SetInsertionParent(aChild, nsnull);
1680 : }
1681 :
1682 : // Also remove from the list in mContentListTable, if any.
1683 0 : if (mContentListTable.ops) {
1684 : nsCOMPtr<nsIDOMNodeList> otherNodeList =
1685 : static_cast<nsAnonymousContentList*>
1686 0 : (LookupObject(mContentListTable, point));
1687 0 : if (otherNodeList && otherNodeList != nodeList) {
1688 : // otherNodeList is always anonymous
1689 : RemoveChildFromInsertionPoint(static_cast<nsAnonymousContentList*>
1690 : (static_cast<nsIDOMNodeList*>
1691 0 : (otherNodeList)),
1692 : aChild,
1693 0 : false);
1694 : }
1695 : }
1696 : }
1697 :
1698 : // Whether the child has a nested insertion point or not, aContainer might
1699 : // have insertion points under it. If that's the case, we need to remove
1700 : // aChild from the pseudo insertion point it's in.
1701 0 : if (mContentListTable.ops) {
1702 : nsAnonymousContentList* insertionPointList =
1703 : static_cast<nsAnonymousContentList*>(LookupObject(mContentListTable,
1704 0 : aContainer));
1705 0 : if (insertionPointList) {
1706 0 : RemoveChildFromInsertionPoint(insertionPointList, aChild, true);
1707 : }
1708 : }
1709 : }
1710 239 : }
1711 :
1712 : void
1713 1271 : nsBindingManager::DropDocumentReference()
1714 : {
1715 1271 : mDestroyed = true;
1716 :
1717 : // Make sure to not run any more XBL constructors
1718 1271 : mProcessingAttachedStack = true;
1719 1271 : if (mProcessAttachedQueueEvent) {
1720 0 : mProcessAttachedQueueEvent->Revoke();
1721 : }
1722 :
1723 1271 : if (mContentListTable.ops)
1724 0 : PL_DHashTableFinish(&(mContentListTable));
1725 1271 : mContentListTable.ops = nsnull;
1726 :
1727 1271 : if (mAnonymousNodesTable.ops)
1728 0 : PL_DHashTableFinish(&(mAnonymousNodesTable));
1729 1271 : mAnonymousNodesTable.ops = nsnull;
1730 :
1731 1271 : if (mInsertionParentTable.ops)
1732 0 : PL_DHashTableFinish(&(mInsertionParentTable));
1733 1271 : mInsertionParentTable.ops = nsnull;
1734 :
1735 1271 : if (mBindingTable.IsInitialized())
1736 0 : mBindingTable.Clear();
1737 :
1738 1271 : mDocument = nsnull;
1739 1271 : }
1740 :
1741 : void
1742 136368 : nsBindingManager::Traverse(nsIContent *aContent,
1743 : nsCycleCollectionTraversalCallback &cb)
1744 : {
1745 136368 : if (!aContent->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
1746 136368 : return;
1747 : }
1748 :
1749 : nsISupports *value;
1750 0 : if (mInsertionParentTable.ops &&
1751 0 : (value = LookupObject(mInsertionParentTable, aContent))) {
1752 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mInsertionParentTable key");
1753 0 : cb.NoteXPCOMChild(aContent);
1754 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mInsertionParentTable value");
1755 0 : cb.NoteXPCOMChild(value);
1756 : }
1757 :
1758 : // XXXbz how exactly would NODE_MAY_BE_IN_BINDING_MNGR end up on non-elements?
1759 0 : if (!aContent->IsElement()) {
1760 0 : return;
1761 : }
1762 :
1763 0 : nsXBLBinding *binding = GetBinding(aContent);
1764 0 : if (binding) {
1765 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mBindingTable key");
1766 0 : cb.NoteXPCOMChild(aContent);
1767 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_PTR(binding, nsXBLBinding,
1768 : "[via binding manager] mBindingTable value")
1769 : }
1770 0 : if (mContentListTable.ops &&
1771 0 : (value = LookupObject(mContentListTable, aContent))) {
1772 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mContentListTable key");
1773 0 : cb.NoteXPCOMChild(aContent);
1774 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mContentListTable value");
1775 0 : cb.NoteXPCOMChild(value);
1776 : }
1777 0 : if (mAnonymousNodesTable.ops &&
1778 0 : (value = LookupObject(mAnonymousNodesTable, aContent))) {
1779 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mAnonymousNodesTable key");
1780 0 : cb.NoteXPCOMChild(aContent);
1781 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mAnonymousNodesTable value");
1782 0 : cb.NoteXPCOMChild(value);
1783 : }
1784 0 : if (mWrapperTable.ops &&
1785 0 : (value = LookupObject(mWrapperTable, aContent))) {
1786 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mWrapperTable key");
1787 0 : cb.NoteXPCOMChild(aContent);
1788 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mWrapperTable value");
1789 0 : cb.NoteXPCOMChild(value);
1790 : }
1791 : }
1792 :
1793 : void
1794 4890 : nsBindingManager::BeginOutermostUpdate()
1795 : {
1796 4890 : mAttachedStackSizeOnOutermost = mAttachedStack.Length();
1797 4890 : }
1798 :
1799 : void
1800 4890 : nsBindingManager::EndOutermostUpdate()
1801 : {
1802 4890 : if (!mProcessingAttachedStack) {
1803 4890 : ProcessAttachedQueue(mAttachedStackSizeOnOutermost);
1804 4890 : mAttachedStackSizeOnOutermost = 0;
1805 : }
1806 4890 : }
1807 :
1808 : void
1809 0 : nsBindingManager::HandleChildInsertion(nsIContent* aContainer,
1810 : nsIContent* aChild,
1811 : PRUint32 aIndexInContainer,
1812 : bool aAppend)
1813 : {
1814 0 : NS_PRECONDITION(aChild, "Must have child");
1815 0 : NS_PRECONDITION(!aContainer ||
1816 : PRUint32(aContainer->IndexOf(aChild)) == aIndexInContainer,
1817 : "Child not at the right index?");
1818 :
1819 0 : nsIContent* ins = GetNestedInsertionPoint(aContainer, aChild);
1820 :
1821 0 : if (ins) {
1822 : PRInt32 insertionIndex;
1823 : nsXBLInsertionPoint* point =
1824 : FindInsertionPointAndIndex(aContainer, ins, aIndexInContainer, aAppend,
1825 0 : &insertionIndex);
1826 0 : if (point) {
1827 0 : point->InsertChildAt(insertionIndex, aChild);
1828 0 : SetInsertionParent(aChild, ins);
1829 : }
1830 : }
1831 4392 : }
|