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 "nsBindingManager.h"
8 :
9 : #include "nsAutoPtr.h"
10 : #include "nsCOMPtr.h"
11 : #include "nsXBLService.h"
12 : #include "nsIInputStream.h"
13 : #include "nsIURI.h"
14 : #include "nsIURL.h"
15 : #include "nsIChannel.h"
16 : #include "nsXPIDLString.h"
17 : #include "plstr.h"
18 : #include "nsIContent.h"
19 : #include "nsIDOMElement.h"
20 : #include "nsIDocument.h"
21 : #include "nsContentUtils.h"
22 : #include "nsIPresShell.h"
23 : #include "nsIPresShellInlines.h"
24 : #include "nsIXMLContentSink.h"
25 : #include "nsContentCID.h"
26 : #include "mozilla/dom/XMLDocument.h"
27 : #include "nsIStreamListener.h"
28 : #include "ChildIterator.h"
29 : #include "nsITimer.h"
30 :
31 : #include "nsXBLBinding.h"
32 : #include "nsXBLPrototypeBinding.h"
33 : #include "nsXBLDocumentInfo.h"
34 : #include "mozilla/dom/XBLChildrenElement.h"
35 :
36 : #include "nsIStyleRuleProcessor.h"
37 : #include "nsRuleProcessorData.h"
38 : #include "nsIWeakReference.h"
39 :
40 : #include "nsWrapperCacheInlines.h"
41 : #include "nsIXPConnect.h"
42 : #include "nsDOMCID.h"
43 : #include "nsIScriptGlobalObject.h"
44 : #include "nsTHashtable.h"
45 :
46 : #include "nsIScriptContext.h"
47 : #include "xpcpublic.h"
48 : #include "jswrapper.h"
49 :
50 : #include "nsThreadUtils.h"
51 : #include "mozilla/dom/NodeListBinding.h"
52 : #include "mozilla/dom/ScriptSettings.h"
53 : #include "mozilla/Unused.h"
54 :
55 : using namespace mozilla;
56 : using namespace mozilla::dom;
57 :
58 : // Implement our nsISupports methods
59 :
60 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsBindingManager)
61 :
62 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsBindingManager)
63 0 : tmp->mDestroyed = true;
64 :
65 0 : if (tmp->mBoundContentSet)
66 0 : tmp->mBoundContentSet->Clear();
67 :
68 0 : if (tmp->mDocumentTable)
69 0 : tmp->mDocumentTable->Clear();
70 :
71 0 : if (tmp->mLoadingDocTable)
72 0 : tmp->mLoadingDocTable->Clear();
73 :
74 0 : if (tmp->mWrapperTable) {
75 0 : tmp->mWrapperTable->Clear();
76 0 : tmp->mWrapperTable = nullptr;
77 : }
78 :
79 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mAttachedStack)
80 :
81 0 : if (tmp->mProcessAttachedQueueEvent) {
82 0 : tmp->mProcessAttachedQueueEvent->Revoke();
83 : }
84 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
85 :
86 :
87 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsBindingManager)
88 : // The hashes keyed on nsIContent are traversed from the nsIContent itself.
89 0 : if (tmp->mDocumentTable) {
90 0 : for (auto iter = tmp->mDocumentTable->Iter(); !iter.Done(); iter.Next()) {
91 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mDocumentTable value");
92 0 : cb.NoteXPCOMChild(iter.UserData());
93 : }
94 : }
95 0 : if (tmp->mLoadingDocTable) {
96 0 : for (auto iter = tmp->mLoadingDocTable->Iter(); !iter.Done(); iter.Next()) {
97 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mLoadingDocTable value");
98 0 : cb.NoteXPCOMChild(iter.UserData());
99 : }
100 : }
101 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAttachedStack)
102 : // No need to traverse mProcessAttachedQueueEvent, since it'll just
103 : // fire at some point or become revoke and drop its ref to us.
104 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
105 :
106 55 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsBindingManager)
107 0 : NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
108 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
109 0 : NS_INTERFACE_MAP_END
110 :
111 101 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsBindingManager)
112 22 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsBindingManager)
113 :
114 : // Constructors/Destructors
115 55 : nsBindingManager::nsBindingManager(nsIDocument* aDocument)
116 : : mProcessingAttachedStack(false),
117 : mDestroyed(false),
118 : mAttachedStackSizeOnOutermost(0),
119 55 : mDocument(aDocument)
120 : {
121 55 : }
122 :
123 0 : nsBindingManager::~nsBindingManager(void)
124 : {
125 0 : mDestroyed = true;
126 0 : }
127 :
128 : nsXBLBinding*
129 958 : nsBindingManager::GetBindingWithContent(const nsIContent* aContent)
130 : {
131 958 : nsXBLBinding* binding = aContent ? aContent->GetXBLBinding() : nullptr;
132 958 : return binding ? binding->GetBindingWithContent() : nullptr;
133 : }
134 :
135 : void
136 272 : nsBindingManager::AddBoundContent(nsIContent* aContent)
137 : {
138 272 : if (!mBoundContentSet) {
139 3 : mBoundContentSet = new nsTHashtable<nsRefPtrHashKey<nsIContent> >;
140 : }
141 272 : mBoundContentSet->PutEntry(aContent);
142 272 : }
143 :
144 : void
145 12 : nsBindingManager::RemoveBoundContent(nsIContent* aContent)
146 : {
147 12 : if (mBoundContentSet) {
148 12 : mBoundContentSet->RemoveEntry(aContent);
149 : }
150 :
151 : // The death of the bindings means the death of the JS wrapper.
152 12 : SetWrappedJS(aContent, nullptr);
153 12 : }
154 :
155 : nsIXPConnectWrappedJS*
156 351 : nsBindingManager::GetWrappedJS(nsIContent* aContent)
157 : {
158 351 : if (!mWrapperTable) {
159 198 : return nullptr;
160 : }
161 :
162 153 : if (!aContent || !aContent->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
163 0 : return nullptr;
164 : }
165 :
166 153 : return mWrapperTable->GetWeak(aContent);
167 : }
168 :
169 : nsresult
170 30 : nsBindingManager::SetWrappedJS(nsIContent* aContent, nsIXPConnectWrappedJS* aWrappedJS)
171 : {
172 30 : if (mDestroyed) {
173 0 : return NS_OK;
174 : }
175 :
176 30 : if (aWrappedJS) {
177 : // lazily create the table, but only when adding elements
178 18 : if (!mWrapperTable) {
179 1 : mWrapperTable = new WrapperHashtable();
180 : }
181 18 : aContent->SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
182 :
183 18 : NS_ASSERTION(aContent, "key must be non-null");
184 18 : if (!aContent) return NS_ERROR_INVALID_ARG;
185 :
186 18 : mWrapperTable->Put(aContent, aWrappedJS);
187 :
188 18 : return NS_OK;
189 : }
190 :
191 : // no value, so remove the key from the table
192 12 : if (mWrapperTable) {
193 12 : mWrapperTable->Remove(aContent);
194 : }
195 :
196 12 : return NS_OK;
197 : }
198 :
199 : void
200 12 : nsBindingManager::RemovedFromDocumentInternal(nsIContent* aContent,
201 : nsIDocument* aOldDocument,
202 : DestructorHandling aDestructorHandling)
203 : {
204 12 : NS_PRECONDITION(aOldDocument != nullptr, "no old document");
205 :
206 24 : RefPtr<nsXBLBinding> binding = aContent->GetXBLBinding();
207 12 : if (binding) {
208 : // The binding manager may have been destroyed before a runnable
209 : // has had a chance to reach this point. If so, we bail out on calling
210 : // BindingDetached (which may invoke a XBL destructor) and
211 : // ChangeDocument, but we still want to clear out the binding
212 : // and insertion parent that may hold references.
213 7 : if (!mDestroyed && aDestructorHandling == eRunDtor) {
214 7 : binding->PrototypeBinding()->BindingDetached(binding->GetBoundElement());
215 7 : binding->ChangeDocument(aOldDocument, nullptr);
216 : }
217 :
218 7 : aContent->SetXBLBinding(nullptr, this);
219 : }
220 :
221 : // Clear out insertion parent and content lists.
222 12 : aContent->SetXBLInsertionParent(nullptr);
223 12 : }
224 :
225 : nsIAtom*
226 766 : nsBindingManager::ResolveTag(nsIContent* aContent, int32_t* aNameSpaceID)
227 : {
228 766 : nsXBLBinding *binding = aContent->GetXBLBinding();
229 :
230 766 : if (binding) {
231 432 : nsIAtom* base = binding->GetBaseTag(aNameSpaceID);
232 :
233 432 : if (base) {
234 86 : return base;
235 : }
236 : }
237 :
238 680 : *aNameSpaceID = aContent->GetNameSpaceID();
239 680 : return aContent->NodeInfo()->NameAtom();
240 : }
241 :
242 : nsresult
243 0 : nsBindingManager::GetAnonymousNodesFor(nsIContent* aContent,
244 : nsIDOMNodeList** aResult)
245 : {
246 0 : NS_IF_ADDREF(*aResult = GetAnonymousNodesFor(aContent));
247 0 : return NS_OK;
248 : }
249 :
250 : nsINodeList*
251 19 : nsBindingManager::GetAnonymousNodesFor(nsIContent* aContent)
252 : {
253 19 : nsXBLBinding* binding = GetBindingWithContent(aContent);
254 19 : return binding ? binding->GetAnonymousNodeList() : nullptr;
255 : }
256 :
257 : nsresult
258 0 : nsBindingManager::ClearBinding(Element* aElement)
259 : {
260 : // Hold a ref to the binding so it won't die when we remove it from our table
261 : RefPtr<nsXBLBinding> binding =
262 0 : aElement ? aElement->GetXBLBinding() : nullptr;
263 :
264 0 : if (!binding) {
265 0 : return NS_OK;
266 : }
267 :
268 : // For now we can only handle removing a binding if it's the only one
269 0 : NS_ENSURE_FALSE(binding->GetBaseBinding(), NS_ERROR_FAILURE);
270 :
271 : // Hold strong ref in case removing the binding tries to close the
272 : // window or something.
273 : // XXXbz should that be ownerdoc? Wouldn't we need a ref to the
274 : // currentdoc too? What's the one that should be passed to
275 : // ChangeDocument?
276 0 : nsCOMPtr<nsIDocument> doc = aElement->OwnerDoc();
277 :
278 : // Finally remove the binding...
279 : // XXXbz this doesn't remove the implementation! Should fix! Until
280 : // then we need the explicit UnhookEventHandlers here.
281 0 : binding->UnhookEventHandlers();
282 0 : binding->ChangeDocument(doc, nullptr);
283 0 : aElement->SetXBLBinding(nullptr, this);
284 0 : binding->MarkForDeath();
285 :
286 : // ...and recreate its frames. We need to do this since the frames may have
287 : // been removed and style may have changed due to the removal of the
288 : // anonymous children.
289 : // XXXbz this should be using the current doc (if any), not the owner doc.
290 0 : nsIPresShell *presShell = doc->GetShell();
291 0 : NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
292 :
293 0 : presShell->PostRecreateFramesFor(aElement);
294 0 : return NS_OK;
295 : }
296 :
297 : nsresult
298 0 : nsBindingManager::LoadBindingDocument(nsIDocument* aBoundDoc,
299 : nsIURI* aURL,
300 : nsIPrincipal* aOriginPrincipal)
301 : {
302 0 : NS_PRECONDITION(aURL, "Must have a URI to load!");
303 :
304 : // First we need to load our binding.
305 0 : nsXBLService* xblService = nsXBLService::GetInstance();
306 0 : if (!xblService)
307 0 : return NS_ERROR_FAILURE;
308 :
309 : // Load the binding doc.
310 0 : RefPtr<nsXBLDocumentInfo> info;
311 0 : xblService->LoadBindingDocumentInfo(nullptr, aBoundDoc, aURL,
312 : aOriginPrincipal, true,
313 0 : getter_AddRefs(info));
314 0 : if (!info)
315 0 : return NS_ERROR_FAILURE;
316 :
317 0 : return NS_OK;
318 : }
319 :
320 : void
321 12 : nsBindingManager::RemoveFromAttachedQueue(nsXBLBinding* aBinding)
322 : {
323 : // Don't remove items here as that could mess up an executing
324 : // ProcessAttachedQueue. Instead, null the entry in the queue.
325 12 : size_t index = mAttachedStack.IndexOf(aBinding);
326 12 : if (index != mAttachedStack.NoIndex) {
327 0 : mAttachedStack[index] = nullptr;
328 : }
329 12 : }
330 :
331 : nsresult
332 238 : nsBindingManager::AddToAttachedQueue(nsXBLBinding* aBinding)
333 : {
334 238 : mAttachedStack.AppendElement(aBinding);
335 :
336 : // If we're in the middle of processing our queue already, don't
337 : // bother posting the event.
338 238 : if (!mProcessingAttachedStack && !mProcessAttachedQueueEvent) {
339 6 : PostProcessAttachedQueueEvent();
340 : }
341 :
342 : // Make sure that flushes will flush out the new items as needed.
343 238 : if (nsIPresShell* shell = mDocument->GetShell()) {
344 238 : shell->SetNeedStyleFlush();
345 : }
346 :
347 238 : return NS_OK;
348 :
349 : }
350 :
351 : void
352 6 : nsBindingManager::PostProcessAttachedQueueEvent()
353 : {
354 6 : MOZ_ASSERT(NS_IsMainThread());
355 6 : if (!mDocument) {
356 0 : return;
357 : }
358 : mProcessAttachedQueueEvent =
359 12 : NewRunnableMethod("nsBindingManager::DoProcessAttachedQueue",
360 6 : this, &nsBindingManager::DoProcessAttachedQueue);
361 6 : nsresult rv = mDocument->EventTargetFor(TaskCategory::Other)->Dispatch(do_AddRef(mProcessAttachedQueueEvent));
362 6 : if (NS_SUCCEEDED(rv)) {
363 6 : mDocument->BlockOnload();
364 : }
365 : }
366 :
367 : // static
368 : void
369 0 : nsBindingManager::PostPAQEventCallback(nsITimer* aTimer, void* aClosure)
370 : {
371 : RefPtr<nsBindingManager> mgr =
372 0 : already_AddRefed<nsBindingManager>(static_cast<nsBindingManager*>(aClosure));
373 0 : mgr->PostProcessAttachedQueueEvent();
374 0 : NS_RELEASE(aTimer);
375 0 : }
376 :
377 : void
378 6 : nsBindingManager::DoProcessAttachedQueue()
379 : {
380 6 : if (!mProcessingAttachedStack) {
381 6 : ProcessAttachedQueue();
382 :
383 6 : NS_ASSERTION(mAttachedStack.Length() == 0,
384 : "Shouldn't have pending bindings!");
385 :
386 6 : mProcessAttachedQueueEvent = nullptr;
387 : } else {
388 : // Someone's doing event processing from inside a constructor.
389 : // They're evil, but we'll fight back! Just poll on them being
390 : // done and repost the attached queue event.
391 : //
392 : // But don't poll in a tight loop -- otherwise we keep the Gecko
393 : // event loop non-empty and trigger bug 1021240 on OS X.
394 0 : nsresult rv = NS_ERROR_FAILURE;
395 0 : nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
396 0 : if (timer) {
397 0 : rv = timer->InitWithNamedFuncCallback(
398 : PostPAQEventCallback,
399 : this,
400 : 100,
401 : nsITimer::TYPE_ONE_SHOT,
402 0 : "nsBindingManager::DoProcessAttachedQueue");
403 : }
404 0 : if (NS_SUCCEEDED(rv)) {
405 0 : NS_ADDREF_THIS();
406 : // We drop our reference to the timer here, since the timer callback is
407 : // responsible for releasing the object.
408 0 : Unused << timer.forget().take();
409 : }
410 : }
411 :
412 : // No matter what, unblock onload for the event that's fired.
413 6 : if (mDocument) {
414 : // Hold a strong reference while calling UnblockOnload since that might
415 : // run script.
416 12 : nsCOMPtr<nsIDocument> doc = mDocument;
417 6 : doc->UnblockOnload(true);
418 : }
419 6 : }
420 :
421 : void
422 10 : nsBindingManager::ProcessAttachedQueueInternal(uint32_t aSkipSize)
423 : {
424 10 : mProcessingAttachedStack = true;
425 :
426 : // Excute constructors. Do this from high index to low
427 486 : while (mAttachedStack.Length() > aSkipSize) {
428 238 : uint32_t lastItem = mAttachedStack.Length() - 1;
429 476 : RefPtr<nsXBLBinding> binding = mAttachedStack.ElementAt(lastItem);
430 238 : mAttachedStack.RemoveElementAt(lastItem);
431 238 : if (binding) {
432 238 : binding->ExecuteAttachedHandler();
433 : }
434 : }
435 :
436 : // If NodeWillBeDestroyed has run we don't want to clobber
437 : // mProcessingAttachedStack set there.
438 10 : if (mDocument) {
439 10 : mProcessingAttachedStack = false;
440 : }
441 :
442 10 : NS_ASSERTION(mAttachedStack.Length() == aSkipSize, "How did we get here?");
443 :
444 10 : mAttachedStack.Compact();
445 10 : }
446 :
447 : // Keep bindings and bound elements alive while executing detached handlers.
448 : void
449 4 : nsBindingManager::ExecuteDetachedHandlers()
450 : {
451 : // Walk our hashtable of bindings.
452 4 : if (!mBoundContentSet) {
453 4 : return;
454 : }
455 :
456 0 : nsCOMArray<nsIContent> boundElements;
457 0 : nsBindingList bindings;
458 :
459 0 : for (auto iter = mBoundContentSet->Iter(); !iter.Done(); iter.Next()) {
460 0 : nsXBLBinding* binding = iter.Get()->GetKey()->GetXBLBinding();
461 0 : if (binding && bindings.AppendElement(binding)) {
462 0 : if (!boundElements.AppendObject(binding->GetBoundElement())) {
463 0 : bindings.RemoveElementAt(bindings.Length() - 1);
464 : }
465 : }
466 : }
467 :
468 0 : uint32_t i, count = bindings.Length();
469 0 : for (i = 0; i < count; ++i) {
470 0 : bindings[i]->ExecuteDetachedHandler();
471 : }
472 : }
473 :
474 : nsresult
475 634 : nsBindingManager::PutXBLDocumentInfo(nsXBLDocumentInfo* aDocumentInfo)
476 : {
477 634 : NS_PRECONDITION(aDocumentInfo, "Must have a non-null documentinfo!");
478 :
479 634 : if (!mDocumentTable) {
480 4 : mDocumentTable = new nsRefPtrHashtable<nsURIHashKey,nsXBLDocumentInfo>();
481 : }
482 :
483 634 : mDocumentTable->Put(aDocumentInfo->DocumentURI(), aDocumentInfo);
484 :
485 634 : return NS_OK;
486 : }
487 :
488 : void
489 1 : nsBindingManager::RemoveXBLDocumentInfo(nsXBLDocumentInfo* aDocumentInfo)
490 : {
491 1 : if (mDocumentTable) {
492 1 : mDocumentTable->Remove(aDocumentInfo->DocumentURI());
493 : }
494 1 : }
495 :
496 : nsXBLDocumentInfo*
497 634 : nsBindingManager::GetXBLDocumentInfo(nsIURI* aURL)
498 : {
499 634 : if (!mDocumentTable)
500 3 : return nullptr;
501 :
502 631 : return mDocumentTable->GetWeak(aURL);
503 : }
504 :
505 : nsresult
506 0 : nsBindingManager::PutLoadingDocListener(nsIURI* aURL, nsIStreamListener* aListener)
507 : {
508 0 : NS_PRECONDITION(aListener, "Must have a non-null listener!");
509 :
510 0 : if (!mLoadingDocTable) {
511 : mLoadingDocTable =
512 0 : new nsInterfaceHashtable<nsURIHashKey,nsIStreamListener>();
513 : }
514 0 : mLoadingDocTable->Put(aURL, aListener);
515 :
516 0 : return NS_OK;
517 : }
518 :
519 : nsIStreamListener*
520 23 : nsBindingManager::GetLoadingDocListener(nsIURI* aURL)
521 : {
522 23 : if (!mLoadingDocTable)
523 23 : return nullptr;
524 :
525 0 : return mLoadingDocTable->GetWeak(aURL);
526 : }
527 :
528 : void
529 0 : nsBindingManager::RemoveLoadingDocListener(nsIURI* aURL)
530 : {
531 0 : if (mLoadingDocTable) {
532 0 : mLoadingDocTable->Remove(aURL);
533 : }
534 0 : }
535 :
536 : void
537 0 : nsBindingManager::FlushSkinBindings()
538 : {
539 0 : if (!mBoundContentSet) {
540 0 : return;
541 : }
542 :
543 0 : for (auto iter = mBoundContentSet->Iter(); !iter.Done(); iter.Next()) {
544 0 : nsXBLBinding* binding = iter.Get()->GetKey()->GetXBLBinding();
545 :
546 0 : if (binding->MarkedForDeath()) {
547 0 : continue;
548 : }
549 :
550 0 : nsAutoCString path;
551 0 : binding->PrototypeBinding()->DocURI()->GetPath(path);
552 :
553 0 : if (!strncmp(path.get(), "/skin", 5)) {
554 0 : binding->MarkForDeath();
555 : }
556 : }
557 : }
558 :
559 : // Used below to protect from recurring in QI calls through XPConnect.
560 : struct AntiRecursionData {
561 : nsIContent* element;
562 : REFNSIID iid;
563 : AntiRecursionData* next;
564 :
565 48 : AntiRecursionData(nsIContent* aElement,
566 : REFNSIID aIID,
567 : AntiRecursionData* aNext)
568 48 : : element(aElement), iid(aIID), next(aNext) {}
569 : };
570 :
571 : nsresult
572 210 : nsBindingManager::GetBindingImplementation(nsIContent* aContent, REFNSIID aIID,
573 : void** aResult)
574 : {
575 210 : *aResult = nullptr;
576 210 : nsXBLBinding *binding = aContent ? aContent->GetXBLBinding() : nullptr;
577 210 : if (binding) {
578 : // The binding should not be asked for nsISupports
579 140 : NS_ASSERTION(!aIID.Equals(NS_GET_IID(nsISupports)), "Asking a binding for nsISupports");
580 140 : if (binding->ImplementsInterface(aIID)) {
581 132 : nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS = GetWrappedJS(aContent);
582 :
583 66 : if (wrappedJS) {
584 : // Protect from recurring in QI calls through XPConnect.
585 : // This can happen when a second binding is being resolved.
586 : // At that point a wrappedJS exists, but it doesn't yet know about
587 : // the iid we are asking for. So, without this protection,
588 : // AggregatedQueryInterface would end up recurring back into itself
589 : // through this code.
590 : //
591 : // With this protection, when we detect the recursion we return
592 : // NS_NOINTERFACE in the inner call. The outer call will then fall
593 : // through (see below) and build a new chained wrappedJS for the iid.
594 : //
595 : // We're careful to not assume that only one direct nesting can occur
596 : // because there is a call into JS in the middle and we can't assume
597 : // that this code won't be reached by some more complex nesting path.
598 : //
599 : // NOTE: We *assume* this is single threaded, so we can use a
600 : // static linked list to do the check.
601 :
602 : static AntiRecursionData* list = nullptr;
603 :
604 49 : for (AntiRecursionData* p = list; p; p = p->next) {
605 1 : if (p->element == aContent && p->iid.Equals(aIID)) {
606 1 : *aResult = nullptr;
607 49 : return NS_NOINTERFACE;
608 : }
609 : }
610 :
611 48 : AntiRecursionData item(aContent, aIID, list);
612 48 : list = &item;
613 :
614 48 : nsresult rv = wrappedJS->AggregatedQueryInterface(aIID, aResult);
615 :
616 48 : list = item.next;
617 :
618 48 : if (*aResult)
619 47 : return rv;
620 :
621 : // No result was found, so this must be another XBL interface.
622 : // Fall through to create a new wrapper.
623 : }
624 :
625 : // We have never made a wrapper for this implementation.
626 : // Create an XPC wrapper for the script object and hand it back.
627 36 : AutoJSAPI jsapi;
628 18 : jsapi.Init();
629 18 : JSContext* cx = jsapi.cx();
630 :
631 18 : nsIXPConnect *xpConnect = nsContentUtils::XPConnect();
632 :
633 36 : JS::Rooted<JSObject*> jsobj(cx, aContent->GetWrapper());
634 18 : NS_ENSURE_TRUE(jsobj, NS_NOINTERFACE);
635 :
636 : // If we're using an XBL scope, we need to use the Xray view to the bound
637 : // content in order to view the full array of methods defined in the
638 : // binding, some of which may not be exposed on the prototype of
639 : // untrusted content. We don't need to consider add-on scopes here
640 : // because they're chrome-only and no Xrays are involved.
641 : //
642 : // If there's no separate XBL scope, or if the reflector itself lives in
643 : // the XBL scope, we'll end up with the global of the reflector.
644 36 : JS::Rooted<JSObject*> xblScope(cx, xpc::GetXBLScopeOrGlobal(cx, jsobj));
645 18 : NS_ENSURE_TRUE(xblScope, NS_ERROR_UNEXPECTED);
646 36 : JSAutoCompartment ac(cx, xblScope);
647 18 : bool ok = JS_WrapObject(cx, &jsobj);
648 18 : NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
649 18 : MOZ_ASSERT_IF(js::IsWrapper(jsobj), xpc::IsXrayWrapper(jsobj));
650 :
651 18 : nsresult rv = xpConnect->WrapJSAggregatedToNative(aContent, cx,
652 36 : jsobj, aIID, aResult);
653 18 : if (NS_FAILED(rv))
654 0 : return rv;
655 :
656 : // We successfully created a wrapper. We will own this wrapper for as long as the binding remains
657 : // alive. At the time the binding is cleared out of the bindingManager, we will remove the wrapper
658 : // from the bindingManager as well.
659 18 : nsISupports* supp = static_cast<nsISupports*>(*aResult);
660 18 : wrappedJS = do_QueryInterface(supp);
661 18 : SetWrappedJS(aContent, wrappedJS);
662 :
663 18 : return rv;
664 : }
665 : }
666 :
667 144 : *aResult = nullptr;
668 144 : return NS_NOINTERFACE;
669 : }
670 :
671 : nsresult
672 4488 : nsBindingManager::WalkRules(nsIStyleRuleProcessor::EnumFunc aFunc,
673 : ElementDependentRuleProcessorData* aData,
674 : bool* aCutOffInheritance)
675 : {
676 4488 : *aCutOffInheritance = false;
677 :
678 4488 : NS_ASSERTION(aData->mElement, "How did that happen?");
679 :
680 : // Walk the binding scope chain, starting with the binding attached to our
681 : // content, up till we run out of scopes or we get cut off.
682 4488 : nsIContent *content = aData->mElement;
683 :
684 1850 : do {
685 6338 : nsXBLBinding *binding = content->GetXBLBinding();
686 6338 : if (binding) {
687 3603 : binding->WalkRules(aFunc, aData);
688 : // If we're not looking at our original content, allow the binding to cut
689 : // off style inheritance
690 3603 : if (content != aData->mElement) {
691 1804 : if (!binding->InheritsStyle()) {
692 : // Go no further; we're not inheriting style from anything above here
693 0 : break;
694 : }
695 : }
696 : }
697 :
698 6338 : if (content->IsRootOfNativeAnonymousSubtree()) {
699 367 : break; // Deliberately cut off style inheritance here.
700 : }
701 :
702 5971 : content = content->GetBindingParent();
703 5971 : } while (content);
704 :
705 : // If "content" is non-null that means we cut off inheritance at some point
706 : // in the loop.
707 4488 : *aCutOffInheritance = (content != nullptr);
708 :
709 4488 : return NS_OK;
710 : }
711 :
712 : typedef nsTHashtable<nsPtrHashKey<nsIStyleRuleProcessor> > RuleProcessorSet;
713 :
714 : static RuleProcessorSet*
715 5 : GetContentSetRuleProcessors(nsTHashtable<nsRefPtrHashKey<nsIContent>>* aContentSet)
716 : {
717 5 : RuleProcessorSet* set = nullptr;
718 :
719 962 : for (auto iter = aContentSet->Iter(); !iter.Done(); iter.Next()) {
720 957 : nsIContent* boundContent = iter.Get()->GetKey();
721 3184 : for (nsXBLBinding* binding = boundContent->GetXBLBinding(); binding;
722 : binding = binding->GetBaseBinding()) {
723 : nsIStyleRuleProcessor* ruleProc =
724 2227 : binding->PrototypeBinding()->GetRuleProcessor();
725 2227 : if (ruleProc) {
726 596 : if (!set) {
727 4 : set = new RuleProcessorSet;
728 : }
729 596 : set->PutEntry(ruleProc);
730 : }
731 : }
732 : }
733 :
734 5 : return set;
735 : }
736 :
737 : void
738 2 : nsBindingManager::WalkAllRules(nsIStyleRuleProcessor::EnumFunc aFunc,
739 : ElementDependentRuleProcessorData* aData)
740 : {
741 2 : if (!mBoundContentSet) {
742 1 : return;
743 : }
744 :
745 3 : nsAutoPtr<RuleProcessorSet> set;
746 2 : set = GetContentSetRuleProcessors(mBoundContentSet);
747 2 : if (!set) {
748 1 : return;
749 : }
750 :
751 24 : for (auto iter = set->Iter(); !iter.Done(); iter.Next()) {
752 23 : nsIStyleRuleProcessor* ruleProcessor = iter.Get()->GetKey();
753 23 : (*(aFunc))(ruleProcessor, aData);
754 : }
755 : }
756 :
757 : nsresult
758 21 : nsBindingManager::MediumFeaturesChanged(nsPresContext* aPresContext,
759 : bool* aRulesChanged)
760 : {
761 21 : *aRulesChanged = false;
762 21 : if (!mBoundContentSet) {
763 18 : return NS_OK;
764 : }
765 :
766 6 : nsAutoPtr<RuleProcessorSet> set;
767 3 : set = GetContentSetRuleProcessors(mBoundContentSet);
768 3 : if (!set) {
769 0 : return NS_OK;
770 : }
771 :
772 72 : for (auto iter = set->Iter(); !iter.Done(); iter.Next()) {
773 69 : nsIStyleRuleProcessor* ruleProcessor = iter.Get()->GetKey();
774 69 : bool thisChanged = ruleProcessor->MediumFeaturesChanged(aPresContext);
775 69 : *aRulesChanged = *aRulesChanged || thisChanged;
776 : }
777 :
778 3 : return NS_OK;
779 : }
780 :
781 : void
782 0 : nsBindingManager::AppendAllSheets(nsTArray<StyleSheet*>& aArray)
783 : {
784 0 : if (!mBoundContentSet) {
785 0 : return;
786 : }
787 :
788 0 : for (auto iter = mBoundContentSet->Iter(); !iter.Done(); iter.Next()) {
789 0 : nsIContent* boundContent = iter.Get()->GetKey();
790 0 : for (nsXBLBinding* binding = boundContent->GetXBLBinding(); binding;
791 : binding = binding->GetBaseBinding()) {
792 0 : binding->PrototypeBinding()->AppendStyleSheetsTo(aArray);
793 : }
794 : }
795 : }
796 :
797 : static void
798 0 : InsertAppendedContent(XBLChildrenElement* aPoint,
799 : nsIContent* aFirstNewContent)
800 : {
801 : int32_t insertionIndex;
802 0 : if (nsIContent* prevSibling = aFirstNewContent->GetPreviousSibling()) {
803 : // If we have a previous sibling, then it must already be in aPoint. Find
804 : // it and insert after it.
805 0 : insertionIndex = aPoint->IndexOfInsertedChild(prevSibling);
806 0 : MOZ_ASSERT(insertionIndex != -1);
807 :
808 : // Our insertion index is one after our previous sibling's index.
809 0 : ++insertionIndex;
810 : } else {
811 : // Otherwise, we append.
812 : // TODO This is wrong for nested insertion points. In that case, we need to
813 : // keep track of the right index to insert into.
814 0 : insertionIndex = aPoint->InsertedChildrenLength();
815 : }
816 :
817 : // Do the inserting.
818 0 : for (nsIContent* currentChild = aFirstNewContent;
819 0 : currentChild;
820 0 : currentChild = currentChild->GetNextSibling()) {
821 0 : aPoint->InsertInsertedChildAt(currentChild, insertionIndex++);
822 : }
823 0 : }
824 :
825 : void
826 143 : nsBindingManager::ContentAppended(nsIDocument* aDocument,
827 : nsIContent* aContainer,
828 : nsIContent* aFirstNewContent,
829 : int32_t aNewIndexInContainer)
830 : {
831 143 : if (aNewIndexInContainer == -1) {
832 0 : return;
833 : }
834 :
835 : // Try to find insertion points for all the new kids.
836 143 : XBLChildrenElement* point = nullptr;
837 143 : nsIContent* parent = aContainer;
838 :
839 : // Handle appending of default content.
840 143 : if (parent && parent->IsActiveChildrenElement()) {
841 0 : XBLChildrenElement* childrenEl = static_cast<XBLChildrenElement*>(parent);
842 0 : if (childrenEl->HasInsertedChildren()) {
843 : // Appending default content that isn't being used. Ignore.
844 0 : return;
845 : }
846 :
847 0 : childrenEl->MaybeSetupDefaultContent();
848 0 : parent = childrenEl->GetParent();
849 : }
850 :
851 143 : bool first = true;
852 2 : do {
853 145 : nsXBLBinding* binding = GetBindingWithContent(parent);
854 145 : if (!binding) {
855 138 : break;
856 : }
857 :
858 7 : if (binding->HasFilteredInsertionPoints()) {
859 : // There are filtered insertion points involved, handle each child
860 : // separately.
861 : // We could optimize this in the case when we've nested a few levels
862 : // deep already, without hitting bindings that have filtered insertion
863 : // points.
864 4 : int32_t currentIndex = aNewIndexInContainer;
865 8 : for (nsIContent* currentChild = aFirstNewContent; currentChild;
866 4 : currentChild = currentChild->GetNextSibling()) {
867 4 : HandleChildInsertion(aContainer, currentChild,
868 8 : currentIndex++, true);
869 : }
870 :
871 4 : return;
872 : }
873 :
874 3 : point = binding->GetDefaultInsertionPoint();
875 3 : if (!point) {
876 0 : break;
877 : }
878 :
879 : // Even though we're in ContentAppended, nested insertion points force us
880 : // to deal with this append as an insertion except in the outermost
881 : // binding.
882 3 : if (first) {
883 3 : first = false;
884 19 : for (nsIContent* child = aFirstNewContent; child;
885 16 : child = child->GetNextSibling()) {
886 16 : point->AppendInsertedChild(child);
887 : }
888 : } else {
889 0 : InsertAppendedContent(point, aFirstNewContent);
890 : }
891 :
892 3 : nsIContent* newParent = point->GetParent();
893 3 : if (newParent == parent) {
894 1 : break;
895 : }
896 2 : parent = newParent;
897 2 : } while (parent);
898 : }
899 :
900 : void
901 71 : nsBindingManager::ContentInserted(nsIDocument* aDocument,
902 : nsIContent* aContainer,
903 : nsIContent* aChild,
904 : int32_t aIndexInContainer)
905 : {
906 71 : if (aIndexInContainer == -1) {
907 0 : return;
908 : }
909 :
910 71 : HandleChildInsertion(aContainer, aChild, aIndexInContainer, false);
911 : }
912 :
913 : void
914 47 : nsBindingManager::ContentRemoved(nsIDocument* aDocument,
915 : nsIContent* aContainer,
916 : nsIContent* aChild,
917 : int32_t aIndexInContainer,
918 : nsIContent* aPreviousSibling)
919 : {
920 47 : aChild->SetXBLInsertionParent(nullptr);
921 :
922 47 : XBLChildrenElement* point = nullptr;
923 47 : nsIContent* parent = aContainer;
924 :
925 : // Handle appending of default content.
926 47 : if (parent && parent->IsActiveChildrenElement()) {
927 0 : XBLChildrenElement* childrenEl = static_cast<XBLChildrenElement*>(parent);
928 0 : if (childrenEl->HasInsertedChildren()) {
929 : // Removing default content that isn't being used. Ignore.
930 0 : return;
931 : }
932 :
933 0 : parent = childrenEl->GetParent();
934 : }
935 :
936 2 : do {
937 49 : nsXBLBinding* binding = GetBindingWithContent(parent);
938 49 : if (!binding) {
939 : // If aChild is XBL content, it might have <xbl:children> elements
940 : // somewhere under it. We need to inform those elements that they're no
941 : // longer in the tree so they can tell their distributed children that
942 : // they're no longer distributed under them.
943 : // XXX This is wrong. We need to do far more work to update the parent
944 : // binding's list of insertion points and to get the new insertion parent
945 : // for the newly-distributed children correct.
946 47 : if (aChild->GetBindingParent()) {
947 5 : ClearInsertionPointsRecursively(aChild);
948 : }
949 47 : return;
950 : }
951 :
952 2 : point = binding->FindInsertionPointFor(aChild);
953 2 : if (!point) {
954 0 : break;
955 : }
956 :
957 2 : point->RemoveInsertedChild(aChild);
958 :
959 2 : nsIContent* newParent = point->GetParent();
960 2 : if (newParent == parent) {
961 0 : break;
962 : }
963 2 : parent = newParent;
964 2 : } while (parent);
965 : }
966 :
967 : void
968 5 : nsBindingManager::ClearInsertionPointsRecursively(nsIContent* aContent)
969 : {
970 5 : if (aContent->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
971 0 : static_cast<XBLChildrenElement*>(aContent)->ClearInsertedChildren();
972 : }
973 :
974 5 : for (nsIContent* child = aContent->GetFirstChild(); child;
975 0 : child = child->GetNextSibling()) {
976 0 : ClearInsertionPointsRecursively(child);
977 : }
978 5 : }
979 :
980 : void
981 0 : nsBindingManager::DropDocumentReference()
982 : {
983 0 : mDestroyed = true;
984 :
985 : // Make sure to not run any more XBL constructors
986 0 : mProcessingAttachedStack = true;
987 0 : if (mProcessAttachedQueueEvent) {
988 0 : mProcessAttachedQueueEvent->Revoke();
989 : }
990 :
991 0 : if (mBoundContentSet) {
992 0 : mBoundContentSet->Clear();
993 : }
994 :
995 0 : mDocument = nullptr;
996 0 : }
997 :
998 : void
999 432 : nsBindingManager::Traverse(nsIContent *aContent,
1000 : nsCycleCollectionTraversalCallback &cb)
1001 : {
1002 717 : if (!aContent->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) ||
1003 285 : !aContent->IsElement()) {
1004 : // Don't traverse if content is not in this binding manager.
1005 : // We also don't traverse non-elements because there should not
1006 : // be bindings (checking the flag alone is not sufficient because
1007 : // the flag is also set on children of insertion points that may be
1008 : // non-elements).
1009 147 : return;
1010 : }
1011 :
1012 285 : if (mBoundContentSet && mBoundContentSet->Contains(aContent)) {
1013 279 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mBoundContentSet entry");
1014 279 : cb.NoteXPCOMChild(aContent);
1015 : }
1016 :
1017 285 : nsIXPConnectWrappedJS *value = GetWrappedJS(aContent);
1018 285 : if (value) {
1019 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mWrapperTable key");
1020 0 : cb.NoteXPCOMChild(aContent);
1021 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mWrapperTable value");
1022 0 : cb.NoteXPCOMChild(value);
1023 : }
1024 : }
1025 :
1026 : void
1027 75 : nsBindingManager::HandleChildInsertion(nsIContent* aContainer,
1028 : nsIContent* aChild,
1029 : uint32_t aIndexInContainer,
1030 : bool aAppend)
1031 : {
1032 75 : NS_PRECONDITION(aChild, "Must have child");
1033 75 : NS_PRECONDITION(!aContainer ||
1034 : uint32_t(aContainer->IndexOf(aChild)) == aIndexInContainer,
1035 : "Child not at the right index?");
1036 :
1037 75 : XBLChildrenElement* point = nullptr;
1038 75 : nsIContent* parent = aContainer;
1039 :
1040 : // Handle insertion of default content.
1041 75 : if (parent && parent->IsActiveChildrenElement()) {
1042 0 : XBLChildrenElement* childrenEl = static_cast<XBLChildrenElement*>(parent);
1043 0 : if (childrenEl->HasInsertedChildren()) {
1044 : // Inserting default content that isn't being used. Ignore.
1045 0 : return;
1046 : }
1047 :
1048 0 : childrenEl->MaybeSetupDefaultContent();
1049 0 : parent = childrenEl->GetParent();
1050 : }
1051 :
1052 101 : while (parent) {
1053 36 : nsXBLBinding* binding = GetBindingWithContent(parent);
1054 36 : if (!binding) {
1055 19 : break;
1056 : }
1057 :
1058 17 : point = binding->FindInsertionPointFor(aChild);
1059 17 : if (!point) {
1060 0 : break;
1061 : }
1062 :
1063 : // Insert the child into the proper insertion point.
1064 : // TODO If there were multiple insertion points, this approximation can be
1065 : // wrong. We need to re-run the distribution algorithm. In the meantime,
1066 : // this should work well enough.
1067 17 : uint32_t index = aAppend ? point->InsertedChildrenLength() : 0;
1068 17 : for (nsIContent* currentSibling = aChild->GetPreviousSibling();
1069 17 : currentSibling;
1070 0 : currentSibling = currentSibling->GetPreviousSibling()) {
1071 : // If we find one of our previous siblings in the insertion point, the
1072 : // index following it is the correct insertion point. Otherwise, we guess
1073 : // based on whether we're appending or inserting.
1074 13 : int32_t pointIndex = point->IndexOfInsertedChild(currentSibling);
1075 13 : if (pointIndex != -1) {
1076 13 : index = pointIndex + 1;
1077 13 : break;
1078 : }
1079 : }
1080 :
1081 17 : point->InsertInsertedChildAt(aChild, index);
1082 :
1083 17 : nsIContent* newParent = point->GetParent();
1084 17 : if (newParent == parent) {
1085 4 : break;
1086 : }
1087 :
1088 13 : parent = newParent;
1089 : }
1090 : }
1091 :
1092 :
1093 : nsIContent*
1094 46 : nsBindingManager::FindNestedInsertionPoint(nsIContent* aContainer,
1095 : nsIContent* aChild)
1096 : {
1097 46 : NS_PRECONDITION(aChild->GetParent() == aContainer,
1098 : "Wrong container");
1099 :
1100 46 : nsIContent* parent = aContainer;
1101 46 : if (aContainer->IsActiveChildrenElement()) {
1102 0 : if (static_cast<XBLChildrenElement*>(aContainer)->
1103 : HasInsertedChildren()) {
1104 0 : return nullptr;
1105 : }
1106 0 : parent = aContainer->GetParent();
1107 : }
1108 :
1109 114 : while (parent) {
1110 80 : nsXBLBinding* binding = GetBindingWithContent(parent);
1111 80 : if (!binding) {
1112 39 : break;
1113 : }
1114 :
1115 41 : XBLChildrenElement* point = binding->FindInsertionPointFor(aChild);
1116 41 : if (!point) {
1117 0 : return nullptr;
1118 : }
1119 :
1120 41 : nsIContent* newParent = point->GetParent();
1121 41 : if (newParent == parent) {
1122 7 : break;
1123 : }
1124 34 : parent = newParent;
1125 : }
1126 :
1127 46 : return parent;
1128 : }
1129 :
1130 : nsIContent*
1131 41 : nsBindingManager::FindNestedSingleInsertionPoint(nsIContent* aContainer,
1132 : bool* aMulti)
1133 : {
1134 41 : *aMulti = false;
1135 :
1136 41 : nsIContent* parent = aContainer;
1137 41 : if (aContainer->IsActiveChildrenElement()) {
1138 0 : if (static_cast<XBLChildrenElement*>(aContainer)->
1139 : HasInsertedChildren()) {
1140 0 : return nullptr;
1141 : }
1142 0 : parent = aContainer->GetParent();
1143 : }
1144 :
1145 41 : while(parent) {
1146 41 : nsXBLBinding* binding = GetBindingWithContent(parent);
1147 41 : if (!binding) {
1148 36 : break;
1149 : }
1150 :
1151 5 : if (binding->HasFilteredInsertionPoints()) {
1152 4 : *aMulti = true;
1153 4 : return nullptr;
1154 : }
1155 :
1156 1 : XBLChildrenElement* point = binding->GetDefaultInsertionPoint();
1157 1 : if (!point) {
1158 0 : return nullptr;
1159 : }
1160 :
1161 1 : nsIContent* newParent = point->GetParent();
1162 1 : if (newParent == parent) {
1163 1 : break;
1164 : }
1165 0 : parent = newParent;
1166 : }
1167 :
1168 37 : return parent;
1169 : }
|