nsXBLBinding.cpp 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "nsCOMPtr.h"
  6. #include "nsIAtom.h"
  7. #include "nsXBLDocumentInfo.h"
  8. #include "nsIInputStream.h"
  9. #include "nsNameSpaceManager.h"
  10. #include "nsIURI.h"
  11. #include "nsIURL.h"
  12. #include "nsIChannel.h"
  13. #include "nsXPIDLString.h"
  14. #include "nsReadableUtils.h"
  15. #include "plstr.h"
  16. #include "nsIContent.h"
  17. #include "nsIDocument.h"
  18. #include "nsContentUtils.h"
  19. #include "ChildIterator.h"
  20. #ifdef MOZ_XUL
  21. #include "nsIXULDocument.h"
  22. #endif
  23. #include "nsIXMLContentSink.h"
  24. #include "nsContentCID.h"
  25. #include "mozilla/dom/XMLDocument.h"
  26. #include "jsapi.h"
  27. #include "nsXBLService.h"
  28. #include "nsIXPConnect.h"
  29. #include "nsIScriptContext.h"
  30. #include "nsCRT.h"
  31. // Event listeners
  32. #include "mozilla/EventListenerManager.h"
  33. #include "nsIDOMEventListener.h"
  34. #include "nsAttrName.h"
  35. #include "nsGkAtoms.h"
  36. #include "nsXBLPrototypeHandler.h"
  37. #include "nsXBLPrototypeBinding.h"
  38. #include "nsXBLBinding.h"
  39. #include "nsIPrincipal.h"
  40. #include "nsIScriptSecurityManager.h"
  41. #include "mozilla/dom/XBLChildrenElement.h"
  42. #include "prprf.h"
  43. #include "nsNodeUtils.h"
  44. #include "nsJSUtils.h"
  45. // Nasty hack. Maybe we could move some of the classinfo utility methods
  46. // (e.g. WrapNative) over to nsContentUtils?
  47. #include "nsDOMClassInfo.h"
  48. #include "mozilla/DeferredFinalize.h"
  49. #include "mozilla/dom/Element.h"
  50. #include "mozilla/dom/ScriptSettings.h"
  51. #include "mozilla/dom/ShadowRoot.h"
  52. #include "mozilla/ServoStyleSet.h"
  53. using namespace mozilla;
  54. using namespace mozilla::dom;
  55. // Helper classes
  56. /***********************************************************************/
  57. //
  58. // The JS class for XBLBinding
  59. //
  60. static void
  61. XBLFinalize(JSFreeOp *fop, JSObject *obj)
  62. {
  63. nsXBLDocumentInfo* docInfo =
  64. static_cast<nsXBLDocumentInfo*>(::JS_GetPrivate(obj));
  65. DeferredFinalize(docInfo);
  66. }
  67. static bool
  68. XBLEnumerate(JSContext *cx, JS::Handle<JSObject*> obj)
  69. {
  70. nsXBLPrototypeBinding* protoBinding =
  71. static_cast<nsXBLPrototypeBinding*>(::JS_GetReservedSlot(obj, 0).toPrivate());
  72. MOZ_ASSERT(protoBinding);
  73. return protoBinding->ResolveAllFields(cx, obj);
  74. }
  75. static const JSClassOps gPrototypeJSClassOps = {
  76. nullptr, nullptr, nullptr, nullptr,
  77. XBLEnumerate, nullptr,
  78. nullptr, XBLFinalize,
  79. nullptr, nullptr, nullptr, nullptr
  80. };
  81. static const JSClass gPrototypeJSClass = {
  82. "XBL prototype JSClass",
  83. JSCLASS_HAS_PRIVATE |
  84. JSCLASS_PRIVATE_IS_NSISUPPORTS |
  85. JSCLASS_FOREGROUND_FINALIZE |
  86. // Our one reserved slot holds the relevant nsXBLPrototypeBinding
  87. JSCLASS_HAS_RESERVED_SLOTS(1),
  88. &gPrototypeJSClassOps
  89. };
  90. // Implementation /////////////////////////////////////////////////////////////////
  91. // Constructors/Destructors
  92. nsXBLBinding::nsXBLBinding(nsXBLPrototypeBinding* aBinding)
  93. : mMarkedForDeath(false)
  94. , mUsingContentXBLScope(false)
  95. , mIsShadowRootBinding(false)
  96. , mPrototypeBinding(aBinding)
  97. {
  98. NS_ASSERTION(mPrototypeBinding, "Must have a prototype binding!");
  99. // Grab a ref to the document info so the prototype binding won't die
  100. NS_ADDREF(mPrototypeBinding->XBLDocumentInfo());
  101. }
  102. // Constructor used by web components.
  103. nsXBLBinding::nsXBLBinding(ShadowRoot* aShadowRoot, nsXBLPrototypeBinding* aBinding)
  104. : mMarkedForDeath(false),
  105. mUsingContentXBLScope(false),
  106. mIsShadowRootBinding(true),
  107. mPrototypeBinding(aBinding),
  108. mContent(aShadowRoot)
  109. {
  110. NS_ASSERTION(mPrototypeBinding, "Must have a prototype binding!");
  111. // Grab a ref to the document info so the prototype binding won't die
  112. NS_ADDREF(mPrototypeBinding->XBLDocumentInfo());
  113. }
  114. nsXBLBinding::~nsXBLBinding(void)
  115. {
  116. if (mContent && !mIsShadowRootBinding) {
  117. // It is unnecessary to uninstall anonymous content in a shadow tree
  118. // because the ShadowRoot itself is a DocumentFragment and does not
  119. // need any additional cleanup.
  120. nsXBLBinding::UninstallAnonymousContent(mContent->OwnerDoc(), mContent);
  121. }
  122. nsXBLDocumentInfo* info = mPrototypeBinding->XBLDocumentInfo();
  123. NS_RELEASE(info);
  124. }
  125. NS_IMPL_CYCLE_COLLECTION_CLASS(nsXBLBinding)
  126. NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXBLBinding)
  127. // XXX Probably can't unlink mPrototypeBinding->XBLDocumentInfo(), because
  128. // mPrototypeBinding is weak.
  129. if (tmp->mContent && !tmp->mIsShadowRootBinding) {
  130. nsXBLBinding::UninstallAnonymousContent(tmp->mContent->OwnerDoc(),
  131. tmp->mContent);
  132. }
  133. NS_IMPL_CYCLE_COLLECTION_UNLINK(mContent)
  134. NS_IMPL_CYCLE_COLLECTION_UNLINK(mNextBinding)
  135. NS_IMPL_CYCLE_COLLECTION_UNLINK(mDefaultInsertionPoint)
  136. NS_IMPL_CYCLE_COLLECTION_UNLINK(mInsertionPoints)
  137. NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnonymousContentList)
  138. NS_IMPL_CYCLE_COLLECTION_UNLINK_END
  139. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXBLBinding)
  140. NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
  141. "mPrototypeBinding->XBLDocumentInfo()");
  142. cb.NoteXPCOMChild(tmp->mPrototypeBinding->XBLDocumentInfo());
  143. NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContent)
  144. NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNextBinding)
  145. NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDefaultInsertionPoint)
  146. NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInsertionPoints)
  147. NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnonymousContentList)
  148. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
  149. NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsXBLBinding, AddRef)
  150. NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsXBLBinding, Release)
  151. void
  152. nsXBLBinding::SetBaseBinding(nsXBLBinding* aBinding)
  153. {
  154. if (mNextBinding) {
  155. NS_ERROR("Base XBL binding is already defined!");
  156. return;
  157. }
  158. mNextBinding = aBinding; // Comptr handles rel/add
  159. }
  160. nsXBLBinding*
  161. nsXBLBinding::GetBindingWithContent()
  162. {
  163. if (mContent) {
  164. return this;
  165. }
  166. return mNextBinding ? mNextBinding->GetBindingWithContent() : nullptr;
  167. }
  168. void
  169. nsXBLBinding::InstallAnonymousContent(nsIContent* aAnonParent, nsIContent* aElement,
  170. bool aChromeOnlyContent)
  171. {
  172. // We need to ensure two things.
  173. // (1) The anonymous content should be fooled into thinking it's in the bound
  174. // element's document, assuming that the bound element is in a document
  175. // Note that we don't change the current doc of aAnonParent here, since that
  176. // quite simply does not matter. aAnonParent is just a way of keeping refs
  177. // to all its kids, which are anonymous content from the point of view of
  178. // aElement.
  179. // (2) The children's parent back pointer should not be to this synthetic root
  180. // but should instead point to the enclosing parent element.
  181. nsIDocument* doc = aElement->GetUncomposedDoc();
  182. bool allowScripts = AllowScripts();
  183. nsAutoScriptBlocker scriptBlocker;
  184. for (nsIContent* child = aAnonParent->GetFirstChild();
  185. child;
  186. child = child->GetNextSibling()) {
  187. child->UnbindFromTree();
  188. if (aChromeOnlyContent) {
  189. child->SetFlags(NODE_CHROME_ONLY_ACCESS |
  190. NODE_IS_ROOT_OF_CHROME_ONLY_ACCESS);
  191. }
  192. nsresult rv =
  193. child->BindToTree(doc, aElement, mBoundElement, allowScripts);
  194. if (NS_FAILED(rv)) {
  195. // Oh, well... Just give up.
  196. // XXXbz This really shouldn't be a void method!
  197. child->UnbindFromTree();
  198. return;
  199. }
  200. child->SetFlags(NODE_IS_ANONYMOUS_ROOT);
  201. #ifdef MOZ_XUL
  202. // To make XUL templates work (and other goodies that happen when
  203. // an element is added to a XUL document), we need to notify the
  204. // XUL document using its special API.
  205. nsCOMPtr<nsIXULDocument> xuldoc(do_QueryInterface(doc));
  206. if (xuldoc)
  207. xuldoc->AddSubtreeToDocument(child);
  208. #endif
  209. }
  210. }
  211. void
  212. nsXBLBinding::UninstallAnonymousContent(nsIDocument* aDocument,
  213. nsIContent* aAnonParent)
  214. {
  215. nsAutoScriptBlocker scriptBlocker;
  216. // Hold a strong ref while doing this, just in case.
  217. nsCOMPtr<nsIContent> anonParent = aAnonParent;
  218. #ifdef MOZ_XUL
  219. nsCOMPtr<nsIXULDocument> xuldoc =
  220. do_QueryInterface(aDocument);
  221. #endif
  222. for (nsIContent* child = aAnonParent->GetFirstChild();
  223. child;
  224. child = child->GetNextSibling()) {
  225. child->UnbindFromTree();
  226. #ifdef MOZ_XUL
  227. if (xuldoc) {
  228. xuldoc->RemoveSubtreeFromDocument(child);
  229. }
  230. #endif
  231. }
  232. }
  233. void
  234. nsXBLBinding::SetBoundElement(nsIContent* aElement)
  235. {
  236. mBoundElement = aElement;
  237. if (mNextBinding)
  238. mNextBinding->SetBoundElement(aElement);
  239. if (!mBoundElement) {
  240. return;
  241. }
  242. // Compute whether we're using an XBL scope.
  243. //
  244. // We disable XBL scopes for remote XUL, where we care about compat more
  245. // than security. So we need to know whether we're using an XBL scope so that
  246. // we can decide what to do about untrusted events when "allowuntrusted"
  247. // is not given in the handler declaration.
  248. nsCOMPtr<nsIGlobalObject> go = mBoundElement->OwnerDoc()->GetScopeObject();
  249. NS_ENSURE_TRUE_VOID(go && go->GetGlobalJSObject());
  250. mUsingContentXBLScope = xpc::UseContentXBLScope(js::GetObjectCompartment(go->GetGlobalJSObject()));
  251. }
  252. bool
  253. nsXBLBinding::HasStyleSheets() const
  254. {
  255. // Find out if we need to re-resolve style. We'll need to do this
  256. // if we have additional stylesheets in our binding document.
  257. if (mPrototypeBinding->HasStyleSheets())
  258. return true;
  259. return mNextBinding ? mNextBinding->HasStyleSheets() : false;
  260. }
  261. void
  262. nsXBLBinding::GenerateAnonymousContent()
  263. {
  264. NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
  265. "Someone forgot a script blocker");
  266. // Fetch the content element for this binding.
  267. nsIContent* content =
  268. mPrototypeBinding->GetImmediateChild(nsGkAtoms::content);
  269. if (!content) {
  270. // We have no anonymous content.
  271. if (mNextBinding)
  272. mNextBinding->GenerateAnonymousContent();
  273. return;
  274. }
  275. // Find out if we're really building kids or if we're just
  276. // using the attribute-setting shorthand hack.
  277. uint32_t contentCount = content->GetChildCount();
  278. // Plan to build the content by default.
  279. bool hasContent = (contentCount > 0);
  280. if (hasContent) {
  281. nsIDocument* doc = mBoundElement->OwnerDoc();
  282. nsCOMPtr<nsINode> clonedNode;
  283. nsCOMArray<nsINode> nodesWithProperties;
  284. nsNodeUtils::Clone(content, true, doc->NodeInfoManager(),
  285. nodesWithProperties, getter_AddRefs(clonedNode));
  286. mContent = clonedNode->AsElement();
  287. // Search for <xbl:children> elements in the XBL content. In the presence
  288. // of multiple default insertion points, we use the last one in document
  289. // order.
  290. for (nsIContent* child = mContent; child; child = child->GetNextNode(mContent)) {
  291. if (child->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
  292. XBLChildrenElement* point = static_cast<XBLChildrenElement*>(child);
  293. if (point->IsDefaultInsertion()) {
  294. mDefaultInsertionPoint = point;
  295. } else {
  296. mInsertionPoints.AppendElement(point);
  297. }
  298. }
  299. }
  300. // Do this after looking for <children> as this messes up the parent
  301. // pointer which would make the GetNextNode call above fail
  302. InstallAnonymousContent(mContent, mBoundElement,
  303. mPrototypeBinding->ChromeOnlyContent());
  304. // Insert explicit children into insertion points
  305. if (mDefaultInsertionPoint && mInsertionPoints.IsEmpty()) {
  306. ExplicitChildIterator iter(mBoundElement);
  307. for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
  308. mDefaultInsertionPoint->AppendInsertedChild(child);
  309. }
  310. } else {
  311. // It is odd to come into this code if mInsertionPoints is not empty, but
  312. // we need to make sure to do the compatibility hack below if the bound
  313. // node has any non <xul:template> or <xul:observes> children.
  314. ExplicitChildIterator iter(mBoundElement);
  315. for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
  316. XBLChildrenElement* point = FindInsertionPointForInternal(child);
  317. if (point) {
  318. point->AppendInsertedChild(child);
  319. } else {
  320. NodeInfo *ni = child->NodeInfo();
  321. if (ni->NamespaceID() != kNameSpaceID_XUL ||
  322. (!ni->Equals(nsGkAtoms::_template) &&
  323. !ni->Equals(nsGkAtoms::observes))) {
  324. // Compatibility hack. For some reason the original XBL
  325. // implementation dropped the content of a binding if any child of
  326. // the bound element didn't match any of the <children> in the
  327. // binding. This became a pseudo-API that we have to maintain.
  328. // Undo InstallAnonymousContent
  329. UninstallAnonymousContent(doc, mContent);
  330. // Clear out our children elements to avoid dangling references.
  331. ClearInsertionPoints();
  332. // Pretend as though there was no content in the binding.
  333. mContent = nullptr;
  334. return;
  335. }
  336. }
  337. }
  338. }
  339. // Set binding parent on default content if need
  340. if (mDefaultInsertionPoint) {
  341. mDefaultInsertionPoint->MaybeSetupDefaultContent();
  342. }
  343. for (uint32_t i = 0; i < mInsertionPoints.Length(); ++i) {
  344. mInsertionPoints[i]->MaybeSetupDefaultContent();
  345. }
  346. mPrototypeBinding->SetInitialAttributes(mBoundElement, mContent);
  347. }
  348. // Always check the content element for potential attributes.
  349. // This shorthand hack always happens, even when we didn't
  350. // build anonymous content.
  351. BorrowedAttrInfo attrInfo;
  352. for (uint32_t i = 0; (attrInfo = content->GetAttrInfoAt(i)); ++i) {
  353. int32_t namespaceID = attrInfo.mName->NamespaceID();
  354. // Hold a strong reference here so that the atom doesn't go away during
  355. // UnsetAttr.
  356. nsCOMPtr<nsIAtom> name = attrInfo.mName->LocalName();
  357. if (name != nsGkAtoms::includes) {
  358. if (!nsContentUtils::HasNonEmptyAttr(mBoundElement, namespaceID, name)) {
  359. nsAutoString value2;
  360. attrInfo.mValue->ToString(value2);
  361. mBoundElement->SetAttr(namespaceID, name, attrInfo.mName->GetPrefix(),
  362. value2, false);
  363. }
  364. }
  365. // Conserve space by wiping the attributes off the clone.
  366. if (mContent)
  367. mContent->UnsetAttr(namespaceID, name, false);
  368. }
  369. // Now that we've finished shuffling the tree around, go ahead and restyle it
  370. // since frame construction is about to happen.
  371. nsIPresShell* presShell = mBoundElement->OwnerDoc()->GetShell();
  372. ServoStyleSet* servoSet = presShell->StyleSet()->GetAsServo();
  373. if (servoSet) {
  374. mBoundElement->SetHasDirtyDescendantsForServo();
  375. servoSet->StyleNewChildren(mBoundElement);
  376. }
  377. }
  378. nsIURI*
  379. nsXBLBinding::GetSourceDocURI()
  380. {
  381. nsIContent* targetContent =
  382. mPrototypeBinding->GetImmediateChild(nsGkAtoms::content);
  383. if (!targetContent) {
  384. return nullptr;
  385. }
  386. return targetContent->OwnerDoc()->GetDocumentURI();
  387. }
  388. XBLChildrenElement*
  389. nsXBLBinding::FindInsertionPointFor(nsIContent* aChild)
  390. {
  391. // XXX We should get rid of this function as it causes us to traverse the
  392. // binding chain multiple times
  393. if (mContent) {
  394. return FindInsertionPointForInternal(aChild);
  395. }
  396. return mNextBinding ? mNextBinding->FindInsertionPointFor(aChild)
  397. : nullptr;
  398. }
  399. XBLChildrenElement*
  400. nsXBLBinding::FindInsertionPointForInternal(nsIContent* aChild)
  401. {
  402. for (uint32_t i = 0; i < mInsertionPoints.Length(); ++i) {
  403. XBLChildrenElement* point = mInsertionPoints[i];
  404. if (point->Includes(aChild)) {
  405. return point;
  406. }
  407. }
  408. return mDefaultInsertionPoint;
  409. }
  410. void
  411. nsXBLBinding::ClearInsertionPoints()
  412. {
  413. if (mDefaultInsertionPoint) {
  414. mDefaultInsertionPoint->ClearInsertedChildren();
  415. }
  416. for (uint32_t i = 0; i < mInsertionPoints.Length(); ++i) {
  417. mInsertionPoints[i]->ClearInsertedChildren();
  418. }
  419. }
  420. nsAnonymousContentList*
  421. nsXBLBinding::GetAnonymousNodeList()
  422. {
  423. if (!mContent) {
  424. return mNextBinding ? mNextBinding->GetAnonymousNodeList() : nullptr;
  425. }
  426. if (!mAnonymousContentList) {
  427. mAnonymousContentList = new nsAnonymousContentList(mContent);
  428. }
  429. return mAnonymousContentList;
  430. }
  431. void
  432. nsXBLBinding::InstallEventHandlers()
  433. {
  434. // Don't install handlers if scripts aren't allowed.
  435. if (AllowScripts()) {
  436. // Fetch the handlers prototypes for this binding.
  437. nsXBLPrototypeHandler* handlerChain = mPrototypeBinding->GetPrototypeHandlers();
  438. if (handlerChain) {
  439. EventListenerManager* manager = mBoundElement->GetOrCreateListenerManager();
  440. if (!manager)
  441. return;
  442. bool isChromeDoc =
  443. nsContentUtils::IsChromeDoc(mBoundElement->OwnerDoc());
  444. bool isChromeBinding = mPrototypeBinding->IsChrome();
  445. nsXBLPrototypeHandler* curr;
  446. for (curr = handlerChain; curr; curr = curr->GetNextHandler()) {
  447. // Fetch the event type.
  448. nsCOMPtr<nsIAtom> eventAtom = curr->GetEventName();
  449. if (!eventAtom ||
  450. eventAtom == nsGkAtoms::keyup ||
  451. eventAtom == nsGkAtoms::keydown ||
  452. eventAtom == nsGkAtoms::keypress)
  453. continue;
  454. nsXBLEventHandler* handler = curr->GetEventHandler();
  455. if (handler) {
  456. // Figure out if we're using capturing or not.
  457. EventListenerFlags flags;
  458. flags.mCapture = (curr->GetPhase() == NS_PHASE_CAPTURING);
  459. // If this is a command, add it in the system event group
  460. if ((curr->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND |
  461. NS_HANDLER_TYPE_SYSTEM)) &&
  462. (isChromeBinding || mBoundElement->IsInNativeAnonymousSubtree())) {
  463. flags.mInSystemGroup = true;
  464. }
  465. bool hasAllowUntrustedAttr = curr->HasAllowUntrustedAttr();
  466. if ((hasAllowUntrustedAttr && curr->AllowUntrustedEvents()) ||
  467. (!hasAllowUntrustedAttr && !isChromeDoc && !mUsingContentXBLScope)) {
  468. flags.mAllowUntrustedEvents = true;
  469. }
  470. manager->AddEventListenerByType(handler,
  471. nsDependentAtomString(eventAtom),
  472. flags);
  473. }
  474. }
  475. const nsCOMArray<nsXBLKeyEventHandler>* keyHandlers =
  476. mPrototypeBinding->GetKeyEventHandlers();
  477. int32_t i;
  478. for (i = 0; i < keyHandlers->Count(); ++i) {
  479. nsXBLKeyEventHandler* handler = keyHandlers->ObjectAt(i);
  480. handler->SetIsBoundToChrome(isChromeDoc);
  481. handler->SetUsingContentXBLScope(mUsingContentXBLScope);
  482. nsAutoString type;
  483. handler->GetEventName(type);
  484. // If this is a command, add it in the system event group, otherwise
  485. // add it to the standard event group.
  486. // Figure out if we're using capturing or not.
  487. EventListenerFlags flags;
  488. flags.mCapture = (handler->GetPhase() == NS_PHASE_CAPTURING);
  489. if ((handler->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND |
  490. NS_HANDLER_TYPE_SYSTEM)) &&
  491. (isChromeBinding || mBoundElement->IsInNativeAnonymousSubtree())) {
  492. flags.mInSystemGroup = true;
  493. }
  494. // For key handlers we have to set mAllowUntrustedEvents flag.
  495. // Whether the handling of the event is allowed or not is handled in
  496. // nsXBLKeyEventHandler::HandleEvent
  497. flags.mAllowUntrustedEvents = true;
  498. manager->AddEventListenerByType(handler, type, flags);
  499. }
  500. }
  501. }
  502. if (mNextBinding)
  503. mNextBinding->InstallEventHandlers();
  504. }
  505. nsresult
  506. nsXBLBinding::InstallImplementation()
  507. {
  508. // Always install the base class properties first, so that
  509. // derived classes can reference the base class properties.
  510. if (mNextBinding) {
  511. nsresult rv = mNextBinding->InstallImplementation();
  512. NS_ENSURE_SUCCESS(rv, rv);
  513. }
  514. // iterate through each property in the prototype's list and install the property.
  515. if (AllowScripts())
  516. return mPrototypeBinding->InstallImplementation(this);
  517. return NS_OK;
  518. }
  519. nsIAtom*
  520. nsXBLBinding::GetBaseTag(int32_t* aNameSpaceID)
  521. {
  522. nsIAtom *tag = mPrototypeBinding->GetBaseTag(aNameSpaceID);
  523. if (!tag && mNextBinding)
  524. return mNextBinding->GetBaseTag(aNameSpaceID);
  525. return tag;
  526. }
  527. void
  528. nsXBLBinding::AttributeChanged(nsIAtom* aAttribute, int32_t aNameSpaceID,
  529. bool aRemoveFlag, bool aNotify)
  530. {
  531. // XXX Change if we ever allow multiple bindings in a chain to contribute anonymous content
  532. if (!mContent) {
  533. if (mNextBinding)
  534. mNextBinding->AttributeChanged(aAttribute, aNameSpaceID,
  535. aRemoveFlag, aNotify);
  536. } else {
  537. mPrototypeBinding->AttributeChanged(aAttribute, aNameSpaceID, aRemoveFlag,
  538. mBoundElement, mContent, aNotify);
  539. }
  540. }
  541. void
  542. nsXBLBinding::ExecuteAttachedHandler()
  543. {
  544. if (mNextBinding)
  545. mNextBinding->ExecuteAttachedHandler();
  546. if (AllowScripts())
  547. mPrototypeBinding->BindingAttached(mBoundElement);
  548. }
  549. void
  550. nsXBLBinding::ExecuteDetachedHandler()
  551. {
  552. if (AllowScripts())
  553. mPrototypeBinding->BindingDetached(mBoundElement);
  554. if (mNextBinding)
  555. mNextBinding->ExecuteDetachedHandler();
  556. }
  557. void
  558. nsXBLBinding::UnhookEventHandlers()
  559. {
  560. nsXBLPrototypeHandler* handlerChain = mPrototypeBinding->GetPrototypeHandlers();
  561. if (handlerChain) {
  562. EventListenerManager* manager = mBoundElement->GetExistingListenerManager();
  563. if (!manager) {
  564. return;
  565. }
  566. bool isChromeBinding = mPrototypeBinding->IsChrome();
  567. nsXBLPrototypeHandler* curr;
  568. for (curr = handlerChain; curr; curr = curr->GetNextHandler()) {
  569. nsXBLEventHandler* handler = curr->GetCachedEventHandler();
  570. if (!handler) {
  571. continue;
  572. }
  573. nsCOMPtr<nsIAtom> eventAtom = curr->GetEventName();
  574. if (!eventAtom ||
  575. eventAtom == nsGkAtoms::keyup ||
  576. eventAtom == nsGkAtoms::keydown ||
  577. eventAtom == nsGkAtoms::keypress)
  578. continue;
  579. // Figure out if we're using capturing or not.
  580. EventListenerFlags flags;
  581. flags.mCapture = (curr->GetPhase() == NS_PHASE_CAPTURING);
  582. // If this is a command, remove it from the system event group,
  583. // otherwise remove it from the standard event group.
  584. if ((curr->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND |
  585. NS_HANDLER_TYPE_SYSTEM)) &&
  586. (isChromeBinding || mBoundElement->IsInNativeAnonymousSubtree())) {
  587. flags.mInSystemGroup = true;
  588. }
  589. manager->RemoveEventListenerByType(handler,
  590. nsDependentAtomString(eventAtom),
  591. flags);
  592. }
  593. const nsCOMArray<nsXBLKeyEventHandler>* keyHandlers =
  594. mPrototypeBinding->GetKeyEventHandlers();
  595. int32_t i;
  596. for (i = 0; i < keyHandlers->Count(); ++i) {
  597. nsXBLKeyEventHandler* handler = keyHandlers->ObjectAt(i);
  598. nsAutoString type;
  599. handler->GetEventName(type);
  600. // Figure out if we're using capturing or not.
  601. EventListenerFlags flags;
  602. flags.mCapture = (handler->GetPhase() == NS_PHASE_CAPTURING);
  603. // If this is a command, remove it from the system event group, otherwise
  604. // remove it from the standard event group.
  605. if ((handler->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND | NS_HANDLER_TYPE_SYSTEM)) &&
  606. (isChromeBinding || mBoundElement->IsInNativeAnonymousSubtree())) {
  607. flags.mInSystemGroup = true;
  608. }
  609. manager->RemoveEventListenerByType(handler, type, flags);
  610. }
  611. }
  612. }
  613. static void
  614. UpdateInsertionParent(XBLChildrenElement* aPoint,
  615. nsIContent* aOldBoundElement)
  616. {
  617. if (aPoint->IsDefaultInsertion()) {
  618. return;
  619. }
  620. for (size_t i = 0; i < aPoint->InsertedChildrenLength(); ++i) {
  621. nsIContent* child = aPoint->InsertedChild(i);
  622. MOZ_ASSERT(child->GetParentNode());
  623. // Here, we're iterating children that we inserted. There are two cases:
  624. // either |child| is an explicit child of |aOldBoundElement| and is no
  625. // longer inserted anywhere or it's a child of a <children> element
  626. // parented to |aOldBoundElement|. In the former case, the child is no
  627. // longer inserted anywhere, so we set its insertion parent to null. In the
  628. // latter case, the child is now inserted into |aOldBoundElement| from some
  629. // binding above us, so we set its insertion parent to aOldBoundElement.
  630. if (child->GetParentNode() == aOldBoundElement) {
  631. child->SetXBLInsertionParent(nullptr);
  632. } else {
  633. child->SetXBLInsertionParent(aOldBoundElement);
  634. }
  635. }
  636. }
  637. void
  638. nsXBLBinding::ChangeDocument(nsIDocument* aOldDocument, nsIDocument* aNewDocument)
  639. {
  640. if (aOldDocument == aNewDocument)
  641. return;
  642. // Now the binding dies. Unhook our prototypes.
  643. if (mPrototypeBinding->HasImplementation()) {
  644. AutoJSAPI jsapi;
  645. // Init might fail here if we've cycle-collected the global object, since
  646. // the Unlink phase of cycle collection happens after JS GC finalization.
  647. // But in that case, we don't care about fixing the prototype chain, since
  648. // everything's going away immediately.
  649. if (jsapi.Init(aOldDocument->GetScopeObject())) {
  650. JSContext* cx = jsapi.cx();
  651. JS::Rooted<JSObject*> scriptObject(cx, mBoundElement->GetWrapper());
  652. if (scriptObject) {
  653. // XXX Stay in sync! What if a layered binding has an
  654. // <interface>?!
  655. // XXXbz what does that comment mean, really? It seems to date
  656. // back to when there was such a thing as an <interface>, whever
  657. // that was...
  658. // Find the right prototype.
  659. JSAutoCompartment ac(cx, scriptObject);
  660. JS::Rooted<JSObject*> base(cx, scriptObject);
  661. JS::Rooted<JSObject*> proto(cx);
  662. for ( ; true; base = proto) { // Will break out on null proto
  663. if (!JS_GetPrototype(cx, base, &proto)) {
  664. return;
  665. }
  666. if (!proto) {
  667. break;
  668. }
  669. if (JS_GetClass(proto) != &gPrototypeJSClass) {
  670. // Clearly not the right class
  671. continue;
  672. }
  673. RefPtr<nsXBLDocumentInfo> docInfo =
  674. static_cast<nsXBLDocumentInfo*>(::JS_GetPrivate(proto));
  675. if (!docInfo) {
  676. // Not the proto we seek
  677. continue;
  678. }
  679. JS::Value protoBinding = ::JS_GetReservedSlot(proto, 0);
  680. if (protoBinding.toPrivate() != mPrototypeBinding) {
  681. // Not the right binding
  682. continue;
  683. }
  684. // Alright! This is the right prototype. Pull it out of the
  685. // proto chain.
  686. JS::Rooted<JSObject*> grandProto(cx);
  687. if (!JS_GetPrototype(cx, proto, &grandProto)) {
  688. return;
  689. }
  690. ::JS_SetPrototype(cx, base, grandProto);
  691. break;
  692. }
  693. mPrototypeBinding->UndefineFields(cx, scriptObject);
  694. // Don't remove the reference from the document to the
  695. // wrapper here since it'll be removed by the element
  696. // itself when that's taken out of the document.
  697. }
  698. }
  699. }
  700. // Remove our event handlers
  701. UnhookEventHandlers();
  702. {
  703. nsAutoScriptBlocker scriptBlocker;
  704. // Then do our ancestors. This reverses the construction order, so that at
  705. // all times things are consistent as far as everyone is concerned.
  706. if (mNextBinding) {
  707. mNextBinding->ChangeDocument(aOldDocument, aNewDocument);
  708. }
  709. // Update the anonymous content.
  710. // XXXbz why not only for style bindings?
  711. if (mContent && !mIsShadowRootBinding) {
  712. nsXBLBinding::UninstallAnonymousContent(aOldDocument, mContent);
  713. }
  714. // Now that we've unbound our anonymous content from the tree and updated
  715. // its binding parent, update the insertion parent for content inserted
  716. // into our <children> elements.
  717. if (mDefaultInsertionPoint) {
  718. UpdateInsertionParent(mDefaultInsertionPoint, mBoundElement);
  719. }
  720. for (size_t i = 0; i < mInsertionPoints.Length(); ++i) {
  721. UpdateInsertionParent(mInsertionPoints[i], mBoundElement);
  722. }
  723. // Now that our inserted children no longer think they're inserted
  724. // anywhere, make sure our internal state reflects that as well.
  725. ClearInsertionPoints();
  726. }
  727. }
  728. bool
  729. nsXBLBinding::InheritsStyle() const
  730. {
  731. // XXX Will have to change if we ever allow multiple bindings to contribute anonymous content.
  732. // Most derived binding with anonymous content determines style inheritance for now.
  733. // XXX What about bindings with <content> but no kids, e.g., my treecell-text binding?
  734. if (mContent)
  735. return mPrototypeBinding->InheritsStyle();
  736. if (mNextBinding)
  737. return mNextBinding->InheritsStyle();
  738. return true;
  739. }
  740. void
  741. nsXBLBinding::WalkRules(nsIStyleRuleProcessor::EnumFunc aFunc, void* aData)
  742. {
  743. if (mNextBinding)
  744. mNextBinding->WalkRules(aFunc, aData);
  745. nsIStyleRuleProcessor *rules = mPrototypeBinding->GetRuleProcessor();
  746. if (rules)
  747. (*aFunc)(rules, aData);
  748. }
  749. // Internal helper methods ////////////////////////////////////////////////////////////////
  750. // Get or create a WeakMap object on a given XBL-hosting global.
  751. //
  752. // The scheme is as follows. XBL-hosting globals (either privileged content
  753. // Windows or XBL scopes) get two lazily-defined WeakMap properties. Each
  754. // WeakMap is keyed by the grand-proto - i.e. the original prototype of the
  755. // content before it was bound, and the prototype of the class object that we
  756. // splice in. The values in the WeakMap are simple dictionary-style objects,
  757. // mapping from XBL class names to class objects.
  758. static JSObject*
  759. GetOrCreateClassObjectMap(JSContext *cx, JS::Handle<JSObject*> scope, const char *mapName)
  760. {
  761. AssertSameCompartment(cx, scope);
  762. MOZ_ASSERT(JS_IsGlobalObject(scope));
  763. MOZ_ASSERT(scope == xpc::GetXBLScopeOrGlobal(cx, scope));
  764. // First, see if the map is already defined.
  765. JS::Rooted<JS::PropertyDescriptor> desc(cx);
  766. if (!JS_GetOwnPropertyDescriptor(cx, scope, mapName, &desc)) {
  767. return nullptr;
  768. }
  769. if (desc.object() && desc.value().isObject() &&
  770. JS::IsWeakMapObject(&desc.value().toObject())) {
  771. return &desc.value().toObject();
  772. }
  773. // It's not there. Create and define it.
  774. JS::Rooted<JSObject*> map(cx, JS::NewWeakMapObject(cx));
  775. if (!map || !JS_DefineProperty(cx, scope, mapName, map,
  776. JSPROP_PERMANENT | JSPROP_READONLY,
  777. JS_STUBGETTER, JS_STUBSETTER))
  778. {
  779. return nullptr;
  780. }
  781. return map;
  782. }
  783. static JSObject*
  784. GetOrCreateMapEntryForPrototype(JSContext *cx, JS::Handle<JSObject*> proto)
  785. {
  786. AssertSameCompartment(cx, proto);
  787. // We want to hang our class objects off the XBL scope. But since we also
  788. // hoist anonymous content into the XBL scope, this creates the potential for
  789. // tricky collisions, since we can simultaneously have a bound in-content
  790. // node with grand-proto HTMLDivElement and a bound anonymous node whose
  791. // grand-proto is the XBL scope's cross-compartment wrapper to HTMLDivElement.
  792. // Since we have to wrap the WeakMap keys into its scope, this distinction
  793. // would be lost if we don't do something about it.
  794. //
  795. // So we define two maps - one class objects that live in content (prototyped
  796. // to content prototypes), and the other for class objects that live in the
  797. // XBL scope (prototyped to cross-compartment-wrapped content prototypes).
  798. const char* name = xpc::IsInContentXBLScope(proto) ? "__ContentClassObjectMap__"
  799. : "__XBLClassObjectMap__";
  800. // Now, enter the XBL scope, since that's where we need to operate, and wrap
  801. // the proto accordingly. We hang the map off of the content XBL scope for
  802. // content, and the Window for chrome (whether add-ons are involved or not).
  803. JS::Rooted<JSObject*> scope(cx, xpc::GetXBLScopeOrGlobal(cx, proto));
  804. NS_ENSURE_TRUE(scope, nullptr);
  805. MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(scope) == scope);
  806. JS::Rooted<JSObject*> wrappedProto(cx, proto);
  807. JSAutoCompartment ac(cx, scope);
  808. if (!JS_WrapObject(cx, &wrappedProto)) {
  809. return nullptr;
  810. }
  811. // Grab the appropriate WeakMap.
  812. JS::Rooted<JSObject*> map(cx, GetOrCreateClassObjectMap(cx, scope, name));
  813. if (!map) {
  814. return nullptr;
  815. }
  816. // See if we already have a map entry for that prototype.
  817. JS::Rooted<JS::Value> val(cx);
  818. if (!JS::GetWeakMapEntry(cx, map, wrappedProto, &val)) {
  819. return nullptr;
  820. }
  821. if (val.isObject()) {
  822. return &val.toObject();
  823. }
  824. // We don't have an entry. Create one and stick it in the map.
  825. JS::Rooted<JSObject*> entry(cx);
  826. entry = JS_NewObjectWithGivenProto(cx, nullptr, nullptr);
  827. if (!entry) {
  828. return nullptr;
  829. }
  830. JS::Rooted<JS::Value> entryVal(cx, JS::ObjectValue(*entry));
  831. if (!JS::SetWeakMapEntry(cx, map, wrappedProto, entryVal)) {
  832. NS_WARNING("SetWeakMapEntry failed, probably due to non-preservable WeakMap "
  833. "key. XBL binding will fail for this element.");
  834. return nullptr;
  835. }
  836. return entry;
  837. }
  838. static
  839. nsXBLPrototypeBinding*
  840. GetProtoBindingFromClassObject(JSObject* obj)
  841. {
  842. MOZ_ASSERT(JS_GetClass(obj) == &gPrototypeJSClass);
  843. return static_cast<nsXBLPrototypeBinding*>(::JS_GetReservedSlot(obj, 0).toPrivate());
  844. }
  845. // static
  846. nsresult
  847. nsXBLBinding::DoInitJSClass(JSContext *cx,
  848. JS::Handle<JSObject*> obj,
  849. const nsAFlatString& aClassName,
  850. nsXBLPrototypeBinding* aProtoBinding,
  851. JS::MutableHandle<JSObject*> aClassObject,
  852. bool* aNew)
  853. {
  854. MOZ_ASSERT(obj);
  855. // Note that, now that NAC reflectors are created in the XBL scope, the
  856. // reflector is not necessarily same-compartment with the document. So we'll
  857. // end up creating a separate instance of the oddly-named XBL class object
  858. // and defining it as a property on the XBL scope's global. This works fine,
  859. // but we need to make sure never to assume that the the reflector and
  860. // prototype are same-compartment with the bound document.
  861. JS::Rooted<JSObject*> global(cx, js::GetGlobalForObjectCrossCompartment(obj));
  862. // We never store class objects in add-on scopes.
  863. JS::Rooted<JSObject*> xblScope(cx, xpc::GetXBLScopeOrGlobal(cx, global));
  864. NS_ENSURE_TRUE(xblScope, NS_ERROR_UNEXPECTED);
  865. JS::Rooted<JSObject*> parent_proto(cx);
  866. {
  867. JS::RootedObject wrapped(cx, obj);
  868. JSAutoCompartment ac(cx, xblScope);
  869. if (!JS_WrapObject(cx, &wrapped)) {
  870. return NS_ERROR_FAILURE;
  871. }
  872. if (!JS_GetPrototype(cx, wrapped, &parent_proto)) {
  873. return NS_ERROR_FAILURE;
  874. }
  875. }
  876. if (!JS_WrapObject(cx, &parent_proto)) {
  877. return NS_ERROR_FAILURE;
  878. }
  879. // Get the map entry for the parent prototype. In the one-off case that the
  880. // parent prototype is null, we somewhat hackily just use the WeakMap itself
  881. // as a property holder.
  882. JS::Rooted<JSObject*> holder(cx);
  883. if (parent_proto) {
  884. holder = GetOrCreateMapEntryForPrototype(cx, parent_proto);
  885. } else {
  886. JSAutoCompartment innerAC(cx, xblScope);
  887. holder = GetOrCreateClassObjectMap(cx, xblScope, "__ContentClassObjectMap__");
  888. }
  889. if (NS_WARN_IF(!holder)) {
  890. return NS_ERROR_FAILURE;
  891. }
  892. js::AssertSameCompartment(holder, xblScope);
  893. JSAutoCompartment ac(cx, holder);
  894. // Look up the class on the property holder. The only properties on the
  895. // holder should be class objects. If we don't find the class object, we need
  896. // to create and define it.
  897. JS::Rooted<JSObject*> proto(cx);
  898. JS::Rooted<JS::PropertyDescriptor> desc(cx);
  899. if (!JS_GetOwnUCPropertyDescriptor(cx, holder, aClassName.get(),
  900. aClassName.Length(), &desc)) {
  901. return NS_ERROR_OUT_OF_MEMORY;
  902. }
  903. *aNew = !desc.object();
  904. if (desc.object()) {
  905. proto = &desc.value().toObject();
  906. DebugOnly<nsXBLPrototypeBinding*> cachedBinding =
  907. GetProtoBindingFromClassObject(js::UncheckedUnwrap(proto));
  908. MOZ_ASSERT(cachedBinding == aProtoBinding);
  909. } else {
  910. // We need to create the prototype. First, enter the compartment where it's
  911. // going to live, and create it.
  912. JSAutoCompartment ac2(cx, global);
  913. proto = JS_NewObjectWithGivenProto(cx, &gPrototypeJSClass, parent_proto);
  914. if (!proto) {
  915. return NS_ERROR_OUT_OF_MEMORY;
  916. }
  917. // Keep this proto binding alive while we're alive. Do this first so that
  918. // we can guarantee that in XBLFinalize this will be non-null.
  919. // Note that we can't just store aProtoBinding in the private and
  920. // addref/release the nsXBLDocumentInfo through it, because cycle
  921. // collection doesn't seem to work right if the private is not an
  922. // nsISupports.
  923. nsXBLDocumentInfo* docInfo = aProtoBinding->XBLDocumentInfo();
  924. ::JS_SetPrivate(proto, docInfo);
  925. NS_ADDREF(docInfo);
  926. JS_SetReservedSlot(proto, 0, JS::PrivateValue(aProtoBinding));
  927. // Next, enter the compartment of the property holder, wrap the proto, and
  928. // stick it on.
  929. JSAutoCompartment ac3(cx, holder);
  930. if (!JS_WrapObject(cx, &proto) ||
  931. !JS_DefineUCProperty(cx, holder, aClassName.get(), -1, proto,
  932. JSPROP_READONLY | JSPROP_PERMANENT,
  933. JS_STUBGETTER, JS_STUBSETTER))
  934. {
  935. return NS_ERROR_OUT_OF_MEMORY;
  936. }
  937. }
  938. // Whew. We have the proto. Wrap it back into the compartment of |obj|,
  939. // splice it in, and return it.
  940. JSAutoCompartment ac4(cx, obj);
  941. if (!JS_WrapObject(cx, &proto) || !JS_SetPrototype(cx, obj, proto)) {
  942. return NS_ERROR_FAILURE;
  943. }
  944. aClassObject.set(proto);
  945. return NS_OK;
  946. }
  947. bool
  948. nsXBLBinding::AllowScripts()
  949. {
  950. return mBoundElement && mPrototypeBinding->GetAllowScripts();
  951. }
  952. nsXBLBinding*
  953. nsXBLBinding::RootBinding()
  954. {
  955. if (mNextBinding)
  956. return mNextBinding->RootBinding();
  957. return this;
  958. }
  959. bool
  960. nsXBLBinding::ResolveAllFields(JSContext *cx, JS::Handle<JSObject*> obj) const
  961. {
  962. if (!mPrototypeBinding->ResolveAllFields(cx, obj)) {
  963. return false;
  964. }
  965. if (mNextBinding) {
  966. return mNextBinding->ResolveAllFields(cx, obj);
  967. }
  968. return true;
  969. }
  970. bool
  971. nsXBLBinding::LookupMember(JSContext* aCx, JS::Handle<jsid> aId,
  972. JS::MutableHandle<JS::PropertyDescriptor> aDesc)
  973. {
  974. // We should never enter this function with a pre-filled property descriptor.
  975. MOZ_ASSERT(!aDesc.object());
  976. // Get the string as an nsString before doing anything, so we can make
  977. // convenient comparisons during our search.
  978. if (!JSID_IS_STRING(aId)) {
  979. return true;
  980. }
  981. nsAutoJSString name;
  982. if (!name.init(aCx, JSID_TO_STRING(aId))) {
  983. return false;
  984. }
  985. // We have a weak reference to our bound element, so make sure it's alive.
  986. if (!mBoundElement || !mBoundElement->GetWrapper()) {
  987. return false;
  988. }
  989. // Get the scope of mBoundElement and the associated XBL scope. We should only
  990. // be calling into this machinery if we're running in a separate XBL scope.
  991. //
  992. // Note that we only end up in LookupMember for XrayWrappers from XBL scopes
  993. // into content. So for NAC reflectors that live in the XBL scope, we should
  994. // never get here. But on the off-chance that someone adds new callsites to
  995. // LookupMember, we do a release-mode assertion as belt-and-braces.
  996. // We do a release-mode assertion here to be extra safe.
  997. //
  998. // This code is only called for content XBL, so we don't have to worry about
  999. // add-on scopes here.
  1000. JS::Rooted<JSObject*> boundScope(aCx,
  1001. js::GetGlobalForObjectCrossCompartment(mBoundElement->GetWrapper()));
  1002. MOZ_RELEASE_ASSERT(!xpc::IsInAddonScope(boundScope));
  1003. MOZ_RELEASE_ASSERT(!xpc::IsInContentXBLScope(boundScope));
  1004. JS::Rooted<JSObject*> xblScope(aCx, xpc::GetXBLScope(aCx, boundScope));
  1005. NS_ENSURE_TRUE(xblScope, false);
  1006. MOZ_ASSERT(boundScope != xblScope);
  1007. // Enter the xbl scope and invoke the internal version.
  1008. {
  1009. JSAutoCompartment ac(aCx, xblScope);
  1010. JS::Rooted<jsid> id(aCx, aId);
  1011. if (!LookupMemberInternal(aCx, name, id, aDesc, xblScope)) {
  1012. return false;
  1013. }
  1014. }
  1015. // Wrap into the caller's scope.
  1016. return JS_WrapPropertyDescriptor(aCx, aDesc);
  1017. }
  1018. bool
  1019. nsXBLBinding::LookupMemberInternal(JSContext* aCx, nsString& aName,
  1020. JS::Handle<jsid> aNameAsId,
  1021. JS::MutableHandle<JS::PropertyDescriptor> aDesc,
  1022. JS::Handle<JSObject*> aXBLScope)
  1023. {
  1024. // First, see if we have an implementation. If we don't, it means that this
  1025. // binding doesn't have a class object, and thus doesn't have any members.
  1026. // Skip it.
  1027. if (!PrototypeBinding()->HasImplementation()) {
  1028. if (!mNextBinding) {
  1029. return true;
  1030. }
  1031. return mNextBinding->LookupMemberInternal(aCx, aName, aNameAsId,
  1032. aDesc, aXBLScope);
  1033. }
  1034. // Find our class object. It's in a protected scope and permanent just in case,
  1035. // so should be there no matter what.
  1036. JS::Rooted<JS::Value> classObject(aCx);
  1037. if (!JS_GetUCProperty(aCx, aXBLScope, PrototypeBinding()->ClassName().get(),
  1038. -1, &classObject)) {
  1039. return false;
  1040. }
  1041. // The bound element may have been adoped by a document and have a different
  1042. // wrapper (and different xbl scope) than when the binding was applied, in
  1043. // this case getting the class object will fail. Behave as if the class
  1044. // object did not exist.
  1045. if (classObject.isUndefined()) {
  1046. return true;
  1047. }
  1048. MOZ_ASSERT(classObject.isObject());
  1049. // Look for the property on this binding. If it's not there, try the next
  1050. // binding on the chain.
  1051. nsXBLProtoImpl* impl = mPrototypeBinding->GetImplementation();
  1052. JS::Rooted<JSObject*> object(aCx, &classObject.toObject());
  1053. if (impl && !impl->LookupMember(aCx, aName, aNameAsId, aDesc, object)) {
  1054. return false;
  1055. }
  1056. if (aDesc.object() || !mNextBinding) {
  1057. return true;
  1058. }
  1059. return mNextBinding->LookupMemberInternal(aCx, aName, aNameAsId, aDesc,
  1060. aXBLScope);
  1061. }
  1062. bool
  1063. nsXBLBinding::HasField(nsString& aName)
  1064. {
  1065. // See if this binding has such a field.
  1066. return mPrototypeBinding->FindField(aName) ||
  1067. (mNextBinding && mNextBinding->HasField(aName));
  1068. }
  1069. void
  1070. nsXBLBinding::MarkForDeath()
  1071. {
  1072. mMarkedForDeath = true;
  1073. ExecuteDetachedHandler();
  1074. }
  1075. bool
  1076. nsXBLBinding::ImplementsInterface(REFNSIID aIID) const
  1077. {
  1078. return mPrototypeBinding->ImplementsInterface(aIID) ||
  1079. (mNextBinding && mNextBinding->ImplementsInterface(aIID));
  1080. }