1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* ***** BEGIN LICENSE BLOCK *****
3
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
5
* The contents of this file are subject to the Netscape Public License
6
* Version 1.1 (the "License"); you may not use this file except in
7
* compliance with the License. You may obtain a copy of the License at
8
* http://www.mozilla.org/NPL/
10
* Software distributed under the License is distributed on an "AS IS" basis,
11
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12
* for the specific language governing rights and limitations under the
15
* The Original Code is Mozilla Communicator client code.
17
* The Initial Developer of the Original Code is
18
* Netscape Communications Corporation.
19
* Portions created by the Initial Developer are Copyright (C) 1998
20
* the Initial Developer. All Rights Reserved.
22
* Original Author: David W. Hyatt (hyatt@netscape.com)
25
* Brendan Eich (brendan@mozilla.org)
26
* Scott MacGregor (mscott@netscape.com)
29
* Alternatively, the contents of this file may be used under the terms of
30
* either the GNU General Public License Version 2 or later (the "GPL"), or
31
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32
* in which case the provisions of the GPL or the LGPL are applicable instead
33
* of those above. If you wish to allow use of your version of this file only
34
* under the terms of either the GPL or the LGPL, and not to allow others to
35
* use your version of this file under the terms of the NPL, indicate your
36
* decision by deleting the provisions above and replace them with the notice
37
* and other provisions required by the GPL or the LGPL. If you do not delete
38
* the provisions above, a recipient may use your version of this file under
39
* the terms of any one of the NPL, the GPL or the LGPL.
41
* ***** END LICENSE BLOCK ***** */
45
#include "nsIXBLBinding.h"
46
#include "nsIXBLDocumentInfo.h"
47
#include "nsIInputStream.h"
48
#include "nsINameSpaceManager.h"
49
#include "nsHashtable.h"
52
#include "nsIDOMEventReceiver.h"
53
#include "nsIChannel.h"
54
#include "nsXPIDLString.h"
55
#include "nsReadableUtils.h"
56
#include "nsIParser.h"
57
#include "nsParserCIID.h"
58
#include "nsNetUtil.h"
60
#include "nsIContent.h"
61
#include "nsIDocument.h"
63
#include "nsIXULDocument.h"
65
#include "nsIXMLContentSink.h"
66
#include "nsContentCID.h"
67
#include "nsXMLDocument.h"
68
#include "nsIDOMElement.h"
69
#include "nsIDOMText.h"
70
#include "nsSupportsArray.h"
71
#include "nsINameSpace.h"
73
#include "nsXBLService.h"
74
#include "nsXBLInsertionPoint.h"
75
#include "nsIXPConnect.h"
76
#include "nsIScriptGlobalObjectOwner.h"
77
#include "nsIScriptContext.h"
81
#include "nsIEventListenerManager.h"
82
#include "nsIDOMMouseListener.h"
83
#include "nsIDOMMouseMotionListener.h"
84
#include "nsIDOMLoadListener.h"
85
#include "nsIDOMFocusListener.h"
86
#include "nsIDOMPaintListener.h"
87
#include "nsIDOMKeyListener.h"
88
#include "nsIDOMFormListener.h"
89
#include "nsIDOMXULListener.h"
90
#include "nsIDOMDragListener.h"
91
#include "nsIDOMMutationListener.h"
92
#include "nsIDOMContextMenuListener.h"
93
#include "nsIDOMEventGroup.h"
95
#include "nsXBLAtoms.h"
96
#include "nsXULAtoms.h"
98
#include "nsIDOMAttr.h"
99
#include "nsIDOMNamedNodeMap.h"
101
#include "nsXBLPrototypeHandler.h"
103
#include "nsXBLPrototypeBinding.h"
104
#include "nsXBLBinding.h"
110
/***********************************************************************/
112
// The JS class for XBLBinding
114
PR_STATIC_CALLBACK(void)
115
XBLFinalize(JSContext *cx, JSObject *obj)
117
nsXBLJSClass* c = NS_STATIC_CAST(nsXBLJSClass*, ::JS_GetClass(cx, obj));
121
nsXBLJSClass::nsXBLJSClass(const nsAFlatCString& aClassName)
123
memset(this, 0, sizeof(nsXBLJSClass));
124
next = prev = NS_STATIC_CAST(JSCList*, this);
125
name = ToNewCString(aClassName);
126
addProperty = delProperty = setProperty = getProperty = ::JS_PropertyStub;
127
enumerate = ::JS_EnumerateStub;
128
resolve = ::JS_ResolveStub;
129
convert = ::JS_ConvertStub;
130
finalize = XBLFinalize;
134
nsXBLJSClass::Destroy()
136
NS_ASSERTION(next == prev && prev == NS_STATIC_CAST(JSCList*, this),
137
"referenced nsXBLJSClass is on LRU list already!?");
139
if (nsXBLService::gClassTable) {
140
nsCStringKey key(name);
141
(nsXBLService::gClassTable)->Remove(&key);
144
if (nsXBLService::gClassLRUListLength >= nsXBLService::gClassLRUListQuota) {
145
// Over LRU list quota, just unhash and delete this class.
148
// Put this most-recently-used class on end of the LRU-sorted freelist.
149
JSCList* mru = NS_STATIC_CAST(JSCList*, this);
150
JS_APPEND_LINK(mru, &nsXBLService::gClassLRUList);
151
nsXBLService::gClassLRUListLength++;
157
// Implementation /////////////////////////////////////////////////////////////////
159
// Implement our nsISupports methods
160
NS_IMPL_ISUPPORTS1(nsXBLBinding, nsIXBLBinding)
162
// Constructors/Destructors
163
nsXBLBinding::nsXBLBinding(nsXBLPrototypeBinding* aBinding)
164
: mPrototypeBinding(aBinding),
165
mInsertionPointTable(nsnull),
166
mIsStyleBinding(PR_TRUE),
167
mMarkedForDeath(PR_FALSE)
169
NS_ASSERTION(mPrototypeBinding, "Must have a prototype binding!");
170
// Grab a ref to the document info so the prototype binding won't die
171
NS_ADDREF(mPrototypeBinding->XBLDocumentInfo());
175
nsXBLBinding::~nsXBLBinding(void)
177
delete mInsertionPointTable;
178
nsIXBLDocumentInfo* info = mPrototypeBinding->XBLDocumentInfo();
182
// nsIXBLBinding Interface ////////////////////////////////////////////////////////////////
185
nsXBLBinding::GetBaseBinding(nsIXBLBinding** aResult)
187
*aResult = mNextBinding;
188
NS_IF_ADDREF(*aResult);
193
nsXBLBinding::SetBaseBinding(nsIXBLBinding* aBinding)
196
NS_ERROR("Base XBL binding is already defined!");
200
mNextBinding = aBinding; // Comptr handles rel/add
205
nsXBLBinding::GetAnonymousContent(nsIContent** aResult)
208
NS_IF_ADDREF(*aResult);
213
nsXBLBinding::InstallAnonymousContent(nsIContent* aAnonParent, nsIContent* aElement)
215
// We need to ensure two things.
216
// (1) The anonymous content should be fooled into thinking it's in the bound
217
// element's document.
218
nsCOMPtr<nsIDocument> doc = aElement->GetDocument();
220
aAnonParent->SetDocument(doc, PR_TRUE, AllowScripts());
222
// (2) The children's parent back pointer should not be to this synthetic root
223
// but should instead point to the enclosing parent element.
224
PRUint32 childCount = aAnonParent->GetChildCount();
225
for (PRUint32 i = 0; i < childCount; i++) {
226
nsIContent *child = aAnonParent->GetChildAt(i);
227
child->SetParent(aElement);
228
child->SetBindingParent(mBoundElement);
231
// To make XUL templates work (and other goodies that happen when
232
// an element is added to a XUL document), we need to notify the
233
// XUL document using its special API.
234
nsCOMPtr<nsIXULDocument> xuldoc(do_QueryInterface(doc));
236
xuldoc->AddSubtreeToDocument(child);
242
nsXBLBinding::SetAnonymousContent(nsIContent* aParent)
244
// First cache the element.
247
InstallAnonymousContent(mContent, mBoundElement);
252
nsXBLBinding::GetPrototypeBinding(nsXBLPrototypeBinding** aResult)
254
*aResult = mPrototypeBinding;
259
nsXBLBinding::SetPrototypeBinding(nsXBLPrototypeBinding* aProtoBinding)
261
mPrototypeBinding = aProtoBinding;
266
nsXBLBinding::GetBindingElement(nsIContent** aResult)
268
*aResult = mPrototypeBinding->GetBindingElement().get();
273
nsXBLBinding::SetBindingElement(nsIContent* aElement)
275
mPrototypeBinding->SetBindingElement(aElement);
280
nsXBLBinding::GetBoundElement(nsIContent** aResult)
282
*aResult = mBoundElement;
283
NS_IF_ADDREF(*aResult);
288
nsXBLBinding::SetBoundElement(nsIContent* aElement)
290
mBoundElement = aElement;
292
mNextBinding->SetBoundElement(aElement);
297
nsXBLBinding::GetFirstBindingWithConstructor(nsIXBLBinding** aResult)
301
nsXBLPrototypeHandler* constructor = mPrototypeBinding->GetConstructor();
306
else if (mNextBinding)
307
return mNextBinding->GetFirstBindingWithConstructor(aResult);
313
nsXBLBinding::HasStyleSheets(PRBool* aResolveStyle)
315
// Find out if we need to re-resolve style. We'll need to do this
316
// if we have additional stylesheets in our binding document.
317
if (mPrototypeBinding->HasStyleSheets()) {
318
*aResolveStyle = PR_TRUE;
323
return mNextBinding->HasStyleSheets(aResolveStyle);
328
nsXBLBinding* mBinding;
330
EnumData(nsXBLBinding* aBinding)
335
struct ContentListData : public EnumData {
336
nsIBindingManager* mBindingManager;
338
ContentListData(nsXBLBinding* aBinding, nsIBindingManager* aManager)
339
:EnumData(aBinding), mBindingManager(aManager)
345
PR_STATIC_CALLBACK(PRBool)
346
BuildContentLists(nsHashKey* aKey, void* aData, void* aClosure)
348
ContentListData* data = (ContentListData*)aClosure;
349
nsIBindingManager* bm = data->mBindingManager;
350
nsXBLBinding* binding = data->mBinding;
352
nsCOMPtr<nsIContent> boundElement;
353
binding->GetBoundElement(getter_AddRefs(boundElement));
355
nsVoidArray* arr = NS_STATIC_CAST(nsVoidArray*, aData);
356
PRInt32 count = arr->Count();
361
// XXX Could this array just be altered in place and passed directly to
362
// SetContentListFor? We'd save space if we could pull this off.
363
nsVoidArray* contentList = new nsVoidArray();
365
// Figure out the relevant content node.
366
nsXBLInsertionPoint* currPoint = NS_STATIC_CAST(nsXBLInsertionPoint*, arr->ElementAt(0));
367
nsCOMPtr<nsIContent> parent = currPoint->GetInsertionParent();
368
PRInt32 currIndex = currPoint->GetInsertionIndex();
370
nsCOMPtr<nsIDOMNodeList> nodeList;
371
if (parent == boundElement) {
372
// We are altering anonymous nodes to accommodate insertion points.
373
binding->GetAnonymousNodes(getter_AddRefs(nodeList));
376
// We are altering the explicit content list of a node to accommodate insertion points.
377
nsCOMPtr<nsIDOMNode> node(do_QueryInterface(parent));
378
node->GetChildNodes(getter_AddRefs(nodeList));
381
nsXBLInsertionPoint* pseudoPoint = nsnull;
383
nodeList->GetLength(&childCount);
386
for (PRUint32 i = 0; i < childCount; i++) {
387
nsCOMPtr<nsIDOMNode> node;
388
nodeList->Item(i, getter_AddRefs(node));
389
nsCOMPtr<nsIContent> child(do_QueryInterface(node));
390
if (((PRInt32)i) == currIndex) {
391
// Add the currPoint to the supports array.
392
contentList->AppendElement(currPoint);
394
// Get the next real insertion point and update our currIndex.
397
currPoint = NS_STATIC_CAST(nsXBLInsertionPoint*, arr->ElementAt(j));
398
currIndex = currPoint->GetInsertionIndex();
401
// Null out our current pseudo-point.
402
pseudoPoint = nsnull;
406
pseudoPoint = new nsXBLInsertionPoint(parent, (PRUint32) -1, nsnull);
407
contentList->AppendElement(pseudoPoint);
410
pseudoPoint->AddChild(child);
413
// Add in all the remaining insertion points.
414
for ( ; j < count; j++) {
415
currPoint = NS_STATIC_CAST(nsXBLInsertionPoint*, arr->ElementAt(j));
416
contentList->AppendElement(currPoint);
419
// Now set the content list using the binding manager,
420
// If the bound element is the parent, then we alter the anonymous node list
421
// instead. This allows us to always maintain two distinct lists should
422
// insertion points be nested into an inner binding.
423
if (parent == boundElement)
424
bm->SetAnonymousNodesFor(parent, contentList);
426
bm->SetContentListFor(parent, contentList);
430
PR_STATIC_CALLBACK(PRBool)
431
RealizeDefaultContent(nsHashKey* aKey, void* aData, void* aClosure)
433
ContentListData* data = (ContentListData*)aClosure;
434
nsIBindingManager* bm = data->mBindingManager;
435
nsXBLBinding* binding = data->mBinding;
437
nsCOMPtr<nsIContent> boundElement;
438
binding->GetBoundElement(getter_AddRefs(boundElement));
440
nsVoidArray* arr = (nsVoidArray*)aData;
441
PRInt32 count = arr->Count();
443
for (PRInt32 i = 0; i < count; i++) {
444
nsXBLInsertionPoint* currPoint = NS_STATIC_CAST(nsXBLInsertionPoint*, arr->ElementAt(i));
445
PRInt32 insCount = currPoint->ChildCount();
448
nsCOMPtr<nsIContent> defContent = currPoint->GetDefaultContentTemplate();
450
// We need to take this template and use it to realize the
451
// actual default content (through cloning).
452
// Clone this insertion point element.
453
nsCOMPtr<nsIDOMElement> elt(do_QueryInterface(defContent));
454
nsCOMPtr<nsIDOMNode> clonedNode;
455
elt->CloneNode(PR_TRUE, getter_AddRefs(clonedNode));
457
nsCOMPtr<nsIContent> insParent = currPoint->GetInsertionParent();
459
// Now that we have the cloned content, install the default content as
460
// if it were additional anonymous content.
461
nsCOMPtr<nsIContent> clonedContent(do_QueryInterface(clonedNode));
462
binding->InstallAnonymousContent(clonedContent, insParent);
464
// Cache the clone so that it can be properly destroyed if/when our
465
// other anonymous content is destroyed.
466
currPoint->SetDefaultContent(clonedContent);
468
// Now make sure the kids of the clone are added to the insertion point as
470
PRUint32 cloneKidCount = clonedContent->GetChildCount();
471
for (PRUint32 k = 0; k < cloneKidCount; k++) {
472
nsIContent *cloneChild = clonedContent->GetChildAt(k);
473
bm->SetInsertionParent(cloneChild, insParent);
474
currPoint->AddChild(cloneChild);
483
PR_STATIC_CALLBACK(PRBool)
484
ChangeDocumentForDefaultContent(nsHashKey* aKey, void* aData, void* aClosure)
486
nsVoidArray* arr = NS_STATIC_CAST(nsVoidArray*, aData);
487
PRInt32 count = arr->Count();
489
for (PRInt32 i = 0; i < count; i++) {
490
nsXBLInsertionPoint* currPoint = NS_STATIC_CAST(nsXBLInsertionPoint*, arr->ElementAt(i));
491
nsCOMPtr<nsIContent> defContent = currPoint->GetDefaultContent();
494
defContent->SetDocument(nsnull, PR_TRUE, PR_TRUE);
501
nsXBLBinding::GenerateAnonymousContent()
503
// Fetch the content element for this binding.
504
nsCOMPtr<nsIContent> content;
505
GetImmediateChild(nsXBLAtoms::content, getter_AddRefs(content));
508
// We have no anonymous content.
510
return mNextBinding->GenerateAnonymousContent();
514
// Find out if we're really building kids or if we're just
515
// using the attribute-setting shorthand hack.
516
PRUint32 contentCount = content->GetChildCount();
518
// Plan to build the content by default.
519
PRBool hasContent = (contentCount > 0);
520
PRBool hasInsertionPoints = mPrototypeBinding->HasInsertionPoints();
523
// See if there's an includes attribute.
524
nsAutoString includes;
525
content->GetAttr(kNameSpaceID_None, nsXBLAtoms::includes, includes);
526
if (!includes.IsEmpty()) {
528
mPrototypeBinding->GetID(id);
529
nsCAutoString message("An XBL Binding with an id of ");
531
message += " and found in the file ";
533
mPrototypeBinding->DocURI()->GetSpec(uri);
535
message += " is still using the deprecated\n<content includes=\"\"> syntax! Use <children> instead!\n";
536
NS_WARNING(message.get());
540
if (hasContent || hasInsertionPoints) {
541
nsIDocument* doc = mBoundElement->GetDocument();
543
// XXX doc will be null if we're in the midst of paint suppression.
547
nsIBindingManager *bindingManager = doc->GetBindingManager();
549
nsCOMPtr<nsIDOMNodeList> children;
550
bindingManager->GetContentListFor(mBoundElement, getter_AddRefs(children));
552
nsCOMPtr<nsIDOMNode> node;
553
nsCOMPtr<nsIContent> childContent;
555
children->GetLength(&length);
556
if (length > 0 && !hasInsertionPoints) {
557
// There are children being placed underneath us, but we have no specified
558
// insertion points, and therefore no place to put the kids. Don't generate
559
// anonymous content.
560
// Special case template and observes.
561
for (PRUint32 i = 0; i < length; i++) {
562
children->Item(i, getter_AddRefs(node));
563
childContent = do_QueryInterface(node);
565
nsINodeInfo *ni = childContent->GetNodeInfo();
567
if (!ni || (!ni->Equals(nsXULAtoms::observes, kNameSpaceID_XUL) &&
568
!ni->Equals(nsXULAtoms::templateAtom, kNameSpaceID_XUL))) {
569
hasContent = PR_FALSE;
575
if (hasContent || hasInsertionPoints) {
576
nsCOMPtr<nsIContent> clonedContent;
577
nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(content));
579
nsCOMPtr<nsIDOMNode> clonedNode;
580
domElement->CloneNode(PR_TRUE, getter_AddRefs(clonedNode));
582
clonedContent = do_QueryInterface(clonedNode);
583
SetAnonymousContent(clonedContent);
585
if (hasInsertionPoints) {
586
// Now check and see if we have a single insertion point
587
// or multiple insertion points.
589
// Enumerate the prototype binding's insertion table to build
590
// our table of instantiated insertion points.
591
mPrototypeBinding->InstantiateInsertionPoints(this);
593
// We now have our insertion point table constructed. We
594
// enumerate this table. For each array of insertion points
595
// bundled under the same content node, we generate a content
596
// list. In the case of the bound element, we generate a new
597
// anonymous node list that will be used in place of the binding's
598
// cached anonymous node list.
599
ContentListData data(this, bindingManager);
600
mInsertionPointTable->Enumerate(BuildContentLists, &data);
602
// We need to place the children
603
// at their respective insertion points.
604
nsCOMPtr<nsIContent> singlePoint;
606
PRBool multiplePoints = PR_FALSE;
607
nsCOMPtr<nsIContent> singleDefaultContent;
608
GetSingleInsertionPoint(getter_AddRefs(singlePoint), &index,
609
&multiplePoints, getter_AddRefs(singleDefaultContent));
612
if (multiplePoints) {
613
// We must walk the entire content list in order to determine where
614
// each child belongs.
615
children->GetLength(&length);
616
for (PRUint32 i = 0; i < length; i++) {
617
children->Item(i, getter_AddRefs(node));
618
childContent = do_QueryInterface(node);
620
// Now determine the insertion point in the prototype table.
621
nsCOMPtr<nsIContent> point;
623
nsCOMPtr<nsIContent> defContent;
624
GetInsertionPoint(childContent, getter_AddRefs(point), &index, getter_AddRefs(defContent));
625
bindingManager->SetInsertionParent(childContent, point);
627
// Find the correct nsIXBLInsertion point in our table.
629
GetInsertionPointsFor(point, &arr);
630
nsXBLInsertionPoint* insertionPoint = nsnull;
631
PRInt32 arrCount = arr->Count();
632
for (PRInt32 j = 0; j < arrCount; j++) {
633
insertionPoint = NS_STATIC_CAST(nsXBLInsertionPoint*, arr->ElementAt(j));
634
if (insertionPoint->Matches(point, index))
636
insertionPoint = nsnull;
640
insertionPoint->AddChild(childContent);
642
// We were unable to place this child. All anonymous content
643
// should be thrown out. Special-case template and observes.
645
nsINodeInfo *ni = childContent->GetNodeInfo();
648
(!ni->Equals(nsXULAtoms::observes, kNameSpaceID_XUL) &&
649
!ni->Equals(nsXULAtoms::templateAtom, kNameSpaceID_XUL))) {
650
// Kill all anonymous content.
652
bindingManager->SetContentListFor(mBoundElement, nsnull);
653
bindingManager->SetAnonymousNodesFor(mBoundElement, nsnull);
660
// All of our children are shunted to this single insertion point.
662
GetInsertionPointsFor(singlePoint, &arr);
663
nsXBLInsertionPoint* insertionPoint = NS_STATIC_CAST(nsXBLInsertionPoint*, arr->ElementAt(0));
665
nsCOMPtr<nsIDOMNode> node;
666
nsCOMPtr<nsIContent> content;
668
children->GetLength(&length);
670
for (PRUint32 i = 0; i < length; i++) {
671
children->Item(i, getter_AddRefs(node));
672
content = do_QueryInterface(node);
673
bindingManager->SetInsertionParent(content, singlePoint);
674
insertionPoint->AddChild(content);
679
// Now that all of our children have been added, we need to walk all of our
680
// nsIXBLInsertion points to see if any of them have default content that
681
// needs to be built.
682
mInsertionPointTable->Enumerate(RealizeDefaultContent, &data);
686
mPrototypeBinding->SetInitialAttributes(mBoundElement, mContent);
689
// Always check the content element for potential attributes.
690
// This shorthand hack always happens, even when we didn't
691
// build anonymous content.
692
PRUint32 length = content->GetAttrCount();
695
nsCOMPtr<nsIAtom> name;
696
nsCOMPtr<nsIAtom> prefix;
698
for (PRUint32 i = 0; i < length; ++i) {
699
content->GetAttrNameAt(i, &namespaceID, getter_AddRefs(name),
700
getter_AddRefs(prefix));
702
if (name != nsXBLAtoms::includes) {
704
mBoundElement->GetAttr(namespaceID, name, value);
705
if (value.IsEmpty()) {
707
content->GetAttr(namespaceID, name, value2);
708
mBoundElement->SetAttr(namespaceID, name, value2, PR_FALSE);
712
// Conserve space by wiping the attributes off the clone.
714
mContent->UnsetAttr(namespaceID, name, PR_FALSE);
721
nsXBLBinding::InstallEventHandlers()
723
// Don't install handlers if scripts aren't allowed.
724
if (AllowScripts()) {
725
// Fetch the handlers prototypes for this binding.
726
nsXBLPrototypeHandler* handlerChain = mPrototypeBinding->GetPrototypeHandlers();
729
nsCOMPtr<nsIDOMEventReceiver> receiver = do_QueryInterface(mBoundElement);
730
nsCOMPtr<nsIDOM3EventTarget> target = do_QueryInterface(receiver);
731
nsCOMPtr<nsIDOMEventGroup> systemEventGroup;
733
nsXBLPrototypeHandler* curr;
734
for (curr = handlerChain; curr; curr = curr->GetNextHandler()) {
735
// Fetch the event type.
736
nsCOMPtr<nsIAtom> eventAtom = curr->GetEventName();
738
eventAtom == nsXBLAtoms::keyup ||
739
eventAtom == nsXBLAtoms::keydown ||
740
eventAtom == nsXBLAtoms::keypress)
744
eventAtom->ToString(type);
746
// Figure out if we're using capturing or not.
747
PRBool useCapture = (curr->GetPhase() == NS_PHASE_CAPTURING);
749
// If this is a command, add it in the system event group, otherwise
750
// add it to the standard event group.
752
// This is a weak ref. systemEventGroup above is already a
753
// strong ref, so we are guaranteed it will not go away.
754
nsIDOMEventGroup* eventGroup = nsnull;
755
if (curr->GetType() & NS_HANDLER_TYPE_XBL_COMMAND) {
756
if (!systemEventGroup)
757
receiver->GetSystemEventGroup(getter_AddRefs(systemEventGroup));
758
eventGroup = systemEventGroup;
761
nsXBLEventHandler* handler = curr->GetEventHandler();
763
target->AddGroupedEventListener(type, handler, useCapture,
768
const nsCOMArray<nsXBLKeyEventHandler>* keyHandlers =
769
mPrototypeBinding->GetKeyEventHandlers();
771
for (i = 0; i < keyHandlers->Count(); ++i) {
772
nsXBLKeyEventHandler* handler = keyHandlers->ObjectAt(i);
775
handler->GetEventName(type);
777
// Figure out if we're using capturing or not.
778
PRBool useCapture = (handler->GetPhase() == NS_PHASE_CAPTURING);
780
// If this is a command, add it in the system event group, otherwise
781
// add it to the standard event group.
783
// This is a weak ref. systemEventGroup above is already a
784
// strong ref, so we are guaranteed it will not go away.
785
nsIDOMEventGroup* eventGroup = nsnull;
786
if (handler->GetType() & NS_HANDLER_TYPE_XBL_COMMAND) {
787
if (!systemEventGroup)
788
receiver->GetSystemEventGroup(getter_AddRefs(systemEventGroup));
789
eventGroup = systemEventGroup;
792
target->AddGroupedEventListener(type, handler, useCapture, eventGroup);
798
mNextBinding->InstallEventHandlers();
804
nsXBLBinding::InstallImplementation()
806
// Always install the base class properties first, so that
807
// derived classes can reference the base class properties.
810
mNextBinding->InstallImplementation();
812
// iterate through each property in the prototype's list and install the property.
814
mPrototypeBinding->InstallImplementation(mBoundElement);
820
nsXBLBinding::GetBaseTag(PRInt32* aNameSpaceID, nsIAtom** aResult)
822
mPrototypeBinding->GetBaseTag(aNameSpaceID, aResult);
823
if (!*aResult && mNextBinding)
824
return mNextBinding->GetBaseTag(aNameSpaceID, aResult);
829
nsXBLBinding::AttributeChanged(nsIAtom* aAttribute, PRInt32 aNameSpaceID,
830
PRBool aRemoveFlag, PRBool aNotify)
832
// XXX Change if we ever allow multiple bindings in a chain to contribute anonymous content
835
return mNextBinding->AttributeChanged(aAttribute, aNameSpaceID,
836
aRemoveFlag, aNotify);
840
mPrototypeBinding->AttributeChanged(aAttribute, aNameSpaceID, aRemoveFlag,
841
mBoundElement, mContent, aNotify);
846
nsXBLBinding::ExecuteAttachedHandler()
849
mNextBinding->ExecuteAttachedHandler();
851
nsCOMPtr<nsIDOMEventReceiver> rec(do_QueryInterface(mBoundElement));
852
mPrototypeBinding->BindingAttached(rec);
858
nsXBLBinding::ExecuteDetachedHandler()
860
nsCOMPtr<nsIDOMEventReceiver> rec(do_QueryInterface(mBoundElement));
861
mPrototypeBinding->BindingDetached(rec);
864
mNextBinding->ExecuteDetachedHandler();
870
nsXBLBinding::UnhookEventHandlers()
872
nsXBLPrototypeHandler* handlerChain = mPrototypeBinding->GetPrototypeHandlers();
875
nsCOMPtr<nsIDOMEventReceiver> receiver = do_QueryInterface(mBoundElement);
876
nsCOMPtr<nsIDOM3EventTarget> target = do_QueryInterface(receiver);
877
nsCOMPtr<nsIDOMEventGroup> systemEventGroup;
879
nsXBLPrototypeHandler* curr;
880
for (curr = handlerChain; curr; curr = curr->GetNextHandler()) {
881
nsXBLEventHandler* handler = curr->GetCachedEventHandler();
883
nsCOMPtr<nsIAtom> eventAtom = curr->GetEventName();
885
eventAtom == nsXBLAtoms::keyup ||
886
eventAtom == nsXBLAtoms::keydown ||
887
eventAtom == nsXBLAtoms::keypress)
891
eventAtom->ToString(type);
893
// Figure out if we're using capturing or not.
894
PRBool useCapture = (curr->GetPhase() == NS_PHASE_CAPTURING);
896
// If this is a command, remove it from the system event group, otherwise
897
// remove it from the standard event group.
899
// This is a weak ref. systemEventGroup above is already a
900
// strong ref, so we are guaranteed it will not go away.
901
nsIDOMEventGroup* eventGroup = nsnull;
902
if (curr->GetType() & NS_HANDLER_TYPE_XBL_COMMAND) {
903
if (!systemEventGroup)
904
receiver->GetSystemEventGroup(getter_AddRefs(systemEventGroup));
905
eventGroup = systemEventGroup;
908
target->RemoveGroupedEventListener(type, handler, useCapture,
913
const nsCOMArray<nsXBLKeyEventHandler>* keyHandlers =
914
mPrototypeBinding->GetKeyEventHandlers();
916
for (i = 0; i < keyHandlers->Count(); ++i) {
917
nsXBLKeyEventHandler* handler = keyHandlers->ObjectAt(i);
920
handler->GetEventName(type);
922
// Figure out if we're using capturing or not.
923
PRBool useCapture = (handler->GetPhase() == NS_PHASE_CAPTURING);
925
// If this is a command, remove it from the system event group, otherwise
926
// remove it from the standard event group.
928
// This is a weak ref. systemEventGroup above is already a
929
// strong ref, so we are guaranteed it will not go away.
930
nsIDOMEventGroup* eventGroup = nsnull;
931
if (handler->GetType() & NS_HANDLER_TYPE_XBL_COMMAND) {
932
if (!systemEventGroup)
933
receiver->GetSystemEventGroup(getter_AddRefs(systemEventGroup));
934
eventGroup = systemEventGroup;
937
target->RemoveGroupedEventListener(type, handler, useCapture,
946
nsXBLBinding::ChangeDocument(nsIDocument* aOldDocument, nsIDocument* aNewDocument)
948
if (aOldDocument != aNewDocument) {
950
mNextBinding->ChangeDocument(aOldDocument, aNewDocument);
952
// Only style bindings get their prototypes unhooked.
953
if (mIsStyleBinding) {
954
// Now the binding dies. Unhook our prototypes.
955
nsCOMPtr<nsIContent> interfaceElement;
956
GetImmediateChild(nsXBLAtoms::implementation, getter_AddRefs(interfaceElement));
958
if (interfaceElement) {
959
nsIScriptGlobalObject *global = aOldDocument->GetScriptGlobalObject();
961
nsIScriptContext *context = global->GetContext();
963
JSContext *jscontext = (JSContext *)context->GetNativeContext();
966
nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(),
968
NS_ENSURE_SUCCESS(rv, rv);
970
nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
972
rv = xpc->WrapNative(jscontext, ::JS_GetGlobalObject(jscontext),
973
mBoundElement, NS_GET_IID(nsISupports),
974
getter_AddRefs(wrapper));
975
NS_ENSURE_SUCCESS(rv, rv);
977
JSObject* scriptObject = nsnull;
978
rv = wrapper->GetJSObject(&scriptObject);
979
NS_ENSURE_SUCCESS(rv, rv);
981
// XXX Stay in sync! What if a layered binding has an
984
// XXX Sanity check to make sure our class name matches
985
// Pull ourselves out of the proto chain.
986
JSObject* ourProto = ::JS_GetPrototype(jscontext, scriptObject);
989
JSObject* grandProto = ::JS_GetPrototype(jscontext, ourProto);
990
::JS_SetPrototype(jscontext, scriptObject, grandProto);
993
// Don't remove the reference from the document to the
994
// wrapper here since it'll be removed by the element
995
// itself when that's taken out of the document.
1001
// Update the anonymous content.
1002
nsCOMPtr<nsIContent> anonymous;
1003
GetAnonymousContent(getter_AddRefs(anonymous));
1005
// Also kill the default content within all our insertion points.
1006
if (mInsertionPointTable)
1007
mInsertionPointTable->Enumerate(ChangeDocumentForDefaultContent,
1011
nsCOMPtr<nsIXULDocument> xuldoc(do_QueryInterface(aOldDocument));
1014
anonymous->SetDocument(nsnull, PR_TRUE, PR_TRUE); // Kill it.
1017
// To make XUL templates work (and other XUL-specific stuff),
1018
// we'll need to notify it using its add & remove APIs. Grab the
1021
xuldoc->RemoveSubtreeFromDocument(anonymous);
1029
NS_IMETHODIMP_(nsIURI*)
1030
nsXBLBinding::BindingURI() const
1032
return mPrototypeBinding->BindingURI();
1035
NS_IMETHODIMP_(nsIURI*)
1036
nsXBLBinding::DocURI() const
1038
return mPrototypeBinding->DocURI();
1042
nsXBLBinding::GetID(nsACString& aResult) const
1044
return mPrototypeBinding->GetID(aResult);
1048
nsXBLBinding::InheritsStyle(PRBool* aResult)
1050
// XXX Will have to change if we ever allow multiple bindings to contribute anonymous content.
1051
// Most derived binding with anonymous content determines style inheritance for now.
1053
// XXX What about bindings with <content> but no kids, e.g., my treecell-text binding?
1055
*aResult = mPrototypeBinding->InheritsStyle();
1060
return mNextBinding->InheritsStyle(aResult);
1066
nsXBLBinding::WalkRules(nsIStyleRuleProcessor::EnumFunc aFunc, void* aData)
1068
nsresult rv = NS_OK;
1070
rv = mNextBinding->WalkRules(aFunc, aData);
1075
nsCOMArray<nsIStyleRuleProcessor> *rules = mPrototypeBinding->GetRuleProcessors();
1077
rules->EnumerateForwards(aFunc, aData);
1082
// Internal helper methods ////////////////////////////////////////////////////////////////
1086
nsXBLBinding::DoInitJSClass(JSContext *cx, JSObject *global, JSObject *obj,
1087
const nsAFlatCString& aClassName,
1088
void **aClassObject)
1090
// First ensure our JS class is initialized.
1094
nsCAutoString className(aClassName);
1095
// Retrieve the current prototype of obj.
1096
JSObject* parent_proto = ::JS_GetPrototype(cx, obj);
1098
// We need to create a unique classname based on aClassName and
1099
// parent_proto. Append a space (an invalid URI character) to ensure that
1100
// we don't have accidental collisions with the case when parent_proto is
1101
// null and aClassName ends in some bizarre numbers (yeah, it's unlikely).
1102
jsid parent_proto_id;
1103
if (!::JS_GetObjectId(cx, parent_proto, &parent_proto_id)) {
1105
return NS_ERROR_OUT_OF_MEMORY;
1108
// One space, maybe "0x", at most 16 chars (on a 64-bit system) of long,
1109
// and a null-terminator (which PR_snprintf ensures is there even if the
1110
// string representation of what we're printing does not fit in the buffer
1113
PR_snprintf(buf, sizeof(buf), " %lx", parent_proto_id);
1114
className.Append(buf);
1117
if ((!::JS_LookupPropertyWithFlags(cx, global, className.get(),
1118
JSRESOLVE_CLASSNAME,
1120
JSVAL_IS_PRIMITIVE(val)) {
1121
// We need to initialize the class.
1125
nsCStringKey key(className);
1126
classObject = (nsXBLService::gClassTable)->Get(&key);
1129
c = NS_STATIC_CAST(nsXBLJSClass*, classObject);
1131
// If c is on the LRU list (i.e., not linked to itself), remove it now!
1132
JSCList* link = NS_STATIC_CAST(JSCList*, c);
1133
if (c->next != link) {
1134
JS_REMOVE_AND_INIT_LINK(link);
1135
nsXBLService::gClassLRUListLength--;
1138
if (JS_CLIST_IS_EMPTY(&nsXBLService::gClassLRUList)) {
1139
// We need to create a struct for this class.
1140
c = new nsXBLJSClass(className);
1143
return NS_ERROR_OUT_OF_MEMORY;
1145
// Pull the least recently used class struct off the list.
1146
JSCList* lru = (nsXBLService::gClassLRUList).next;
1147
JS_REMOVE_AND_INIT_LINK(lru);
1148
nsXBLService::gClassLRUListLength--;
1150
// Remove any mapping from the old name to the class struct.
1151
c = NS_STATIC_CAST(nsXBLJSClass*, lru);
1152
nsCStringKey oldKey(c->name);
1153
(nsXBLService::gClassTable)->Remove(&oldKey);
1155
// Change the class name and we're done.
1156
nsMemory::Free((void*) c->name);
1157
c->name = ToNewCString(className);
1160
// Add c to our table.
1161
(nsXBLService::gClassTable)->Put(&key, (void*)c);
1164
// The prototype holds a strong reference to its class struct.
1167
// Make a new object prototyped by parent_proto and parented by global.
1168
proto = ::JS_InitClass(cx, // context
1169
global, // global object
1170
parent_proto, // parent proto
1172
nsnull, // JSNative ctor
1174
nsnull, // proto props
1175
nsnull, // proto funcs
1176
nsnull, // ctor props (static)
1177
nsnull); // ctor funcs (static)
1179
// This will happen if we're OOM or if the security manager
1180
// denies defining the new class...
1182
(nsXBLService::gClassTable)->Remove(&key);
1186
return NS_ERROR_OUT_OF_MEMORY;
1189
*aClassObject = (void*)proto;
1192
proto = JSVAL_TO_OBJECT(val);
1195
// Set the prototype of our object to be the new class.
1196
if (!::JS_SetPrototype(cx, obj, proto)) {
1197
return NS_ERROR_FAILURE;
1205
nsXBLBinding::InitClass(const nsCString& aClassName,
1206
nsIScriptContext* aContext,
1207
nsIDocument* aDocument, void** aScriptObject,
1208
void** aClassObject)
1210
*aClassObject = nsnull;
1211
*aScriptObject = nsnull;
1215
// Obtain the bound element's current script object.
1216
nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
1217
NS_ENSURE_SUCCESS(rv, rv);
1219
JSContext* cx = (JSContext*)aContext->GetNativeContext();
1221
nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
1223
JSObject* global = ::JS_GetGlobalObject(cx);
1225
rv = xpc->WrapNative(cx, global, mBoundElement, NS_GET_IID(nsISupports),
1226
getter_AddRefs(wrapper));
1227
NS_ENSURE_SUCCESS(rv, rv);
1229
JSObject* object = nsnull;
1230
rv = wrapper->GetJSObject(&object);
1231
NS_ENSURE_SUCCESS(rv, rv);
1233
*aScriptObject = object;
1235
// First ensure our JS class is initialized.
1237
rv = DoInitJSClass(cx, global, object, aClassName, aClassObject);
1238
NS_ENSURE_SUCCESS(rv, rv);
1240
// Root mBoundElement so that it doesn't lose it's binding
1241
nsIDocument* doc = mBoundElement->GetDocument();
1244
nsCOMPtr<nsIXPConnectWrappedNative> native_wrapper =
1245
do_QueryInterface(wrapper);
1247
if (native_wrapper) {
1248
doc->AddReference(mBoundElement, native_wrapper);
1256
nsXBLBinding::GetImmediateChild(nsIAtom* aTag, nsIContent** aResult)
1258
nsCOMPtr<nsIContent> binding = mPrototypeBinding->GetBindingElement();
1261
PRUint32 childCount = binding->GetChildCount();
1263
for (PRUint32 i = 0; i < childCount; i++) {
1264
nsIContent *child = binding->GetChildAt(i);
1266
if (aTag == child->Tag()) {
1268
NS_ADDREF(*aResult);
1275
nsXBLBinding::IsInExcludesList(nsIAtom* aTag, const nsString& aList)
1277
nsAutoString element;
1278
aTag->ToString(element);
1280
if (aList == NS_LITERAL_STRING("*"))
1281
return PR_TRUE; // match _everything_!
1283
PRInt32 indx = aList.Find(element, 0);
1285
return PR_FALSE; // not in the list at all
1287
// okay, now make sure it's not a substring snafu; e.g., 'ur'
1288
// found inside of 'blur'.
1290
PRUnichar ch = aList[indx - 1];
1291
if (! nsCRT::IsAsciiSpace(ch) && ch != PRUnichar('|'))
1295
if (indx + element.Length() < aList.Length()) {
1296
PRUnichar ch = aList[indx + element.Length()];
1297
if (! nsCRT::IsAsciiSpace(ch) && ch != PRUnichar('|'))
1305
nsXBLBinding::AddScriptEventListener(nsIContent* aElement, nsIAtom* aName,
1306
const nsString& aValue)
1309
aName->ToString(val);
1311
nsAutoString eventStr(NS_LITERAL_STRING("on"));
1314
nsCOMPtr<nsIAtom> eventName = do_GetAtom(eventStr);
1318
nsCOMPtr<nsIDOMEventReceiver> receiver(do_QueryInterface(aElement));
1322
nsCOMPtr<nsIEventListenerManager> manager;
1323
rv = receiver->GetListenerManager(getter_AddRefs(manager));
1324
if (NS_FAILED(rv)) return rv;
1326
rv = manager->AddScriptEventListener(receiver, eventName, aValue, PR_FALSE);
1332
nsXBLBinding::GetTextData(nsIContent *aParent, nsString& aResult)
1334
aResult.Truncate(0);
1336
PRUint32 textCount = aParent->GetChildCount();
1337
nsAutoString answer;
1338
for (PRUint32 j = 0; j < textCount; j++) {
1340
nsCOMPtr<nsIDOMText> text(do_QueryInterface(aParent->GetChildAt(j)));
1343
text->GetData(data);
1351
nsXBLBinding::AllowScripts()
1354
mPrototypeBinding->GetAllowScripts(&result);
1358
PR_STATIC_CALLBACK(PRBool)
1359
DeleteVoidArray(nsHashKey* aKey, void* aData, void* aClosure)
1361
delete NS_STATIC_CAST(nsVoidArray*, aData);
1366
nsXBLBinding::GetInsertionPointsFor(nsIContent* aParent, nsVoidArray** aResult)
1368
if (!mInsertionPointTable) {
1369
mInsertionPointTable = new nsObjectHashtable(nsnull, nsnull,
1370
DeleteVoidArray, nsnull, 4);
1372
NS_ENSURE_TRUE(mInsertionPointTable, NS_ERROR_OUT_OF_MEMORY);
1375
nsISupportsKey key(aParent);
1376
*aResult = NS_STATIC_CAST(nsVoidArray*, mInsertionPointTable->Get(&key));
1379
*aResult = new nsVoidArray();
1380
NS_ENSURE_TRUE(*aResult, NS_ERROR_OUT_OF_MEMORY);
1381
mInsertionPointTable->Put(&key, *aResult);
1388
nsXBLBinding::GetInsertionPoint(nsIContent* aChild, nsIContent** aResult, PRUint32* aIndex,
1389
nsIContent** aDefaultContent)
1392
*aDefaultContent = nsnull;
1394
mPrototypeBinding->GetInsertionPoint(mBoundElement, mContent, aChild, aResult, aIndex, aDefaultContent);
1395
else if (mNextBinding)
1396
return mNextBinding->GetInsertionPoint(aChild, aResult, aIndex, aDefaultContent);
1401
nsXBLBinding::GetSingleInsertionPoint(nsIContent** aResult, PRUint32* aIndex, PRBool* aMultipleInsertionPoints,
1402
nsIContent** aDefaultContent)
1405
*aDefaultContent = nsnull;
1406
*aMultipleInsertionPoints = PR_FALSE;
1408
mPrototypeBinding->GetSingleInsertionPoint(mBoundElement, mContent, aResult, aIndex,
1409
aMultipleInsertionPoints, aDefaultContent);
1410
else if (mNextBinding)
1411
return mNextBinding->GetSingleInsertionPoint(aResult, aIndex, aMultipleInsertionPoints, aDefaultContent);
1416
nsXBLBinding::GetRootBinding(nsIXBLBinding** aResult)
1419
return mNextBinding->GetRootBinding(aResult);
1427
nsXBLBinding::GetFirstStyleBinding(nsIXBLBinding** aResult)
1429
if (mIsStyleBinding) {
1434
else if (mNextBinding)
1435
return mNextBinding->GetFirstStyleBinding(aResult);
1442
nsXBLBinding::MarkForDeath()
1444
mMarkedForDeath = PR_TRUE;
1445
ExecuteDetachedHandler();
1450
nsXBLBinding::MarkedForDeath(PRBool* aResult)
1452
*aResult = mMarkedForDeath;
1457
nsXBLBinding::ImplementsInterface(REFNSIID aIID, PRBool* aResult)
1459
*aResult = mPrototypeBinding->ImplementsInterface(aIID);
1460
if (!*aResult && mNextBinding)
1461
return mNextBinding->ImplementsInterface(aIID, aResult);
1466
nsXBLBinding::GetAnonymousNodes(nsIDOMNodeList** aResult)
1470
nsCOMPtr<nsIDOMElement> elt(do_QueryInterface(mContent));
1471
return elt->GetChildNodes(aResult);
1473
else if (mNextBinding)
1474
return mNextBinding->GetAnonymousNodes(aResult);
1479
nsXBLBinding::ShouldBuildChildFrames(PRBool* aResult)
1483
*aResult = mPrototypeBinding->ShouldBuildChildFrames();
1484
else if (mNextBinding)
1485
return mNextBinding->ShouldBuildChildFrames(aResult);
1490
// Creation Routine ///////////////////////////////////////////////////////////////////////
1493
NS_NewXBLBinding(nsXBLPrototypeBinding* aBinding, nsIXBLBinding** aResult)
1495
*aResult = new nsXBLBinding(aBinding);
1497
return NS_ERROR_OUT_OF_MEMORY;
1498
NS_ADDREF(*aResult);