1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
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
13 : * License.
14 : *
15 : * The Original Code is Mozilla Communicator client code.
16 : *
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.
21 : *
22 : * Contributor(s):
23 : * Brendan Eich (brendan@mozilla.org)
24 : * Scott MacGregor (mscott@netscape.com)
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "nsCOMPtr.h"
41 : #include "nsIAtom.h"
42 : #include "nsXBLDocumentInfo.h"
43 : #include "nsIInputStream.h"
44 : #include "nsINameSpaceManager.h"
45 : #include "nsHashtable.h"
46 : #include "nsIURI.h"
47 : #include "nsIURL.h"
48 : #include "nsIDOMEventTarget.h"
49 : #include "nsIChannel.h"
50 : #include "nsXPIDLString.h"
51 : #include "nsReadableUtils.h"
52 : #include "nsIParser.h"
53 : #include "nsParserCIID.h"
54 : #include "nsNetUtil.h"
55 : #include "plstr.h"
56 : #include "nsIContent.h"
57 : #include "nsIDocument.h"
58 : #include "nsContentUtils.h"
59 : #ifdef MOZ_XUL
60 : #include "nsIXULDocument.h"
61 : #endif
62 : #include "nsIXMLContentSink.h"
63 : #include "nsContentCID.h"
64 : #include "nsXMLDocument.h"
65 : #include "jsapi.h"
66 : #include "nsXBLService.h"
67 : #include "nsXBLInsertionPoint.h"
68 : #include "nsIXPConnect.h"
69 : #include "nsIScriptContext.h"
70 : #include "nsCRT.h"
71 :
72 : // Event listeners
73 : #include "nsEventListenerManager.h"
74 : #include "nsIDOMEventListener.h"
75 : #include "nsAttrName.h"
76 :
77 : #include "nsGkAtoms.h"
78 :
79 : #include "nsIDOMAttr.h"
80 : #include "nsIDOMNamedNodeMap.h"
81 :
82 : #include "nsXBLPrototypeHandler.h"
83 :
84 : #include "nsXBLPrototypeBinding.h"
85 : #include "nsXBLBinding.h"
86 : #include "nsIPrincipal.h"
87 : #include "nsIScriptSecurityManager.h"
88 : #include "nsEventListenerManager.h"
89 : #include "nsGUIEvent.h"
90 :
91 : #include "prprf.h"
92 : #include "nsNodeUtils.h"
93 :
94 : // Nasty hack. Maybe we could move some of the classinfo utility methods
95 : // (e.g. WrapNative and ThrowJSException) over to nsContentUtils?
96 : #include "nsDOMClassInfo.h"
97 : #include "nsJSUtils.h"
98 :
99 : #include "mozilla/dom/Element.h"
100 :
101 : // Helper classes
102 :
103 : /***********************************************************************/
104 : //
105 : // The JS class for XBLBinding
106 : //
107 : static void
108 0 : XBLFinalize(JSContext *cx, JSObject *obj)
109 : {
110 : nsXBLDocumentInfo* docInfo =
111 0 : static_cast<nsXBLDocumentInfo*>(::JS_GetPrivate(obj));
112 0 : NS_RELEASE(docInfo);
113 :
114 0 : nsXBLJSClass* c = static_cast<nsXBLJSClass*>(::JS_GetClass(obj));
115 0 : c->Drop();
116 0 : }
117 :
118 : static JSBool
119 0 : XBLResolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
120 : JSObject **objp)
121 : {
122 : // Note: if we get here, that means that the implementation for some binding
123 : // was installed, which means that AllowScripts() tested true. Hence no need
124 : // to do checks like that here.
125 :
126 : // Default to not resolving things.
127 0 : NS_ASSERTION(*objp, "Must have starting object");
128 :
129 0 : JSObject* origObj = *objp;
130 0 : *objp = NULL;
131 :
132 0 : if (!JSID_IS_STRING(id)) {
133 0 : return JS_TRUE;
134 : }
135 :
136 0 : nsDependentJSString fieldName(id);
137 :
138 0 : jsval slotVal = ::JS_GetReservedSlot(obj, 0);
139 0 : NS_ASSERTION(!JSVAL_IS_VOID(slotVal), "How did that happen?");
140 :
141 : nsXBLPrototypeBinding* protoBinding =
142 0 : static_cast<nsXBLPrototypeBinding*>(JSVAL_TO_PRIVATE(slotVal));
143 0 : NS_ASSERTION(protoBinding, "Must have prototype binding!");
144 :
145 0 : nsXBLProtoImplField* field = protoBinding->FindField(fieldName);
146 0 : if (!field) {
147 0 : return JS_TRUE;
148 : }
149 :
150 : // We have this field. Time to install it. Get our node.
151 0 : JSClass* nodeClass = ::JS_GetClass(origObj);
152 0 : if (!nodeClass) {
153 0 : return JS_FALSE;
154 : }
155 :
156 0 : if (~nodeClass->flags &
157 : (JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS)) {
158 0 : nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_UNEXPECTED);
159 0 : return JS_FALSE;
160 : }
161 :
162 : nsCOMPtr<nsIXPConnectWrappedNative> xpcWrapper =
163 0 : do_QueryInterface(static_cast<nsISupports*>(::JS_GetPrivate(origObj)));
164 0 : if (!xpcWrapper) {
165 : // Looks like whatever |origObj| is it's not our nsIContent. It might well
166 : // be the proto our binding installed, however, where the private is the
167 : // nsXBLDocumentInfo, so just baul out quietly. Do NOT throw an exception
168 : // here.
169 : // We could make this stricter by checking the class maybe, but whatever
170 0 : return JS_TRUE;
171 : }
172 :
173 0 : nsCOMPtr<nsIContent> content = do_QueryWrappedNative(xpcWrapper);
174 0 : if (!content) {
175 0 : nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_UNEXPECTED);
176 0 : return JS_FALSE;
177 : }
178 :
179 : // This mirrors code in nsXBLProtoImpl::InstallImplementation
180 0 : nsIDocument* doc = content->OwnerDoc();
181 :
182 0 : nsIScriptGlobalObject* global = doc->GetScriptGlobalObject();
183 0 : if (!global) {
184 0 : return JS_TRUE;
185 : }
186 :
187 0 : nsCOMPtr<nsIScriptContext> context = global->GetContext();
188 0 : if (!context) {
189 0 : return JS_TRUE;
190 : }
191 :
192 :
193 : // Now we either resolve or fail
194 : bool didInstall;
195 : nsresult rv = field->InstallField(context, origObj,
196 0 : content->NodePrincipal(),
197 : protoBinding->DocURI(),
198 0 : &didInstall);
199 0 : if (NS_FAILED(rv)) {
200 0 : if (!::JS_IsExceptionPending(cx)) {
201 0 : nsDOMClassInfo::ThrowJSException(cx, rv);
202 : }
203 :
204 0 : return JS_FALSE;
205 : }
206 :
207 0 : if (didInstall) {
208 0 : *objp = origObj;
209 : }
210 : // else we didn't resolve this field after all
211 :
212 0 : return JS_TRUE;
213 : }
214 :
215 0 : nsXBLJSClass::nsXBLJSClass(const nsAFlatCString& aClassName)
216 : {
217 0 : memset(this, 0, sizeof(nsXBLJSClass));
218 0 : next = prev = static_cast<JSCList*>(this);
219 0 : name = ToNewCString(aClassName);
220 : flags =
221 : JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS |
222 : JSCLASS_NEW_RESOLVE | JSCLASS_NEW_RESOLVE_GETS_START |
223 : // Our one reserved slot holds the relevant nsXBLPrototypeBinding
224 0 : JSCLASS_HAS_RESERVED_SLOTS(1);
225 0 : addProperty = delProperty = getProperty = ::JS_PropertyStub;
226 0 : setProperty = ::JS_StrictPropertyStub;
227 0 : enumerate = ::JS_EnumerateStub;
228 0 : resolve = (JSResolveOp)XBLResolve;
229 0 : convert = ::JS_ConvertStub;
230 0 : finalize = XBLFinalize;
231 0 : }
232 :
233 : nsrefcnt
234 0 : nsXBLJSClass::Destroy()
235 : {
236 0 : NS_ASSERTION(next == prev && prev == static_cast<JSCList*>(this),
237 : "referenced nsXBLJSClass is on LRU list already!?");
238 :
239 0 : if (nsXBLService::gClassTable) {
240 0 : nsCStringKey key(name);
241 0 : (nsXBLService::gClassTable)->Remove(&key);
242 : }
243 :
244 0 : if (nsXBLService::gClassLRUListLength >= nsXBLService::gClassLRUListQuota) {
245 : // Over LRU list quota, just unhash and delete this class.
246 0 : delete this;
247 : } else {
248 : // Put this most-recently-used class on end of the LRU-sorted freelist.
249 0 : JSCList* mru = static_cast<JSCList*>(this);
250 0 : JS_APPEND_LINK(mru, &nsXBLService::gClassLRUList);
251 0 : nsXBLService::gClassLRUListLength++;
252 : }
253 :
254 0 : return 0;
255 : }
256 :
257 : // Implementation /////////////////////////////////////////////////////////////////
258 :
259 : // Constructors/Destructors
260 0 : nsXBLBinding::nsXBLBinding(nsXBLPrototypeBinding* aBinding)
261 : : mPrototypeBinding(aBinding),
262 : mInsertionPointTable(nsnull),
263 : mIsStyleBinding(true),
264 0 : mMarkedForDeath(false)
265 : {
266 0 : NS_ASSERTION(mPrototypeBinding, "Must have a prototype binding!");
267 : // Grab a ref to the document info so the prototype binding won't die
268 0 : NS_ADDREF(mPrototypeBinding->XBLDocumentInfo());
269 0 : }
270 :
271 :
272 0 : nsXBLBinding::~nsXBLBinding(void)
273 : {
274 0 : if (mContent) {
275 0 : nsXBLBinding::UninstallAnonymousContent(mContent->OwnerDoc(), mContent);
276 : }
277 0 : delete mInsertionPointTable;
278 0 : nsXBLDocumentInfo* info = mPrototypeBinding->XBLDocumentInfo();
279 0 : NS_RELEASE(info);
280 0 : }
281 :
282 : static PLDHashOperator
283 0 : TraverseKey(nsISupports* aKey, nsInsertionPointList* aData, void* aClosure)
284 : {
285 : nsCycleCollectionTraversalCallback &cb =
286 0 : *static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
287 :
288 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mInsertionPointTable key");
289 0 : cb.NoteXPCOMChild(aKey);
290 0 : if (aData) {
291 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSTARRAY(*aData, nsXBLInsertionPoint,
292 : "mInsertionPointTable value")
293 : }
294 0 : return PL_DHASH_NEXT;
295 : }
296 :
297 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsXBLBinding)
298 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_NATIVE(nsXBLBinding)
299 : // XXX Probably can't unlink mPrototypeBinding->XBLDocumentInfo(), because
300 : // mPrototypeBinding is weak.
301 0 : if (tmp->mContent) {
302 0 : nsXBLBinding::UninstallAnonymousContent(tmp->mContent->OwnerDoc(),
303 0 : tmp->mContent);
304 : }
305 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mContent)
306 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mNextBinding)
307 0 : delete tmp->mInsertionPointTable;
308 0 : tmp->mInsertionPointTable = nsnull;
309 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
310 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(nsXBLBinding)
311 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
312 : "mPrototypeBinding->XBLDocumentInfo()");
313 : cb.NoteXPCOMChild(static_cast<nsIScriptGlobalObjectOwner*>(
314 0 : tmp->mPrototypeBinding->XBLDocumentInfo()));
315 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mContent)
316 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mNextBinding, nsXBLBinding)
317 0 : if (tmp->mInsertionPointTable)
318 0 : tmp->mInsertionPointTable->EnumerateRead(TraverseKey, &cb);
319 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
320 0 : NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsXBLBinding, AddRef)
321 0 : NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsXBLBinding, Release)
322 :
323 : void
324 0 : nsXBLBinding::SetBaseBinding(nsXBLBinding* aBinding)
325 : {
326 0 : if (mNextBinding) {
327 0 : NS_ERROR("Base XBL binding is already defined!");
328 0 : return;
329 : }
330 :
331 0 : mNextBinding = aBinding; // Comptr handles rel/add
332 : }
333 :
334 : void
335 0 : nsXBLBinding::InstallAnonymousContent(nsIContent* aAnonParent, nsIContent* aElement)
336 : {
337 : // We need to ensure two things.
338 : // (1) The anonymous content should be fooled into thinking it's in the bound
339 : // element's document, assuming that the bound element is in a document
340 : // Note that we don't change the current doc of aAnonParent here, since that
341 : // quite simply does not matter. aAnonParent is just a way of keeping refs
342 : // to all its kids, which are anonymous content from the point of view of
343 : // aElement.
344 : // (2) The children's parent back pointer should not be to this synthetic root
345 : // but should instead point to the enclosing parent element.
346 0 : nsIDocument* doc = aElement->GetCurrentDoc();
347 0 : bool allowScripts = AllowScripts();
348 :
349 0 : nsAutoScriptBlocker scriptBlocker;
350 0 : for (nsIContent* child = aAnonParent->GetFirstChild();
351 : child;
352 0 : child = child->GetNextSibling()) {
353 0 : child->UnbindFromTree();
354 : nsresult rv =
355 0 : child->BindToTree(doc, aElement, mBoundElement, allowScripts);
356 0 : if (NS_FAILED(rv)) {
357 : // Oh, well... Just give up.
358 : // XXXbz This really shouldn't be a void method!
359 0 : child->UnbindFromTree();
360 : return;
361 : }
362 :
363 0 : child->SetFlags(NODE_IS_ANONYMOUS);
364 :
365 : #ifdef MOZ_XUL
366 : // To make XUL templates work (and other goodies that happen when
367 : // an element is added to a XUL document), we need to notify the
368 : // XUL document using its special API.
369 0 : nsCOMPtr<nsIXULDocument> xuldoc(do_QueryInterface(doc));
370 0 : if (xuldoc)
371 0 : xuldoc->AddSubtreeToDocument(child);
372 : #endif
373 : }
374 : }
375 :
376 : void
377 0 : nsXBLBinding::UninstallAnonymousContent(nsIDocument* aDocument,
378 : nsIContent* aAnonParent)
379 : {
380 0 : nsAutoScriptBlocker scriptBlocker;
381 : // Hold a strong ref while doing this, just in case.
382 0 : nsCOMPtr<nsIContent> anonParent = aAnonParent;
383 : #ifdef MOZ_XUL
384 : nsCOMPtr<nsIXULDocument> xuldoc =
385 0 : do_QueryInterface(aDocument);
386 : #endif
387 0 : for (nsIContent* child = aAnonParent->GetFirstChild();
388 : child;
389 0 : child = child->GetNextSibling()) {
390 0 : child->UnbindFromTree();
391 : #ifdef MOZ_XUL
392 0 : if (xuldoc) {
393 0 : xuldoc->RemoveSubtreeFromDocument(child);
394 : }
395 : #endif
396 : }
397 0 : }
398 :
399 : void
400 0 : nsXBLBinding::SetBoundElement(nsIContent* aElement)
401 : {
402 0 : mBoundElement = aElement;
403 0 : if (mNextBinding)
404 0 : mNextBinding->SetBoundElement(aElement);
405 0 : }
406 :
407 : bool
408 0 : nsXBLBinding::HasStyleSheets() const
409 : {
410 : // Find out if we need to re-resolve style. We'll need to do this
411 : // if we have additional stylesheets in our binding document.
412 0 : if (mPrototypeBinding->HasStyleSheets())
413 0 : return true;
414 :
415 0 : return mNextBinding ? mNextBinding->HasStyleSheets() : false;
416 : }
417 :
418 : struct EnumData {
419 : nsXBLBinding* mBinding;
420 :
421 0 : EnumData(nsXBLBinding* aBinding)
422 0 : :mBinding(aBinding)
423 0 : {}
424 : };
425 :
426 : struct ContentListData : public EnumData {
427 : nsBindingManager* mBindingManager;
428 : nsresult mRv;
429 :
430 0 : ContentListData(nsXBLBinding* aBinding, nsBindingManager* aManager)
431 0 : :EnumData(aBinding), mBindingManager(aManager), mRv(NS_OK)
432 0 : {}
433 : };
434 :
435 : static PLDHashOperator
436 0 : BuildContentLists(nsISupports* aKey,
437 : nsAutoPtr<nsInsertionPointList>& aData,
438 : void* aClosure)
439 : {
440 0 : ContentListData* data = (ContentListData*)aClosure;
441 0 : nsBindingManager* bm = data->mBindingManager;
442 0 : nsXBLBinding* binding = data->mBinding;
443 :
444 0 : nsIContent *boundElement = binding->GetBoundElement();
445 :
446 0 : PRInt32 count = aData->Length();
447 :
448 0 : if (count == 0)
449 0 : return PL_DHASH_NEXT;
450 :
451 : // Figure out the relevant content node.
452 0 : nsXBLInsertionPoint* currPoint = aData->ElementAt(0);
453 0 : nsCOMPtr<nsIContent> parent = currPoint->GetInsertionParent();
454 0 : if (!parent) {
455 0 : data->mRv = NS_ERROR_FAILURE;
456 0 : return PL_DHASH_STOP;
457 : }
458 0 : PRInt32 currIndex = currPoint->GetInsertionIndex();
459 :
460 : // XXX Could this array just be altered in place and passed directly to
461 : // SetContentListFor? We'd save space if we could pull this off.
462 0 : nsInsertionPointList* contentList = new nsInsertionPointList;
463 0 : if (!contentList) {
464 0 : data->mRv = NS_ERROR_OUT_OF_MEMORY;
465 0 : return PL_DHASH_STOP;
466 : }
467 :
468 0 : nsCOMPtr<nsIDOMNodeList> nodeList;
469 0 : if (parent == boundElement) {
470 : // We are altering anonymous nodes to accommodate insertion points.
471 0 : nodeList = binding->GetAnonymousNodes();
472 : }
473 : else {
474 : // We are altering the explicit content list of a node to accommodate insertion points.
475 0 : nsCOMPtr<nsIDOMNode> node(do_QueryInterface(parent));
476 0 : node->GetChildNodes(getter_AddRefs(nodeList));
477 : }
478 :
479 0 : nsXBLInsertionPoint* pseudoPoint = nsnull;
480 : PRUint32 childCount;
481 0 : nodeList->GetLength(&childCount);
482 0 : PRInt32 j = 0;
483 :
484 0 : for (PRUint32 i = 0; i < childCount; i++) {
485 0 : nsCOMPtr<nsIDOMNode> node;
486 0 : nodeList->Item(i, getter_AddRefs(node));
487 0 : nsCOMPtr<nsIContent> child(do_QueryInterface(node));
488 0 : if (((PRInt32)i) == currIndex) {
489 : // Add the currPoint to the insertion point list.
490 0 : contentList->AppendElement(currPoint);
491 :
492 : // Get the next real insertion point and update our currIndex.
493 0 : j++;
494 0 : if (j < count) {
495 0 : currPoint = aData->ElementAt(j);
496 0 : currIndex = currPoint->GetInsertionIndex();
497 : }
498 :
499 : // Null out our current pseudo-point.
500 0 : pseudoPoint = nsnull;
501 : }
502 :
503 0 : if (!pseudoPoint) {
504 0 : pseudoPoint = new nsXBLInsertionPoint(parent, (PRUint32) -1, nsnull);
505 0 : if (pseudoPoint) {
506 0 : contentList->AppendElement(pseudoPoint);
507 : }
508 : }
509 0 : if (pseudoPoint) {
510 0 : pseudoPoint->AddChild(child);
511 : }
512 : }
513 :
514 : // Add in all the remaining insertion points.
515 0 : contentList->AppendElements(aData->Elements() + j, count - j);
516 :
517 : // Now set the content list using the binding manager,
518 : // If the bound element is the parent, then we alter the anonymous node list
519 : // instead. This allows us to always maintain two distinct lists should
520 : // insertion points be nested into an inner binding.
521 0 : if (parent == boundElement)
522 0 : bm->SetAnonymousNodesFor(parent, contentList);
523 : else
524 0 : bm->SetContentListFor(parent, contentList);
525 0 : return PL_DHASH_NEXT;
526 : }
527 :
528 : static PLDHashOperator
529 0 : RealizeDefaultContent(nsISupports* aKey,
530 : nsAutoPtr<nsInsertionPointList>& aData,
531 : void* aClosure)
532 : {
533 0 : ContentListData* data = (ContentListData*)aClosure;
534 0 : nsBindingManager* bm = data->mBindingManager;
535 0 : nsXBLBinding* binding = data->mBinding;
536 :
537 0 : PRInt32 count = aData->Length();
538 :
539 0 : for (PRInt32 i = 0; i < count; i++) {
540 0 : nsXBLInsertionPoint* currPoint = aData->ElementAt(i);
541 0 : PRInt32 insCount = currPoint->ChildCount();
542 :
543 0 : if (insCount == 0) {
544 0 : nsCOMPtr<nsIContent> defContent = currPoint->GetDefaultContentTemplate();
545 0 : if (defContent) {
546 : // We need to take this template and use it to realize the
547 : // actual default content (through cloning).
548 : // Clone this insertion point element.
549 0 : nsCOMPtr<nsIContent> insParent = currPoint->GetInsertionParent();
550 0 : if (!insParent) {
551 0 : data->mRv = NS_ERROR_FAILURE;
552 0 : return PL_DHASH_STOP;
553 : }
554 0 : nsIDocument *document = insParent->OwnerDoc();
555 0 : nsCOMPtr<nsIDOMNode> clonedNode;
556 0 : nsCOMArray<nsINode> nodesWithProperties;
557 : nsNodeUtils::Clone(defContent, true, document->NodeInfoManager(),
558 0 : nodesWithProperties, getter_AddRefs(clonedNode));
559 :
560 : // Now that we have the cloned content, install the default content as
561 : // if it were additional anonymous content.
562 0 : nsCOMPtr<nsIContent> clonedContent(do_QueryInterface(clonedNode));
563 0 : binding->InstallAnonymousContent(clonedContent, insParent);
564 :
565 : // Cache the clone so that it can be properly destroyed if/when our
566 : // other anonymous content is destroyed.
567 0 : currPoint->SetDefaultContent(clonedContent);
568 :
569 : // Now make sure the kids of the clone are added to the insertion point as
570 : // children.
571 0 : for (nsIContent* child = clonedContent->GetFirstChild();
572 : child;
573 0 : child = child->GetNextSibling()) {
574 0 : bm->SetInsertionParent(child, insParent);
575 0 : currPoint->AddChild(child);
576 : }
577 : }
578 : }
579 : }
580 :
581 0 : return PL_DHASH_NEXT;
582 : }
583 :
584 : static PLDHashOperator
585 0 : ChangeDocumentForDefaultContent(nsISupports* aKey,
586 : nsAutoPtr<nsInsertionPointList>& aData,
587 : void* aClosure)
588 : {
589 0 : PRInt32 count = aData->Length();
590 0 : for (PRInt32 i = 0; i < count; i++) {
591 0 : aData->ElementAt(i)->UnbindDefaultContent();
592 : }
593 :
594 0 : return PL_DHASH_NEXT;
595 : }
596 :
597 : void
598 0 : nsXBLBinding::GenerateAnonymousContent()
599 : {
600 0 : NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
601 : "Someone forgot a script blocker");
602 :
603 : // Fetch the content element for this binding.
604 : nsIContent* content =
605 0 : mPrototypeBinding->GetImmediateChild(nsGkAtoms::content);
606 :
607 0 : if (!content) {
608 : // We have no anonymous content.
609 0 : if (mNextBinding)
610 0 : mNextBinding->GenerateAnonymousContent();
611 :
612 0 : return;
613 : }
614 :
615 : // Find out if we're really building kids or if we're just
616 : // using the attribute-setting shorthand hack.
617 0 : PRUint32 contentCount = content->GetChildCount();
618 :
619 : // Plan to build the content by default.
620 0 : bool hasContent = (contentCount > 0);
621 0 : bool hasInsertionPoints = mPrototypeBinding->HasInsertionPoints();
622 :
623 : #ifdef DEBUG
624 : // See if there's an includes attribute.
625 0 : if (nsContentUtils::HasNonEmptyAttr(content, kNameSpaceID_None,
626 0 : nsGkAtoms::includes)) {
627 0 : nsCAutoString message("An XBL Binding with URI ");
628 0 : nsCAutoString uri;
629 0 : mPrototypeBinding->BindingURI()->GetSpec(uri);
630 0 : message += uri;
631 0 : message += " is still using the deprecated\n<content includes=\"\"> syntax! Use <children> instead!\n";
632 0 : NS_WARNING(message.get());
633 : }
634 : #endif
635 :
636 0 : if (hasContent || hasInsertionPoints) {
637 0 : nsIDocument* doc = mBoundElement->OwnerDoc();
638 :
639 0 : nsBindingManager *bindingManager = doc->BindingManager();
640 :
641 0 : nsCOMPtr<nsIDOMNodeList> children;
642 0 : bindingManager->GetContentListFor(mBoundElement, getter_AddRefs(children));
643 :
644 0 : nsCOMPtr<nsIDOMNode> node;
645 0 : nsCOMPtr<nsIContent> childContent;
646 : PRUint32 length;
647 0 : children->GetLength(&length);
648 0 : if (length > 0 && !hasInsertionPoints) {
649 : // There are children being placed underneath us, but we have no specified
650 : // insertion points, and therefore no place to put the kids. Don't generate
651 : // anonymous content.
652 : // Special case template and observes.
653 0 : for (PRUint32 i = 0; i < length; i++) {
654 0 : children->Item(i, getter_AddRefs(node));
655 0 : childContent = do_QueryInterface(node);
656 :
657 0 : nsINodeInfo *ni = childContent->NodeInfo();
658 0 : nsIAtom *localName = ni->NameAtom();
659 0 : if (ni->NamespaceID() != kNameSpaceID_XUL ||
660 : (localName != nsGkAtoms::observes &&
661 : localName != nsGkAtoms::_template)) {
662 0 : hasContent = false;
663 0 : break;
664 : }
665 : }
666 : }
667 :
668 0 : if (hasContent || hasInsertionPoints) {
669 0 : nsCOMPtr<nsIDOMNode> clonedNode;
670 0 : nsCOMArray<nsINode> nodesWithProperties;
671 : nsNodeUtils::Clone(content, true, doc->NodeInfoManager(),
672 0 : nodesWithProperties, getter_AddRefs(clonedNode));
673 :
674 0 : mContent = do_QueryInterface(clonedNode);
675 0 : InstallAnonymousContent(mContent, mBoundElement);
676 :
677 0 : if (hasInsertionPoints) {
678 : // Now check and see if we have a single insertion point
679 : // or multiple insertion points.
680 :
681 : // Enumerate the prototype binding's insertion table to build
682 : // our table of instantiated insertion points.
683 0 : mPrototypeBinding->InstantiateInsertionPoints(this);
684 :
685 : // We now have our insertion point table constructed. We
686 : // enumerate this table. For each array of insertion points
687 : // bundled under the same content node, we generate a content
688 : // list. In the case of the bound element, we generate a new
689 : // anonymous node list that will be used in place of the binding's
690 : // cached anonymous node list.
691 0 : ContentListData data(this, bindingManager);
692 0 : mInsertionPointTable->Enumerate(BuildContentLists, &data);
693 0 : if (NS_FAILED(data.mRv)) {
694 : return;
695 : }
696 :
697 : // We need to place the children
698 : // at their respective insertion points.
699 0 : PRUint32 index = 0;
700 0 : bool multiplePoints = false;
701 : nsIContent *singlePoint = GetSingleInsertionPoint(&index,
702 0 : &multiplePoints);
703 :
704 0 : if (children) {
705 0 : if (multiplePoints) {
706 : // We must walk the entire content list in order to determine where
707 : // each child belongs.
708 0 : children->GetLength(&length);
709 0 : for (PRUint32 i = 0; i < length; i++) {
710 0 : children->Item(i, getter_AddRefs(node));
711 0 : childContent = do_QueryInterface(node);
712 :
713 : // Now determine the insertion point in the prototype table.
714 : PRUint32 index;
715 0 : nsIContent *point = GetInsertionPoint(childContent, &index);
716 0 : bindingManager->SetInsertionParent(childContent, point);
717 :
718 : // Find the correct nsIXBLInsertion point in our table.
719 0 : nsInsertionPointList* arr = nsnull;
720 0 : GetInsertionPointsFor(point, &arr);
721 0 : nsXBLInsertionPoint* insertionPoint = nsnull;
722 0 : PRInt32 arrCount = arr->Length();
723 0 : for (PRInt32 j = 0; j < arrCount; j++) {
724 0 : insertionPoint = arr->ElementAt(j);
725 0 : if (insertionPoint->Matches(point, index))
726 0 : break;
727 0 : insertionPoint = nsnull;
728 : }
729 :
730 0 : if (insertionPoint)
731 0 : insertionPoint->AddChild(childContent);
732 : else {
733 : // We were unable to place this child. All anonymous content
734 : // should be thrown out. Special-case template and observes.
735 :
736 0 : nsINodeInfo *ni = childContent->NodeInfo();
737 0 : nsIAtom *localName = ni->NameAtom();
738 0 : if (ni->NamespaceID() != kNameSpaceID_XUL ||
739 : (localName != nsGkAtoms::observes &&
740 : localName != nsGkAtoms::_template)) {
741 : // Undo InstallAnonymousContent
742 0 : UninstallAnonymousContent(doc, mContent);
743 :
744 : // Kill all anonymous content.
745 0 : mContent = nsnull;
746 0 : bindingManager->SetContentListFor(mBoundElement, nsnull);
747 0 : bindingManager->SetAnonymousNodesFor(mBoundElement, nsnull);
748 : return;
749 : }
750 : }
751 : }
752 : }
753 : else {
754 : // All of our children are shunted to this single insertion point.
755 0 : nsInsertionPointList* arr = nsnull;
756 0 : GetInsertionPointsFor(singlePoint, &arr);
757 0 : nsXBLInsertionPoint* insertionPoint = arr->ElementAt(0);
758 :
759 0 : nsCOMPtr<nsIDOMNode> node;
760 0 : nsCOMPtr<nsIContent> content;
761 : PRUint32 length;
762 0 : children->GetLength(&length);
763 :
764 0 : for (PRUint32 i = 0; i < length; i++) {
765 0 : children->Item(i, getter_AddRefs(node));
766 0 : content = do_QueryInterface(node);
767 0 : bindingManager->SetInsertionParent(content, singlePoint);
768 0 : insertionPoint->AddChild(content);
769 : }
770 : }
771 : }
772 :
773 : // Now that all of our children have been added, we need to walk all of our
774 : // nsIXBLInsertion points to see if any of them have default content that
775 : // needs to be built.
776 0 : mInsertionPointTable->Enumerate(RealizeDefaultContent, &data);
777 0 : if (NS_FAILED(data.mRv)) {
778 : return;
779 : }
780 : }
781 : }
782 :
783 0 : mPrototypeBinding->SetInitialAttributes(mBoundElement, mContent);
784 : }
785 :
786 : // Always check the content element for potential attributes.
787 : // This shorthand hack always happens, even when we didn't
788 : // build anonymous content.
789 : const nsAttrName* attrName;
790 0 : for (PRUint32 i = 0; (attrName = content->GetAttrNameAt(i)); ++i) {
791 0 : PRInt32 namespaceID = attrName->NamespaceID();
792 : // Hold a strong reference here so that the atom doesn't go away during
793 : // UnsetAttr.
794 0 : nsCOMPtr<nsIAtom> name = attrName->LocalName();
795 :
796 0 : if (name != nsGkAtoms::includes) {
797 0 : if (!nsContentUtils::HasNonEmptyAttr(mBoundElement, namespaceID, name)) {
798 0 : nsAutoString value2;
799 0 : content->GetAttr(namespaceID, name, value2);
800 : mBoundElement->SetAttr(namespaceID, name, attrName->GetPrefix(),
801 0 : value2, false);
802 : }
803 : }
804 :
805 : // Conserve space by wiping the attributes off the clone.
806 0 : if (mContent)
807 0 : mContent->UnsetAttr(namespaceID, name, false);
808 : }
809 : }
810 :
811 : void
812 0 : nsXBLBinding::InstallEventHandlers()
813 : {
814 : // Don't install handlers if scripts aren't allowed.
815 0 : if (AllowScripts()) {
816 : // Fetch the handlers prototypes for this binding.
817 0 : nsXBLPrototypeHandler* handlerChain = mPrototypeBinding->GetPrototypeHandlers();
818 :
819 0 : if (handlerChain) {
820 : nsEventListenerManager* manager =
821 0 : mBoundElement->GetListenerManager(true);
822 0 : if (!manager)
823 0 : return;
824 :
825 : bool isChromeDoc =
826 0 : nsContentUtils::IsChromeDoc(mBoundElement->OwnerDoc());
827 0 : bool isChromeBinding = mPrototypeBinding->IsChrome();
828 : nsXBLPrototypeHandler* curr;
829 0 : for (curr = handlerChain; curr; curr = curr->GetNextHandler()) {
830 : // Fetch the event type.
831 0 : nsCOMPtr<nsIAtom> eventAtom = curr->GetEventName();
832 0 : if (!eventAtom ||
833 0 : eventAtom == nsGkAtoms::keyup ||
834 0 : eventAtom == nsGkAtoms::keydown ||
835 0 : eventAtom == nsGkAtoms::keypress)
836 0 : continue;
837 :
838 0 : nsXBLEventHandler* handler = curr->GetEventHandler();
839 0 : if (handler) {
840 : // Figure out if we're using capturing or not.
841 0 : PRInt32 flags = (curr->GetPhase() == NS_PHASE_CAPTURING) ?
842 0 : NS_EVENT_FLAG_CAPTURE : NS_EVENT_FLAG_BUBBLE;
843 :
844 : // If this is a command, add it in the system event group
845 0 : if ((curr->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND |
846 : NS_HANDLER_TYPE_SYSTEM)) &&
847 0 : (isChromeBinding || mBoundElement->IsInNativeAnonymousSubtree())) {
848 0 : flags |= NS_EVENT_FLAG_SYSTEM_EVENT;
849 : }
850 :
851 0 : bool hasAllowUntrustedAttr = curr->HasAllowUntrustedAttr();
852 0 : if ((hasAllowUntrustedAttr && curr->AllowUntrustedEvents()) ||
853 0 : (!hasAllowUntrustedAttr && !isChromeDoc)) {
854 0 : flags |= NS_PRIV_EVENT_UNTRUSTED_PERMITTED;
855 : }
856 :
857 : manager->AddEventListenerByType(handler,
858 0 : nsDependentAtomString(eventAtom),
859 0 : flags);
860 : }
861 : }
862 :
863 : const nsCOMArray<nsXBLKeyEventHandler>* keyHandlers =
864 0 : mPrototypeBinding->GetKeyEventHandlers();
865 : PRInt32 i;
866 0 : for (i = 0; i < keyHandlers->Count(); ++i) {
867 0 : nsXBLKeyEventHandler* handler = keyHandlers->ObjectAt(i);
868 0 : handler->SetIsBoundToChrome(isChromeDoc);
869 :
870 0 : nsAutoString type;
871 0 : handler->GetEventName(type);
872 :
873 : // If this is a command, add it in the system event group, otherwise
874 : // add it to the standard event group.
875 :
876 : // Figure out if we're using capturing or not.
877 0 : PRInt32 flags = (handler->GetPhase() == NS_PHASE_CAPTURING) ?
878 0 : NS_EVENT_FLAG_CAPTURE : NS_EVENT_FLAG_BUBBLE;
879 :
880 0 : if ((handler->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND |
881 : NS_HANDLER_TYPE_SYSTEM)) &&
882 0 : (isChromeBinding || mBoundElement->IsInNativeAnonymousSubtree())) {
883 0 : flags |= NS_EVENT_FLAG_SYSTEM_EVENT;
884 : }
885 :
886 : // For key handlers we have to set NS_PRIV_EVENT_UNTRUSTED_PERMITTED flag.
887 : // Whether the handling of the event is allowed or not is handled in
888 : // nsXBLKeyEventHandler::HandleEvent
889 0 : flags |= NS_PRIV_EVENT_UNTRUSTED_PERMITTED;
890 :
891 0 : manager->AddEventListenerByType(handler, type, flags);
892 : }
893 : }
894 : }
895 :
896 0 : if (mNextBinding)
897 0 : mNextBinding->InstallEventHandlers();
898 : }
899 :
900 : nsresult
901 0 : nsXBLBinding::InstallImplementation()
902 : {
903 : // Always install the base class properties first, so that
904 : // derived classes can reference the base class properties.
905 :
906 0 : if (mNextBinding) {
907 0 : nsresult rv = mNextBinding->InstallImplementation();
908 0 : NS_ENSURE_SUCCESS(rv, rv);
909 : }
910 :
911 : // iterate through each property in the prototype's list and install the property.
912 0 : if (AllowScripts())
913 0 : return mPrototypeBinding->InstallImplementation(mBoundElement);
914 :
915 0 : return NS_OK;
916 : }
917 :
918 : nsIAtom*
919 0 : nsXBLBinding::GetBaseTag(PRInt32* aNameSpaceID)
920 : {
921 0 : nsIAtom *tag = mPrototypeBinding->GetBaseTag(aNameSpaceID);
922 0 : if (!tag && mNextBinding)
923 0 : return mNextBinding->GetBaseTag(aNameSpaceID);
924 :
925 0 : return tag;
926 : }
927 :
928 : void
929 0 : nsXBLBinding::AttributeChanged(nsIAtom* aAttribute, PRInt32 aNameSpaceID,
930 : bool aRemoveFlag, bool aNotify)
931 : {
932 : // XXX Change if we ever allow multiple bindings in a chain to contribute anonymous content
933 0 : if (!mContent) {
934 0 : if (mNextBinding)
935 : mNextBinding->AttributeChanged(aAttribute, aNameSpaceID,
936 0 : aRemoveFlag, aNotify);
937 : } else {
938 : mPrototypeBinding->AttributeChanged(aAttribute, aNameSpaceID, aRemoveFlag,
939 0 : mBoundElement, mContent, aNotify);
940 : }
941 0 : }
942 :
943 : void
944 0 : nsXBLBinding::ExecuteAttachedHandler()
945 : {
946 0 : if (mNextBinding)
947 0 : mNextBinding->ExecuteAttachedHandler();
948 :
949 0 : if (AllowScripts())
950 0 : mPrototypeBinding->BindingAttached(mBoundElement);
951 0 : }
952 :
953 : void
954 0 : nsXBLBinding::ExecuteDetachedHandler()
955 : {
956 0 : if (AllowScripts())
957 0 : mPrototypeBinding->BindingDetached(mBoundElement);
958 :
959 0 : if (mNextBinding)
960 0 : mNextBinding->ExecuteDetachedHandler();
961 0 : }
962 :
963 : void
964 0 : nsXBLBinding::UnhookEventHandlers()
965 : {
966 0 : nsXBLPrototypeHandler* handlerChain = mPrototypeBinding->GetPrototypeHandlers();
967 :
968 0 : if (handlerChain) {
969 : nsEventListenerManager* manager =
970 0 : mBoundElement->GetListenerManager(false);
971 0 : if (!manager) {
972 0 : return;
973 : }
974 :
975 0 : bool isChromeBinding = mPrototypeBinding->IsChrome();
976 : nsXBLPrototypeHandler* curr;
977 0 : for (curr = handlerChain; curr; curr = curr->GetNextHandler()) {
978 0 : nsXBLEventHandler* handler = curr->GetCachedEventHandler();
979 0 : if (!handler) {
980 0 : continue;
981 : }
982 :
983 0 : nsCOMPtr<nsIAtom> eventAtom = curr->GetEventName();
984 0 : if (!eventAtom ||
985 0 : eventAtom == nsGkAtoms::keyup ||
986 0 : eventAtom == nsGkAtoms::keydown ||
987 0 : eventAtom == nsGkAtoms::keypress)
988 0 : continue;
989 :
990 : // Figure out if we're using capturing or not.
991 0 : PRInt32 flags = (curr->GetPhase() == NS_PHASE_CAPTURING) ?
992 0 : NS_EVENT_FLAG_CAPTURE : NS_EVENT_FLAG_BUBBLE;
993 :
994 : // If this is a command, remove it from the system event group,
995 : // otherwise remove it from the standard event group.
996 :
997 0 : if ((curr->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND |
998 : NS_HANDLER_TYPE_SYSTEM)) &&
999 0 : (isChromeBinding || mBoundElement->IsInNativeAnonymousSubtree())) {
1000 0 : flags |= NS_EVENT_FLAG_SYSTEM_EVENT;
1001 : }
1002 :
1003 : manager->RemoveEventListenerByType(handler,
1004 0 : nsDependentAtomString(eventAtom),
1005 0 : flags);
1006 : }
1007 :
1008 : const nsCOMArray<nsXBLKeyEventHandler>* keyHandlers =
1009 0 : mPrototypeBinding->GetKeyEventHandlers();
1010 : PRInt32 i;
1011 0 : for (i = 0; i < keyHandlers->Count(); ++i) {
1012 0 : nsXBLKeyEventHandler* handler = keyHandlers->ObjectAt(i);
1013 :
1014 0 : nsAutoString type;
1015 0 : handler->GetEventName(type);
1016 :
1017 : // Figure out if we're using capturing or not.
1018 0 : PRInt32 flags = (handler->GetPhase() == NS_PHASE_CAPTURING) ?
1019 0 : NS_EVENT_FLAG_CAPTURE : NS_EVENT_FLAG_BUBBLE;
1020 :
1021 : // If this is a command, remove it from the system event group, otherwise
1022 : // remove it from the standard event group.
1023 :
1024 0 : if ((handler->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND | NS_HANDLER_TYPE_SYSTEM)) &&
1025 0 : (isChromeBinding || mBoundElement->IsInNativeAnonymousSubtree())) {
1026 0 : flags |= NS_EVENT_FLAG_SYSTEM_EVENT;
1027 : }
1028 :
1029 0 : manager->RemoveEventListenerByType(handler, type, flags);
1030 : }
1031 : }
1032 : }
1033 :
1034 : void
1035 0 : nsXBLBinding::ChangeDocument(nsIDocument* aOldDocument, nsIDocument* aNewDocument)
1036 : {
1037 0 : if (aOldDocument != aNewDocument) {
1038 : // Only style bindings get their prototypes unhooked. First do ourselves.
1039 0 : if (mIsStyleBinding) {
1040 : // Now the binding dies. Unhook our prototypes.
1041 0 : if (mPrototypeBinding->HasImplementation()) {
1042 0 : nsIScriptGlobalObject *global = aOldDocument->GetScopeObject();
1043 0 : if (global) {
1044 0 : JSObject *scope = global->GetGlobalJSObject();
1045 : // scope might be null if we've cycle-collected the global
1046 : // object, since the Unlink phase of cycle collection happens
1047 : // after JS GC finalization. But in that case, we don't care
1048 : // about fixing the prototype chain, since everything's going
1049 : // away immediately.
1050 :
1051 0 : nsCOMPtr<nsIScriptContext> context = global->GetContext();
1052 0 : if (context && scope) {
1053 0 : JSContext *cx = context->GetNativeContext();
1054 :
1055 0 : nsCxPusher pusher;
1056 0 : pusher.Push(cx);
1057 :
1058 0 : nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
1059 0 : nsIXPConnect *xpc = nsContentUtils::XPConnect();
1060 : nsresult rv =
1061 : xpc->GetWrappedNativeOfNativeObject(cx, scope, mBoundElement,
1062 : NS_GET_IID(nsISupports),
1063 0 : getter_AddRefs(wrapper));
1064 0 : if (NS_FAILED(rv))
1065 : return;
1066 :
1067 : JSObject* scriptObject;
1068 0 : if (wrapper)
1069 0 : wrapper->GetJSObject(&scriptObject);
1070 : else
1071 0 : scriptObject = nsnull;
1072 :
1073 0 : if (scriptObject) {
1074 : // XXX Stay in sync! What if a layered binding has an
1075 : // <interface>?!
1076 : // XXXbz what does that comment mean, really? It seems to date
1077 : // back to when there was such a thing as an <interface>, whever
1078 : // that was...
1079 :
1080 : // Find the right prototype.
1081 0 : JSObject* base = scriptObject;
1082 : JSObject* proto;
1083 0 : JSAutoRequest ar(cx);
1084 0 : JSAutoEnterCompartment ac;
1085 0 : if (!ac.enter(cx, scriptObject)) {
1086 : return;
1087 : }
1088 :
1089 0 : for ( ; true; base = proto) { // Will break out on null proto
1090 0 : proto = ::JS_GetPrototype(base);
1091 0 : if (!proto) {
1092 0 : break;
1093 : }
1094 :
1095 0 : JSClass* clazz = ::JS_GetClass(proto);
1096 0 : if (!clazz ||
1097 : (~clazz->flags &
1098 : (JSCLASS_HAS_PRIVATE | JSCLASS_PRIVATE_IS_NSISUPPORTS)) ||
1099 : JSCLASS_RESERVED_SLOTS(clazz) != 1 ||
1100 : clazz->resolve != (JSResolveOp)XBLResolve ||
1101 : clazz->finalize != XBLFinalize) {
1102 : // Clearly not the right class
1103 0 : continue;
1104 : }
1105 :
1106 : nsRefPtr<nsXBLDocumentInfo> docInfo =
1107 0 : static_cast<nsXBLDocumentInfo*>(::JS_GetPrivate(proto));
1108 0 : if (!docInfo) {
1109 : // Not the proto we seek
1110 0 : continue;
1111 : }
1112 :
1113 0 : jsval protoBinding = ::JS_GetReservedSlot(proto, 0);
1114 :
1115 0 : if (JSVAL_TO_PRIVATE(protoBinding) != mPrototypeBinding) {
1116 : // Not the right binding
1117 0 : continue;
1118 : }
1119 :
1120 : // Alright! This is the right prototype. Pull it out of the
1121 : // proto chain.
1122 0 : JSObject* grandProto = ::JS_GetPrototype(proto);
1123 0 : ::JS_SetPrototype(cx, base, grandProto);
1124 0 : break;
1125 : }
1126 :
1127 0 : mPrototypeBinding->UndefineFields(cx, scriptObject);
1128 :
1129 : // Don't remove the reference from the document to the
1130 : // wrapper here since it'll be removed by the element
1131 : // itself when that's taken out of the document.
1132 : }
1133 : }
1134 : }
1135 : }
1136 :
1137 : // Remove our event handlers
1138 0 : UnhookEventHandlers();
1139 : }
1140 :
1141 : {
1142 0 : nsAutoScriptBlocker scriptBlocker;
1143 :
1144 : // Then do our ancestors. This reverses the construction order, so that at
1145 : // all times things are consistent as far as everyone is concerned.
1146 0 : if (mNextBinding) {
1147 0 : mNextBinding->ChangeDocument(aOldDocument, aNewDocument);
1148 : }
1149 :
1150 : // Update the anonymous content.
1151 : // XXXbz why not only for style bindings?
1152 0 : nsIContent *anonymous = mContent;
1153 0 : if (anonymous) {
1154 : // Also kill the default content within all our insertion points.
1155 0 : if (mInsertionPointTable)
1156 : mInsertionPointTable->Enumerate(ChangeDocumentForDefaultContent,
1157 0 : nsnull);
1158 :
1159 0 : nsXBLBinding::UninstallAnonymousContent(aOldDocument, anonymous);
1160 : }
1161 :
1162 : // Make sure that henceforth we don't claim that mBoundElement's children
1163 : // have insertion parents in the old document.
1164 0 : nsBindingManager* bindingManager = aOldDocument->BindingManager();
1165 0 : for (nsIContent* child = mBoundElement->GetLastChild();
1166 : child;
1167 0 : child = child->GetPreviousSibling()) {
1168 0 : bindingManager->SetInsertionParent(child, nsnull);
1169 : }
1170 : }
1171 : }
1172 : }
1173 :
1174 : bool
1175 0 : nsXBLBinding::InheritsStyle() const
1176 : {
1177 : // XXX Will have to change if we ever allow multiple bindings to contribute anonymous content.
1178 : // Most derived binding with anonymous content determines style inheritance for now.
1179 :
1180 : // XXX What about bindings with <content> but no kids, e.g., my treecell-text binding?
1181 0 : if (mContent)
1182 0 : return mPrototypeBinding->InheritsStyle();
1183 :
1184 0 : if (mNextBinding)
1185 0 : return mNextBinding->InheritsStyle();
1186 :
1187 0 : return true;
1188 : }
1189 :
1190 : void
1191 0 : nsXBLBinding::WalkRules(nsIStyleRuleProcessor::EnumFunc aFunc, void* aData)
1192 : {
1193 0 : if (mNextBinding)
1194 0 : mNextBinding->WalkRules(aFunc, aData);
1195 :
1196 0 : nsIStyleRuleProcessor *rules = mPrototypeBinding->GetRuleProcessor();
1197 0 : if (rules)
1198 0 : (*aFunc)(rules, aData);
1199 0 : }
1200 :
1201 : // Internal helper methods ////////////////////////////////////////////////////////////////
1202 :
1203 : // static
1204 : nsresult
1205 0 : nsXBLBinding::DoInitJSClass(JSContext *cx, JSObject *global, JSObject *obj,
1206 : const nsAFlatCString& aClassName,
1207 : nsXBLPrototypeBinding* aProtoBinding,
1208 : JSObject** aClassObject)
1209 : {
1210 : // First ensure our JS class is initialized.
1211 0 : nsCAutoString className(aClassName);
1212 0 : JSObject* parent_proto = nsnull; // If we have an "obj" we can set this
1213 0 : JSAutoRequest ar(cx);
1214 :
1215 0 : JSAutoEnterCompartment ac;
1216 0 : if (!ac.enter(cx, global)) {
1217 0 : return NS_ERROR_FAILURE;
1218 : }
1219 :
1220 0 : if (obj) {
1221 : // Retrieve the current prototype of obj.
1222 0 : parent_proto = ::JS_GetPrototype(obj);
1223 0 : if (parent_proto) {
1224 : // We need to create a unique classname based on aClassName and
1225 : // parent_proto. Append a space (an invalid URI character) to ensure that
1226 : // we don't have accidental collisions with the case when parent_proto is
1227 : // null and aClassName ends in some bizarre numbers (yeah, it's unlikely).
1228 : jsid parent_proto_id;
1229 0 : if (!::JS_GetObjectId(cx, parent_proto, &parent_proto_id)) {
1230 : // Probably OOM
1231 0 : return NS_ERROR_OUT_OF_MEMORY;
1232 : }
1233 :
1234 : // One space, maybe "0x", at most 16 chars (on a 64-bit system) of long,
1235 : // and a null-terminator (which PR_snprintf ensures is there even if the
1236 : // string representation of what we're printing does not fit in the buffer
1237 : // provided).
1238 : char buf[20];
1239 0 : PR_snprintf(buf, sizeof(buf), " %lx", parent_proto_id);
1240 0 : className.Append(buf);
1241 : }
1242 : }
1243 :
1244 : jsval val;
1245 0 : JSObject* proto = NULL;
1246 0 : if ((!::JS_LookupPropertyWithFlags(cx, global, className.get(),
1247 : JSRESOLVE_CLASSNAME,
1248 0 : &val)) ||
1249 0 : JSVAL_IS_PRIMITIVE(val)) {
1250 : // We need to initialize the class.
1251 :
1252 : nsXBLJSClass* c;
1253 : void* classObject;
1254 0 : nsCStringKey key(className);
1255 0 : classObject = (nsXBLService::gClassTable)->Get(&key);
1256 :
1257 0 : if (classObject) {
1258 0 : c = static_cast<nsXBLJSClass*>(classObject);
1259 :
1260 : // If c is on the LRU list (i.e., not linked to itself), remove it now!
1261 0 : JSCList* link = static_cast<JSCList*>(c);
1262 0 : if (c->next != link) {
1263 0 : JS_REMOVE_AND_INIT_LINK(link);
1264 0 : nsXBLService::gClassLRUListLength--;
1265 : }
1266 : } else {
1267 0 : if (JS_CLIST_IS_EMPTY(&nsXBLService::gClassLRUList)) {
1268 : // We need to create a struct for this class.
1269 0 : c = new nsXBLJSClass(className);
1270 :
1271 0 : if (!c)
1272 0 : return NS_ERROR_OUT_OF_MEMORY;
1273 : } else {
1274 : // Pull the least recently used class struct off the list.
1275 0 : JSCList* lru = (nsXBLService::gClassLRUList).next;
1276 0 : JS_REMOVE_AND_INIT_LINK(lru);
1277 0 : nsXBLService::gClassLRUListLength--;
1278 :
1279 : // Remove any mapping from the old name to the class struct.
1280 0 : c = static_cast<nsXBLJSClass*>(lru);
1281 0 : nsCStringKey oldKey(c->name);
1282 0 : (nsXBLService::gClassTable)->Remove(&oldKey);
1283 :
1284 : // Change the class name and we're done.
1285 0 : nsMemory::Free((void*) c->name);
1286 0 : c->name = ToNewCString(className);
1287 : }
1288 :
1289 : // Add c to our table.
1290 0 : (nsXBLService::gClassTable)->Put(&key, (void*)c);
1291 : }
1292 :
1293 : // The prototype holds a strong reference to its class struct.
1294 0 : c->Hold();
1295 :
1296 : // Make a new object prototyped by parent_proto and parented by global.
1297 : proto = ::JS_InitClass(cx, // context
1298 : global, // global object
1299 : parent_proto, // parent proto
1300 : c, // JSClass
1301 : nsnull, // JSNative ctor
1302 : 0, // ctor args
1303 : nsnull, // proto props
1304 : nsnull, // proto funcs
1305 : nsnull, // ctor props (static)
1306 0 : nsnull); // ctor funcs (static)
1307 0 : if (!proto) {
1308 : // This will happen if we're OOM or if the security manager
1309 : // denies defining the new class...
1310 :
1311 0 : (nsXBLService::gClassTable)->Remove(&key);
1312 :
1313 0 : c->Drop();
1314 :
1315 0 : return NS_ERROR_OUT_OF_MEMORY;
1316 : }
1317 :
1318 : // Keep this proto binding alive while we're alive. Do this first so that
1319 : // we can guarantee that in XBLFinalize this will be non-null.
1320 : // Note that we can't just store aProtoBinding in the private and
1321 : // addref/release the nsXBLDocumentInfo through it, because cycle
1322 : // collection doesn't seem to work right if the private is not an
1323 : // nsISupports.
1324 0 : nsXBLDocumentInfo* docInfo = aProtoBinding->XBLDocumentInfo();
1325 0 : ::JS_SetPrivate(proto, docInfo);
1326 0 : NS_ADDREF(docInfo);
1327 :
1328 0 : ::JS_SetReservedSlot(proto, 0, PRIVATE_TO_JSVAL(aProtoBinding));
1329 :
1330 0 : *aClassObject = proto;
1331 : }
1332 : else {
1333 0 : proto = JSVAL_TO_OBJECT(val);
1334 : }
1335 :
1336 0 : if (obj) {
1337 : // Set the prototype of our object to be the new class.
1338 0 : if (!::JS_SetPrototype(cx, obj, proto)) {
1339 0 : return NS_ERROR_FAILURE;
1340 : }
1341 : }
1342 :
1343 0 : return NS_OK;
1344 : }
1345 :
1346 : bool
1347 0 : nsXBLBinding::AllowScripts()
1348 : {
1349 0 : if (!mPrototypeBinding->GetAllowScripts())
1350 0 : return false;
1351 :
1352 : // Nasty hack. Use the JSContext of the bound node, since the
1353 : // security manager API expects to get the docshell type from
1354 : // that. But use the nsIPrincipal of our document.
1355 0 : nsIScriptSecurityManager* mgr = nsContentUtils::GetSecurityManager();
1356 0 : if (!mgr) {
1357 0 : return false;
1358 : }
1359 :
1360 0 : nsIDocument* doc = mBoundElement ? mBoundElement->OwnerDoc() : nsnull;
1361 0 : if (!doc) {
1362 0 : return false;
1363 : }
1364 :
1365 0 : nsIScriptGlobalObject* global = doc->GetScriptGlobalObject();
1366 0 : if (!global) {
1367 0 : return false;
1368 : }
1369 :
1370 0 : nsCOMPtr<nsIScriptContext> context = global->GetContext();
1371 0 : if (!context) {
1372 0 : return false;
1373 : }
1374 :
1375 0 : JSContext* cx = context->GetNativeContext();
1376 :
1377 : nsCOMPtr<nsIDocument> ourDocument =
1378 0 : mPrototypeBinding->XBLDocumentInfo()->GetDocument();
1379 : bool canExecute;
1380 : nsresult rv =
1381 0 : mgr->CanExecuteScripts(cx, ourDocument->NodePrincipal(), &canExecute);
1382 0 : if (NS_FAILED(rv) || !canExecute) {
1383 0 : return false;
1384 : }
1385 :
1386 : // Now one last check: make sure that we're not allowing a privilege
1387 : // escalation here.
1388 : bool haveCert;
1389 0 : doc->NodePrincipal()->GetHasCertificate(&haveCert);
1390 0 : if (!haveCert) {
1391 0 : return true;
1392 : }
1393 :
1394 : bool subsumes;
1395 0 : rv = ourDocument->NodePrincipal()->Subsumes(doc->NodePrincipal(), &subsumes);
1396 0 : return NS_SUCCEEDED(rv) && subsumes;
1397 : }
1398 :
1399 : void
1400 0 : nsXBLBinding::RemoveInsertionParent(nsIContent* aParent)
1401 : {
1402 0 : if (mNextBinding) {
1403 0 : mNextBinding->RemoveInsertionParent(aParent);
1404 : }
1405 0 : if (mInsertionPointTable) {
1406 0 : nsInsertionPointList* list = nsnull;
1407 0 : mInsertionPointTable->Get(aParent, &list);
1408 0 : if (list) {
1409 0 : PRInt32 count = list->Length();
1410 0 : for (PRInt32 i = 0; i < count; ++i) {
1411 0 : nsRefPtr<nsXBLInsertionPoint> currPoint = list->ElementAt(i);
1412 0 : currPoint->UnbindDefaultContent();
1413 : #ifdef DEBUG
1414 0 : nsCOMPtr<nsIContent> parent = currPoint->GetInsertionParent();
1415 0 : NS_ASSERTION(!parent || parent == aParent, "Wrong insertion parent!");
1416 : #endif
1417 0 : currPoint->ClearInsertionParent();
1418 : }
1419 0 : mInsertionPointTable->Remove(aParent);
1420 : }
1421 : }
1422 0 : }
1423 :
1424 : bool
1425 0 : nsXBLBinding::HasInsertionParent(nsIContent* aParent)
1426 : {
1427 0 : if (mInsertionPointTable) {
1428 0 : nsInsertionPointList* list = nsnull;
1429 0 : mInsertionPointTable->Get(aParent, &list);
1430 0 : if (list) {
1431 0 : return true;
1432 : }
1433 : }
1434 0 : return mNextBinding ? mNextBinding->HasInsertionParent(aParent) : false;
1435 : }
1436 :
1437 : nsresult
1438 0 : nsXBLBinding::GetInsertionPointsFor(nsIContent* aParent,
1439 : nsInsertionPointList** aResult)
1440 : {
1441 0 : if (!mInsertionPointTable) {
1442 : mInsertionPointTable =
1443 0 : new nsClassHashtable<nsISupportsHashKey, nsInsertionPointList>;
1444 0 : if (!mInsertionPointTable || !mInsertionPointTable->Init(4)) {
1445 0 : delete mInsertionPointTable;
1446 0 : mInsertionPointTable = nsnull;
1447 0 : return NS_ERROR_OUT_OF_MEMORY;
1448 : }
1449 : }
1450 :
1451 0 : mInsertionPointTable->Get(aParent, aResult);
1452 :
1453 0 : if (!*aResult) {
1454 0 : *aResult = new nsInsertionPointList;
1455 0 : if (!*aResult || !mInsertionPointTable->Put(aParent, *aResult)) {
1456 0 : delete *aResult;
1457 0 : *aResult = nsnull;
1458 0 : return NS_ERROR_OUT_OF_MEMORY;
1459 : }
1460 0 : if (aParent) {
1461 0 : aParent->SetFlags(NODE_IS_INSERTION_PARENT);
1462 : }
1463 : }
1464 :
1465 0 : return NS_OK;
1466 : }
1467 :
1468 : nsInsertionPointList*
1469 0 : nsXBLBinding::GetExistingInsertionPointsFor(nsIContent* aParent)
1470 : {
1471 0 : if (!mInsertionPointTable) {
1472 0 : return nsnull;
1473 : }
1474 :
1475 0 : nsInsertionPointList* result = nsnull;
1476 0 : mInsertionPointTable->Get(aParent, &result);
1477 0 : return result;
1478 : }
1479 :
1480 : nsIContent*
1481 0 : nsXBLBinding::GetInsertionPoint(const nsIContent* aChild, PRUint32* aIndex)
1482 : {
1483 0 : if (mContent) {
1484 : return mPrototypeBinding->GetInsertionPoint(mBoundElement, mContent,
1485 0 : aChild, aIndex);
1486 : }
1487 :
1488 0 : if (mNextBinding)
1489 0 : return mNextBinding->GetInsertionPoint(aChild, aIndex);
1490 :
1491 0 : return nsnull;
1492 : }
1493 :
1494 : nsIContent*
1495 0 : nsXBLBinding::GetSingleInsertionPoint(PRUint32* aIndex,
1496 : bool* aMultipleInsertionPoints)
1497 : {
1498 0 : *aMultipleInsertionPoints = false;
1499 0 : if (mContent) {
1500 : return mPrototypeBinding->GetSingleInsertionPoint(mBoundElement, mContent,
1501 : aIndex,
1502 0 : aMultipleInsertionPoints);
1503 : }
1504 :
1505 0 : if (mNextBinding)
1506 : return mNextBinding->GetSingleInsertionPoint(aIndex,
1507 0 : aMultipleInsertionPoints);
1508 :
1509 0 : return nsnull;
1510 : }
1511 :
1512 : nsXBLBinding*
1513 0 : nsXBLBinding::RootBinding()
1514 : {
1515 0 : if (mNextBinding)
1516 0 : return mNextBinding->RootBinding();
1517 :
1518 0 : return this;
1519 : }
1520 :
1521 : nsXBLBinding*
1522 0 : nsXBLBinding::GetFirstStyleBinding()
1523 : {
1524 0 : if (mIsStyleBinding)
1525 0 : return this;
1526 :
1527 0 : return mNextBinding ? mNextBinding->GetFirstStyleBinding() : nsnull;
1528 : }
1529 :
1530 : bool
1531 0 : nsXBLBinding::ResolveAllFields(JSContext *cx, JSObject *obj) const
1532 : {
1533 0 : if (!mPrototypeBinding->ResolveAllFields(cx, obj)) {
1534 0 : return false;
1535 : }
1536 :
1537 0 : if (mNextBinding) {
1538 0 : return mNextBinding->ResolveAllFields(cx, obj);
1539 : }
1540 :
1541 0 : return true;
1542 : }
1543 :
1544 : void
1545 0 : nsXBLBinding::MarkForDeath()
1546 : {
1547 0 : mMarkedForDeath = true;
1548 0 : ExecuteDetachedHandler();
1549 0 : }
1550 :
1551 : bool
1552 0 : nsXBLBinding::ImplementsInterface(REFNSIID aIID) const
1553 : {
1554 0 : return mPrototypeBinding->ImplementsInterface(aIID) ||
1555 0 : (mNextBinding && mNextBinding->ImplementsInterface(aIID));
1556 : }
1557 :
1558 : nsINodeList*
1559 0 : nsXBLBinding::GetAnonymousNodes()
1560 : {
1561 0 : if (mContent) {
1562 0 : return mContent->GetChildNodesList();
1563 : }
1564 :
1565 0 : if (mNextBinding)
1566 0 : return mNextBinding->GetAnonymousNodes();
1567 :
1568 0 : return nsnull;
1569 4392 : }
|