XULDocument.cpp 152 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579
  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  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. /*
  6. An implementation for the XUL document. This implementation serves
  7. as the basis for generating an NGLayout content model.
  8. Notes
  9. -----
  10. 1. We do some monkey business in the document observer methods to
  11. keep the element map in sync for HTML elements. Why don't we just
  12. do it for _all_ elements? Well, in the case of XUL elements,
  13. which may be lazily created during frame construction, the
  14. document observer methods will never be called because we'll be
  15. adding the XUL nodes into the content model "quietly".
  16. */
  17. #include "mozilla/ArrayUtils.h"
  18. #include "XULDocument.h"
  19. #include "nsError.h"
  20. #include "nsIBoxObject.h"
  21. #include "nsIChromeRegistry.h"
  22. #include "nsView.h"
  23. #include "nsViewManager.h"
  24. #include "nsIContentViewer.h"
  25. #include "nsIDOMXULElement.h"
  26. #include "nsIStreamListener.h"
  27. #include "nsITimer.h"
  28. #include "nsDocShell.h"
  29. #include "nsGkAtoms.h"
  30. #include "nsXMLContentSink.h"
  31. #include "nsXULContentSink.h"
  32. #include "nsXULContentUtils.h"
  33. #include "nsIXULOverlayProvider.h"
  34. #include "nsIStringEnumerator.h"
  35. #include "nsNetUtil.h"
  36. #include "nsParserCIID.h"
  37. #include "nsPIBoxObject.h"
  38. #include "mozilla/dom/BoxObject.h"
  39. #include "nsXPIDLString.h"
  40. #include "nsPIDOMWindow.h"
  41. #include "nsPIWindowRoot.h"
  42. #include "nsXULCommandDispatcher.h"
  43. #include "nsXULElement.h"
  44. #include "mozilla/Logging.h"
  45. #include "rdf.h"
  46. #include "nsIFrame.h"
  47. #include "nsXBLService.h"
  48. #include "nsCExternalHandlerService.h"
  49. #include "nsMimeTypes.h"
  50. #include "nsIObjectInputStream.h"
  51. #include "nsIObjectOutputStream.h"
  52. #include "nsContentList.h"
  53. #include "nsIScriptGlobalObject.h"
  54. #include "nsIScriptSecurityManager.h"
  55. #include "nsNodeInfoManager.h"
  56. #include "nsContentCreatorFunctions.h"
  57. #include "nsContentUtils.h"
  58. #include "nsIParser.h"
  59. #include "nsCharsetSource.h"
  60. #include "mozilla/StyleSheetInlines.h"
  61. #include "mozilla/css/Loader.h"
  62. #include "nsIScriptError.h"
  63. #include "nsIStyleSheetLinkingElement.h"
  64. #include "nsIObserverService.h"
  65. #include "nsNodeUtils.h"
  66. #include "nsIDocShellTreeOwner.h"
  67. #include "nsIXULWindow.h"
  68. #include "nsXULPopupManager.h"
  69. #include "nsCCUncollectableMarker.h"
  70. #include "nsURILoader.h"
  71. #include "mozilla/AddonPathService.h"
  72. #include "mozilla/BasicEvents.h"
  73. #include "mozilla/dom/Element.h"
  74. #include "mozilla/dom/NodeInfoInlines.h"
  75. #include "mozilla/dom/ProcessingInstruction.h"
  76. #include "mozilla/dom/ScriptSettings.h"
  77. #include "mozilla/dom/XULDocumentBinding.h"
  78. #include "mozilla/EventDispatcher.h"
  79. #include "mozilla/LoadInfo.h"
  80. #include "mozilla/Preferences.h"
  81. #include "nsTextNode.h"
  82. #include "nsJSUtils.h"
  83. #include "mozilla/dom/URL.h"
  84. #include "nsIContentPolicy.h"
  85. #include "mozAutoDocUpdate.h"
  86. #include "xpcpublic.h"
  87. #include "mozilla/StyleSheet.h"
  88. #include "mozilla/StyleSheetInlines.h"
  89. using namespace mozilla;
  90. using namespace mozilla::dom;
  91. //----------------------------------------------------------------------
  92. //
  93. // CIDs
  94. //
  95. static NS_DEFINE_CID(kParserCID, NS_PARSER_CID);
  96. static bool IsOverlayAllowed(nsIURI* aURI)
  97. {
  98. bool canOverlay = false;
  99. if (NS_SUCCEEDED(aURI->SchemeIs("about", &canOverlay)) && canOverlay)
  100. return true;
  101. if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &canOverlay)) && canOverlay)
  102. return true;
  103. return false;
  104. }
  105. //----------------------------------------------------------------------
  106. //
  107. // Miscellaneous Constants
  108. //
  109. const nsForwardReference::Phase nsForwardReference::kPasses[] = {
  110. nsForwardReference::eConstruction,
  111. nsForwardReference::eHookup,
  112. nsForwardReference::eDone
  113. };
  114. //----------------------------------------------------------------------
  115. //
  116. // Statics
  117. //
  118. int32_t XULDocument::gRefCnt = 0;
  119. LazyLogModule XULDocument::gXULLog("XULDocument");
  120. //----------------------------------------------------------------------
  121. struct BroadcastListener {
  122. nsWeakPtr mListener;
  123. nsCOMPtr<nsIAtom> mAttribute;
  124. };
  125. struct BroadcasterMapEntry : public PLDHashEntryHdr
  126. {
  127. Element* mBroadcaster; // [WEAK]
  128. nsTArray<BroadcastListener*> mListeners; // [OWNING] of BroadcastListener objects
  129. };
  130. Element*
  131. nsRefMapEntry::GetFirstElement()
  132. {
  133. return mRefContentList.SafeElementAt(0);
  134. }
  135. void
  136. nsRefMapEntry::AppendAll(nsCOMArray<nsIContent>* aElements)
  137. {
  138. for (size_t i = 0; i < mRefContentList.Length(); ++i) {
  139. aElements->AppendObject(mRefContentList[i]);
  140. }
  141. }
  142. bool
  143. nsRefMapEntry::AddElement(Element* aElement)
  144. {
  145. if (mRefContentList.Contains(aElement)) {
  146. return true;
  147. }
  148. return mRefContentList.AppendElement(aElement);
  149. }
  150. bool
  151. nsRefMapEntry::RemoveElement(Element* aElement)
  152. {
  153. mRefContentList.RemoveElement(aElement);
  154. return mRefContentList.IsEmpty();
  155. }
  156. //----------------------------------------------------------------------
  157. //
  158. // ctors & dtors
  159. //
  160. namespace mozilla {
  161. namespace dom {
  162. XULDocument::XULDocument(void)
  163. : XMLDocument("application/vnd.mozilla.xul+xml"),
  164. mDocLWTheme(Doc_Theme_Uninitialized),
  165. mState(eState_Master),
  166. mResolutionPhase(nsForwardReference::eStart)
  167. {
  168. // NOTE! nsDocument::operator new() zeroes out all members, so don't
  169. // bother initializing members to 0.
  170. // Override the default in nsDocument
  171. mCharacterSet.AssignLiteral("UTF-8");
  172. mDefaultElementType = kNameSpaceID_XUL;
  173. mType = eXUL;
  174. mDelayFrameLoaderInitialization = true;
  175. mAllowXULXBL = eTriTrue;
  176. }
  177. XULDocument::~XULDocument()
  178. {
  179. NS_ASSERTION(mNextSrcLoadWaiter == nullptr,
  180. "unreferenced document still waiting for script source to load?");
  181. // In case we failed somewhere early on and the forward observer
  182. // decls never got resolved.
  183. mForwardReferences.Clear();
  184. // Likewise for any references we have to IDs where we might
  185. // look for persisted data:
  186. mPersistenceIds.Clear();
  187. // Destroy our broadcaster map.
  188. delete mBroadcasterMap;
  189. delete mTemplateBuilderTable;
  190. Preferences::UnregisterCallback(XULDocument::DirectionChanged,
  191. "intl.uidirection.", this);
  192. if (mOffThreadCompileStringBuf) {
  193. js_free(mOffThreadCompileStringBuf);
  194. }
  195. }
  196. } // namespace dom
  197. } // namespace mozilla
  198. nsresult
  199. NS_NewXULDocument(nsIXULDocument** result)
  200. {
  201. NS_PRECONDITION(result != nullptr, "null ptr");
  202. if (! result)
  203. return NS_ERROR_NULL_POINTER;
  204. RefPtr<XULDocument> doc = new XULDocument();
  205. nsresult rv;
  206. if (NS_FAILED(rv = doc->Init())) {
  207. return rv;
  208. }
  209. doc.forget(result);
  210. return NS_OK;
  211. }
  212. namespace mozilla {
  213. namespace dom {
  214. //----------------------------------------------------------------------
  215. //
  216. // nsISupports interface
  217. //
  218. NS_IMPL_CYCLE_COLLECTION_CLASS(XULDocument)
  219. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XULDocument, XMLDocument)
  220. NS_ASSERTION(!nsCCUncollectableMarker::InGeneration(cb, tmp->GetMarkedCCGeneration()),
  221. "Shouldn't traverse XULDocument!");
  222. // XXX tmp->mForwardReferences?
  223. // XXX tmp->mContextStack?
  224. // An element will only have a template builder as long as it's in the
  225. // document, so we'll traverse the table here instead of from the element.
  226. if (tmp->mTemplateBuilderTable) {
  227. for (auto iter = tmp->mTemplateBuilderTable->Iter();
  228. !iter.Done();
  229. iter.Next()) {
  230. NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mTemplateBuilderTable key");
  231. cb.NoteXPCOMChild(iter.Key());
  232. NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mTemplateBuilderTable value");
  233. cb.NoteXPCOMChild(iter.UserData());
  234. }
  235. }
  236. NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCurrentPrototype)
  237. NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMasterPrototype)
  238. NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCommandDispatcher)
  239. NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrototypes)
  240. NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStore)
  241. if (tmp->mOverlayLoadObservers) {
  242. for (auto iter = tmp->mOverlayLoadObservers->Iter();
  243. !iter.Done();
  244. iter.Next()) {
  245. NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mOverlayLoadObservers value");
  246. cb.NoteXPCOMChild(iter.Data());
  247. }
  248. }
  249. if (tmp->mPendingOverlayLoadNotifications) {
  250. for (auto iter = tmp->mPendingOverlayLoadNotifications->Iter();
  251. !iter.Done();
  252. iter.Next()) {
  253. NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mPendingOverlayLoadNotifications value");
  254. cb.NoteXPCOMChild(iter.Data());
  255. }
  256. }
  257. NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
  258. NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XULDocument, XMLDocument)
  259. delete tmp->mTemplateBuilderTable;
  260. tmp->mTemplateBuilderTable = nullptr;
  261. NS_IMPL_CYCLE_COLLECTION_UNLINK(mCommandDispatcher)
  262. NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStore)
  263. //XXX We should probably unlink all the objects we traverse.
  264. NS_IMPL_CYCLE_COLLECTION_UNLINK_END
  265. NS_IMPL_ADDREF_INHERITED(XULDocument, XMLDocument)
  266. NS_IMPL_RELEASE_INHERITED(XULDocument, XMLDocument)
  267. // QueryInterface implementation for XULDocument
  268. NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(XULDocument)
  269. NS_INTERFACE_TABLE_INHERITED(XULDocument, nsIXULDocument,
  270. nsIDOMXULDocument, nsIStreamLoaderObserver,
  271. nsICSSLoaderObserver, nsIOffThreadScriptReceiver)
  272. NS_INTERFACE_TABLE_TAIL_INHERITING(XMLDocument)
  273. //----------------------------------------------------------------------
  274. //
  275. // nsIDocument interface
  276. //
  277. void
  278. XULDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup)
  279. {
  280. NS_NOTREACHED("Reset");
  281. }
  282. void
  283. XULDocument::ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup,
  284. nsIPrincipal* aPrincipal)
  285. {
  286. NS_NOTREACHED("ResetToURI");
  287. }
  288. void
  289. XULDocument::SetContentType(const nsAString& aContentType)
  290. {
  291. NS_ASSERTION(aContentType.EqualsLiteral("application/vnd.mozilla.xul+xml"),
  292. "xul-documents always has content-type application/vnd.mozilla.xul+xml");
  293. // Don't do anything, xul always has the mimetype
  294. // application/vnd.mozilla.xul+xml
  295. }
  296. // This is called when the master document begins loading, whether it's
  297. // being cached or not.
  298. nsresult
  299. XULDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
  300. nsILoadGroup* aLoadGroup,
  301. nsISupports* aContainer,
  302. nsIStreamListener **aDocListener,
  303. bool aReset, nsIContentSink* aSink)
  304. {
  305. if (MOZ_LOG_TEST(gXULLog, LogLevel::Warning)) {
  306. nsCOMPtr<nsIURI> uri;
  307. nsresult rv = aChannel->GetOriginalURI(getter_AddRefs(uri));
  308. if (NS_SUCCEEDED(rv)) {
  309. nsAutoCString urlspec;
  310. rv = uri->GetSpec(urlspec);
  311. if (NS_SUCCEEDED(rv)) {
  312. MOZ_LOG(gXULLog, LogLevel::Warning,
  313. ("xul: load document '%s'", urlspec.get()));
  314. }
  315. }
  316. }
  317. // NOTE: If this ever starts calling nsDocument::StartDocumentLoad
  318. // we'll possibly need to reset our content type afterwards.
  319. mStillWalking = true;
  320. mMayStartLayout = false;
  321. mDocumentLoadGroup = do_GetWeakReference(aLoadGroup);
  322. mChannel = aChannel;
  323. // Get the URI. Note that this should match nsDocShell::OnLoadingSite
  324. nsresult rv =
  325. NS_GetFinalChannelURI(aChannel, getter_AddRefs(mDocumentURI));
  326. NS_ENSURE_SUCCESS(rv, rv);
  327. ResetStylesheetsToURI(mDocumentURI);
  328. RetrieveRelevantHeaders(aChannel);
  329. // Look in the chrome cache: we've got this puppy loaded
  330. // already.
  331. nsXULPrototypeDocument* proto = IsChromeURI(mDocumentURI) ?
  332. nsXULPrototypeCache::GetInstance()->GetPrototype(mDocumentURI) :
  333. nullptr;
  334. // Same comment as nsChromeProtocolHandler::NewChannel and
  335. // XULDocument::ResumeWalk
  336. // - Ben Goodger
  337. //
  338. // We don't abort on failure here because there are too many valid
  339. // cases that can return failure, and the null-ness of |proto| is enough
  340. // to trigger the fail-safe parse-from-disk solution. Example failure cases
  341. // (for reference) include:
  342. //
  343. // NS_ERROR_NOT_AVAILABLE: the URI cannot be found in the startup cache,
  344. // parse from disk
  345. // other: the startup cache file could not be found, probably
  346. // due to being accessed before a profile has been selected (e.g.
  347. // loading chrome for the profile manager itself). This must be
  348. // parsed from disk.
  349. if (proto) {
  350. // If we're racing with another document to load proto, wait till the
  351. // load has finished loading before trying to add cloned style sheets.
  352. // XULDocument::EndLoad will call proto->NotifyLoadDone, which will
  353. // find all racing documents and notify them via OnPrototypeLoadDone,
  354. // which will add style sheet clones to each document.
  355. bool loaded;
  356. rv = proto->AwaitLoadDone(this, &loaded);
  357. if (NS_FAILED(rv)) return rv;
  358. mMasterPrototype = mCurrentPrototype = proto;
  359. // Set up the right principal on ourselves.
  360. SetPrincipal(proto->DocumentPrincipal());
  361. // We need a listener, even if proto is not yet loaded, in which
  362. // event the listener's OnStopRequest method does nothing, and all
  363. // the interesting work happens below XULDocument::EndLoad, from
  364. // the call there to mCurrentPrototype->NotifyLoadDone().
  365. *aDocListener = new CachedChromeStreamListener(this, loaded);
  366. }
  367. else {
  368. bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
  369. bool fillXULCache = (useXULCache && IsChromeURI(mDocumentURI));
  370. // It's just a vanilla document load. Create a parser to deal
  371. // with the stream n' stuff.
  372. nsCOMPtr<nsIParser> parser;
  373. rv = PrepareToLoad(aContainer, aCommand, aChannel, aLoadGroup,
  374. getter_AddRefs(parser));
  375. if (NS_FAILED(rv)) return rv;
  376. // Predicate mIsWritingFastLoad on the XUL cache being enabled,
  377. // so we don't have to re-check whether the cache is enabled all
  378. // the time.
  379. mIsWritingFastLoad = useXULCache;
  380. nsCOMPtr<nsIStreamListener> listener = do_QueryInterface(parser, &rv);
  381. NS_ASSERTION(NS_SUCCEEDED(rv), "parser doesn't support nsIStreamListener");
  382. if (NS_FAILED(rv)) return rv;
  383. *aDocListener = listener;
  384. parser->Parse(mDocumentURI);
  385. // Put the current prototype, created under PrepareToLoad, into the
  386. // XUL prototype cache now. We can't do this under PrepareToLoad or
  387. // overlay loading will break; search for PutPrototype in ResumeWalk
  388. // and see the comment there.
  389. if (fillXULCache) {
  390. nsXULPrototypeCache::GetInstance()->PutPrototype(mCurrentPrototype);
  391. }
  392. }
  393. NS_IF_ADDREF(*aDocListener);
  394. return NS_OK;
  395. }
  396. // This gets invoked after a prototype for this document or one of
  397. // its overlays is fully built in the content sink.
  398. void
  399. XULDocument::EndLoad()
  400. {
  401. // This can happen if an overlay fails to load
  402. if (!mCurrentPrototype)
  403. return;
  404. nsresult rv;
  405. // Whack the prototype document into the cache so that the next
  406. // time somebody asks for it, they don't need to load it by hand.
  407. nsCOMPtr<nsIURI> uri = mCurrentPrototype->GetURI();
  408. bool isChrome = IsChromeURI(uri);
  409. // Remember if the XUL cache is on
  410. bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
  411. // If the current prototype is an overlay document (non-master prototype)
  412. // and we're filling the FastLoad disk cache, tell the cache we're done
  413. // loading it, and write the prototype. The master prototype is put into
  414. // the cache earlier in XULDocument::StartDocumentLoad.
  415. if (useXULCache && mIsWritingFastLoad && isChrome &&
  416. mMasterPrototype != mCurrentPrototype) {
  417. nsXULPrototypeCache::GetInstance()->WritePrototype(mCurrentPrototype);
  418. }
  419. if (IsOverlayAllowed(uri)) {
  420. nsCOMPtr<nsIXULOverlayProvider> reg =
  421. mozilla::services::GetXULOverlayProviderService();
  422. if (reg) {
  423. nsCOMPtr<nsISimpleEnumerator> overlays;
  424. rv = reg->GetStyleOverlays(uri, getter_AddRefs(overlays));
  425. if (NS_FAILED(rv)) return;
  426. bool moreSheets;
  427. nsCOMPtr<nsISupports> next;
  428. nsCOMPtr<nsIURI> sheetURI;
  429. while (NS_SUCCEEDED(rv = overlays->HasMoreElements(&moreSheets)) &&
  430. moreSheets) {
  431. overlays->GetNext(getter_AddRefs(next));
  432. sheetURI = do_QueryInterface(next);
  433. if (!sheetURI) {
  434. NS_ERROR("Chrome registry handed me a non-nsIURI object!");
  435. continue;
  436. }
  437. if (IsChromeURI(sheetURI)) {
  438. mCurrentPrototype->AddStyleSheetReference(sheetURI);
  439. }
  440. }
  441. }
  442. if (isChrome && useXULCache) {
  443. // If it's a chrome prototype document, then notify any
  444. // documents that raced to load the prototype, and awaited
  445. // its load completion via proto->AwaitLoadDone().
  446. rv = mCurrentPrototype->NotifyLoadDone();
  447. if (NS_FAILED(rv)) return;
  448. }
  449. }
  450. OnPrototypeLoadDone(true);
  451. if (MOZ_LOG_TEST(gXULLog, LogLevel::Warning)) {
  452. nsAutoCString urlspec;
  453. rv = uri->GetSpec(urlspec);
  454. if (NS_SUCCEEDED(rv)) {
  455. MOZ_LOG(gXULLog, LogLevel::Warning,
  456. ("xul: Finished loading document '%s'", urlspec.get()));
  457. }
  458. }
  459. }
  460. NS_IMETHODIMP
  461. XULDocument::OnPrototypeLoadDone(bool aResumeWalk)
  462. {
  463. nsresult rv;
  464. // Add the style overlays from chrome registry, if any.
  465. rv = AddPrototypeSheets();
  466. if (NS_FAILED(rv)) return rv;
  467. rv = PrepareToWalk();
  468. NS_ASSERTION(NS_SUCCEEDED(rv), "unable to prepare for walk");
  469. if (NS_FAILED(rv)) return rv;
  470. if (aResumeWalk) {
  471. rv = ResumeWalk();
  472. }
  473. return rv;
  474. }
  475. // called when an error occurs parsing a document
  476. bool
  477. XULDocument::OnDocumentParserError()
  478. {
  479. // don't report errors that are from overlays
  480. if (mCurrentPrototype && mMasterPrototype != mCurrentPrototype) {
  481. nsCOMPtr<nsIURI> uri = mCurrentPrototype->GetURI();
  482. if (IsChromeURI(uri)) {
  483. nsCOMPtr<nsIObserverService> os =
  484. mozilla::services::GetObserverService();
  485. if (os)
  486. os->NotifyObservers(uri, "xul-overlay-parsererror",
  487. EmptyString().get());
  488. }
  489. return false;
  490. }
  491. return true;
  492. }
  493. static void
  494. ClearBroadcasterMapEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
  495. {
  496. BroadcasterMapEntry* entry =
  497. static_cast<BroadcasterMapEntry*>(aEntry);
  498. for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
  499. delete entry->mListeners[i];
  500. }
  501. entry->mListeners.Clear();
  502. // N.B. that we need to manually run the dtor because we
  503. // constructed the nsTArray object in-place.
  504. entry->mListeners.~nsTArray<BroadcastListener*>();
  505. }
  506. static bool
  507. CanBroadcast(int32_t aNameSpaceID, nsIAtom* aAttribute)
  508. {
  509. // Don't push changes to the |id|, |ref|, |persist|, |command| or
  510. // |observes| attribute.
  511. if (aNameSpaceID == kNameSpaceID_None) {
  512. if ((aAttribute == nsGkAtoms::id) ||
  513. (aAttribute == nsGkAtoms::ref) ||
  514. (aAttribute == nsGkAtoms::persist) ||
  515. (aAttribute == nsGkAtoms::command) ||
  516. (aAttribute == nsGkAtoms::observes)) {
  517. return false;
  518. }
  519. }
  520. return true;
  521. }
  522. struct nsAttrNameInfo
  523. {
  524. nsAttrNameInfo(int32_t aNamespaceID, nsIAtom* aName, nsIAtom* aPrefix) :
  525. mNamespaceID(aNamespaceID), mName(aName), mPrefix(aPrefix) {}
  526. nsAttrNameInfo(const nsAttrNameInfo& aOther) :
  527. mNamespaceID(aOther.mNamespaceID), mName(aOther.mName),
  528. mPrefix(aOther.mPrefix) {}
  529. int32_t mNamespaceID;
  530. nsCOMPtr<nsIAtom> mName;
  531. nsCOMPtr<nsIAtom> mPrefix;
  532. };
  533. void
  534. XULDocument::SynchronizeBroadcastListener(Element *aBroadcaster,
  535. Element *aListener,
  536. const nsAString &aAttr)
  537. {
  538. if (!nsContentUtils::IsSafeToRunScript()) {
  539. nsDelayedBroadcastUpdate delayedUpdate(aBroadcaster, aListener,
  540. aAttr);
  541. mDelayedBroadcasters.AppendElement(delayedUpdate);
  542. MaybeBroadcast();
  543. return;
  544. }
  545. bool notify = mDocumentLoaded || mHandlingDelayedBroadcasters;
  546. if (aAttr.EqualsLiteral("*")) {
  547. uint32_t count = aBroadcaster->GetAttrCount();
  548. nsTArray<nsAttrNameInfo> attributes(count);
  549. for (uint32_t i = 0; i < count; ++i) {
  550. const nsAttrName* attrName = aBroadcaster->GetAttrNameAt(i);
  551. int32_t nameSpaceID = attrName->NamespaceID();
  552. nsIAtom* name = attrName->LocalName();
  553. // _Don't_ push the |id|, |ref|, or |persist| attribute's value!
  554. if (! CanBroadcast(nameSpaceID, name))
  555. continue;
  556. attributes.AppendElement(nsAttrNameInfo(nameSpaceID, name,
  557. attrName->GetPrefix()));
  558. }
  559. count = attributes.Length();
  560. while (count-- > 0) {
  561. int32_t nameSpaceID = attributes[count].mNamespaceID;
  562. nsIAtom* name = attributes[count].mName;
  563. nsAutoString value;
  564. if (aBroadcaster->GetAttr(nameSpaceID, name, value)) {
  565. aListener->SetAttr(nameSpaceID, name, attributes[count].mPrefix,
  566. value, notify);
  567. }
  568. #if 0
  569. // XXX we don't fire the |onbroadcast| handler during
  570. // initial hookup: doing so would potentially run the
  571. // |onbroadcast| handler before the |onload| handler,
  572. // which could define JS properties that mask XBL
  573. // properties, etc.
  574. ExecuteOnBroadcastHandlerFor(aBroadcaster, aListener, name);
  575. #endif
  576. }
  577. }
  578. else {
  579. // Find out if the attribute is even present at all.
  580. nsCOMPtr<nsIAtom> name = NS_Atomize(aAttr);
  581. nsAutoString value;
  582. if (aBroadcaster->GetAttr(kNameSpaceID_None, name, value)) {
  583. aListener->SetAttr(kNameSpaceID_None, name, value, notify);
  584. } else {
  585. aListener->UnsetAttr(kNameSpaceID_None, name, notify);
  586. }
  587. #if 0
  588. // XXX we don't fire the |onbroadcast| handler during initial
  589. // hookup: doing so would potentially run the |onbroadcast|
  590. // handler before the |onload| handler, which could define JS
  591. // properties that mask XBL properties, etc.
  592. ExecuteOnBroadcastHandlerFor(aBroadcaster, aListener, name);
  593. #endif
  594. }
  595. }
  596. NS_IMETHODIMP
  597. XULDocument::AddBroadcastListenerFor(nsIDOMElement* aBroadcaster,
  598. nsIDOMElement* aListener,
  599. const nsAString& aAttr)
  600. {
  601. ErrorResult rv;
  602. nsCOMPtr<Element> broadcaster = do_QueryInterface(aBroadcaster);
  603. nsCOMPtr<Element> listener = do_QueryInterface(aListener);
  604. NS_ENSURE_ARG(broadcaster && listener);
  605. AddBroadcastListenerFor(*broadcaster, *listener, aAttr, rv);
  606. return rv.StealNSResult();
  607. }
  608. void
  609. XULDocument::AddBroadcastListenerFor(Element& aBroadcaster, Element& aListener,
  610. const nsAString& aAttr, ErrorResult& aRv)
  611. {
  612. nsresult rv =
  613. nsContentUtils::CheckSameOrigin(this, &aBroadcaster);
  614. if (NS_FAILED(rv)) {
  615. aRv.Throw(rv);
  616. return;
  617. }
  618. rv = nsContentUtils::CheckSameOrigin(this, &aListener);
  619. if (NS_FAILED(rv)) {
  620. aRv.Throw(rv);
  621. return;
  622. }
  623. static const PLDHashTableOps gOps = {
  624. PLDHashTable::HashVoidPtrKeyStub,
  625. PLDHashTable::MatchEntryStub,
  626. PLDHashTable::MoveEntryStub,
  627. ClearBroadcasterMapEntry,
  628. nullptr
  629. };
  630. if (! mBroadcasterMap) {
  631. mBroadcasterMap = new PLDHashTable(&gOps, sizeof(BroadcasterMapEntry));
  632. }
  633. auto entry = static_cast<BroadcasterMapEntry*>
  634. (mBroadcasterMap->Search(&aBroadcaster));
  635. if (!entry) {
  636. entry = static_cast<BroadcasterMapEntry*>
  637. (mBroadcasterMap->Add(&aBroadcaster, fallible));
  638. if (! entry) {
  639. aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
  640. return;
  641. }
  642. entry->mBroadcaster = &aBroadcaster;
  643. // N.B. placement new to construct the nsTArray object in-place
  644. new (&entry->mListeners) nsTArray<BroadcastListener*>();
  645. }
  646. // Only add the listener if it's not there already!
  647. nsCOMPtr<nsIAtom> attr = NS_Atomize(aAttr);
  648. for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
  649. BroadcastListener* bl = entry->mListeners[i];
  650. nsCOMPtr<Element> blListener = do_QueryReferent(bl->mListener);
  651. if (blListener == &aListener && bl->mAttribute == attr)
  652. return;
  653. }
  654. BroadcastListener* bl = new BroadcastListener;
  655. bl->mListener = do_GetWeakReference(&aListener);
  656. bl->mAttribute = attr;
  657. entry->mListeners.AppendElement(bl);
  658. SynchronizeBroadcastListener(&aBroadcaster, &aListener, aAttr);
  659. }
  660. NS_IMETHODIMP
  661. XULDocument::RemoveBroadcastListenerFor(nsIDOMElement* aBroadcaster,
  662. nsIDOMElement* aListener,
  663. const nsAString& aAttr)
  664. {
  665. nsCOMPtr<Element> broadcaster = do_QueryInterface(aBroadcaster);
  666. nsCOMPtr<Element> listener = do_QueryInterface(aListener);
  667. NS_ENSURE_ARG(broadcaster && listener);
  668. RemoveBroadcastListenerFor(*broadcaster, *listener, aAttr);
  669. return NS_OK;
  670. }
  671. void
  672. XULDocument::RemoveBroadcastListenerFor(Element& aBroadcaster,
  673. Element& aListener,
  674. const nsAString& aAttr)
  675. {
  676. // If we haven't added any broadcast listeners, then there sure
  677. // aren't any to remove.
  678. if (! mBroadcasterMap)
  679. return;
  680. auto entry = static_cast<BroadcasterMapEntry*>
  681. (mBroadcasterMap->Search(&aBroadcaster));
  682. if (entry) {
  683. nsCOMPtr<nsIAtom> attr = NS_Atomize(aAttr);
  684. for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
  685. BroadcastListener* bl = entry->mListeners[i];
  686. nsCOMPtr<Element> blListener = do_QueryReferent(bl->mListener);
  687. if (blListener == &aListener && bl->mAttribute == attr) {
  688. entry->mListeners.RemoveElementAt(i);
  689. delete bl;
  690. if (entry->mListeners.IsEmpty())
  691. mBroadcasterMap->RemoveEntry(entry);
  692. break;
  693. }
  694. }
  695. }
  696. }
  697. nsresult
  698. XULDocument::ExecuteOnBroadcastHandlerFor(Element* aBroadcaster,
  699. Element* aListener,
  700. nsIAtom* aAttr)
  701. {
  702. // Now we execute the onchange handler in the context of the
  703. // observer. We need to find the observer in order to
  704. // execute the handler.
  705. for (nsIContent* child = aListener->GetFirstChild();
  706. child;
  707. child = child->GetNextSibling()) {
  708. // Look for an <observes> element beneath the listener. This
  709. // ought to have an |element| attribute that refers to
  710. // aBroadcaster, and an |attribute| element that tells us what
  711. // attriubtes we're listening for.
  712. if (!child->NodeInfo()->Equals(nsGkAtoms::observes, kNameSpaceID_XUL))
  713. continue;
  714. // Is this the element that was listening to us?
  715. nsAutoString listeningToID;
  716. child->GetAttr(kNameSpaceID_None, nsGkAtoms::element, listeningToID);
  717. nsAutoString broadcasterID;
  718. aBroadcaster->GetAttr(kNameSpaceID_None, nsGkAtoms::id, broadcasterID);
  719. if (listeningToID != broadcasterID)
  720. continue;
  721. // We are observing the broadcaster, but is this the right
  722. // attribute?
  723. nsAutoString listeningToAttribute;
  724. child->GetAttr(kNameSpaceID_None, nsGkAtoms::attribute,
  725. listeningToAttribute);
  726. if (!aAttr->Equals(listeningToAttribute) &&
  727. !listeningToAttribute.EqualsLiteral("*")) {
  728. continue;
  729. }
  730. // This is the right <observes> element. Execute the
  731. // |onbroadcast| event handler
  732. WidgetEvent event(true, eXULBroadcast);
  733. nsCOMPtr<nsIPresShell> shell = GetShell();
  734. if (shell) {
  735. RefPtr<nsPresContext> aPresContext = shell->GetPresContext();
  736. // Handle the DOM event
  737. nsEventStatus status = nsEventStatus_eIgnore;
  738. EventDispatcher::Dispatch(child, aPresContext, &event, nullptr,
  739. &status);
  740. }
  741. }
  742. return NS_OK;
  743. }
  744. void
  745. XULDocument::AttributeWillChange(nsIDocument* aDocument,
  746. Element* aElement, int32_t aNameSpaceID,
  747. nsIAtom* aAttribute, int32_t aModType,
  748. const nsAttrValue* aNewValue)
  749. {
  750. MOZ_ASSERT(aElement, "Null content!");
  751. NS_PRECONDITION(aAttribute, "Must have an attribute that's changing!");
  752. // XXXbz check aNameSpaceID, dammit!
  753. // See if we need to update our ref map.
  754. if (aAttribute == nsGkAtoms::ref) {
  755. // Might not need this, but be safe for now.
  756. nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
  757. RemoveElementFromRefMap(aElement);
  758. }
  759. }
  760. static bool
  761. ShouldPersistAttribute(Element* aElement, nsIAtom* aAttribute)
  762. {
  763. if (aElement->IsXULElement(nsGkAtoms::window)) {
  764. // This is not an element of the top document, its owner is
  765. // not an nsXULWindow. Persist it.
  766. if (aElement->OwnerDoc()->GetParentDocument()) {
  767. return true;
  768. }
  769. // The following attributes of xul:window should be handled in
  770. // nsXULWindow::SavePersistentAttributes instead of here.
  771. if (aAttribute == nsGkAtoms::screenX ||
  772. aAttribute == nsGkAtoms::screenY ||
  773. aAttribute == nsGkAtoms::width ||
  774. aAttribute == nsGkAtoms::height ||
  775. aAttribute == nsGkAtoms::sizemode) {
  776. return false;
  777. }
  778. }
  779. return true;
  780. }
  781. void
  782. XULDocument::AttributeChanged(nsIDocument* aDocument,
  783. Element* aElement, int32_t aNameSpaceID,
  784. nsIAtom* aAttribute, int32_t aModType,
  785. const nsAttrValue* aOldValue)
  786. {
  787. NS_ASSERTION(aDocument == this, "unexpected doc");
  788. // Might not need this, but be safe for now.
  789. nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
  790. // XXXbz check aNameSpaceID, dammit!
  791. // See if we need to update our ref map.
  792. if (aAttribute == nsGkAtoms::ref) {
  793. AddElementToRefMap(aElement);
  794. }
  795. // Synchronize broadcast listeners
  796. if (mBroadcasterMap &&
  797. CanBroadcast(aNameSpaceID, aAttribute)) {
  798. auto entry = static_cast<BroadcasterMapEntry*>
  799. (mBroadcasterMap->Search(aElement));
  800. if (entry) {
  801. // We've got listeners: push the value.
  802. nsAutoString value;
  803. bool attrSet = aElement->GetAttr(kNameSpaceID_None, aAttribute, value);
  804. for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
  805. BroadcastListener* bl = entry->mListeners[i];
  806. if ((bl->mAttribute == aAttribute) ||
  807. (bl->mAttribute == nsGkAtoms::_asterisk)) {
  808. nsCOMPtr<Element> listenerEl
  809. = do_QueryReferent(bl->mListener);
  810. if (listenerEl) {
  811. nsAutoString currentValue;
  812. bool hasAttr = listenerEl->GetAttr(kNameSpaceID_None,
  813. aAttribute,
  814. currentValue);
  815. // We need to update listener only if we're
  816. // (1) removing an existing attribute,
  817. // (2) adding a new attribute or
  818. // (3) changing the value of an attribute.
  819. bool needsAttrChange =
  820. attrSet != hasAttr || !value.Equals(currentValue);
  821. nsDelayedBroadcastUpdate delayedUpdate(aElement,
  822. listenerEl,
  823. aAttribute,
  824. value,
  825. attrSet,
  826. needsAttrChange);
  827. size_t index =
  828. mDelayedAttrChangeBroadcasts.IndexOf(delayedUpdate,
  829. 0, nsDelayedBroadcastUpdate::Comparator());
  830. if (index != mDelayedAttrChangeBroadcasts.NoIndex) {
  831. if (mHandlingDelayedAttrChange) {
  832. NS_WARNING("Broadcasting loop!");
  833. continue;
  834. }
  835. mDelayedAttrChangeBroadcasts.RemoveElementAt(index);
  836. }
  837. mDelayedAttrChangeBroadcasts.AppendElement(delayedUpdate);
  838. }
  839. }
  840. }
  841. }
  842. }
  843. // checks for modifications in broadcasters
  844. bool listener, resolved;
  845. CheckBroadcasterHookup(aElement, &listener, &resolved);
  846. // See if there is anything we need to persist in the localstore.
  847. //
  848. // XXX Namespace handling broken :-(
  849. nsAutoString persist;
  850. aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::persist, persist);
  851. // Persistence of attributes of xul:window is handled in nsXULWindow.
  852. if (ShouldPersistAttribute(aElement, aAttribute) && !persist.IsEmpty() &&
  853. // XXXldb This should check that it's a token, not just a substring.
  854. persist.Find(nsDependentAtomString(aAttribute)) >= 0) {
  855. nsContentUtils::AddScriptRunner(NewRunnableMethod
  856. <nsIContent*, int32_t, nsIAtom*>
  857. (this, &XULDocument::DoPersist, aElement, kNameSpaceID_None,
  858. aAttribute));
  859. }
  860. }
  861. void
  862. XULDocument::ContentAppended(nsIDocument* aDocument,
  863. nsIContent* aContainer,
  864. nsIContent* aFirstNewContent,
  865. int32_t aNewIndexInContainer)
  866. {
  867. NS_ASSERTION(aDocument == this, "unexpected doc");
  868. // Might not need this, but be safe for now.
  869. nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
  870. // Update our element map
  871. nsresult rv = NS_OK;
  872. for (nsIContent* cur = aFirstNewContent; cur && NS_SUCCEEDED(rv);
  873. cur = cur->GetNextSibling()) {
  874. rv = AddSubtreeToDocument(cur);
  875. }
  876. }
  877. void
  878. XULDocument::ContentInserted(nsIDocument* aDocument,
  879. nsIContent* aContainer,
  880. nsIContent* aChild,
  881. int32_t aIndexInContainer)
  882. {
  883. NS_ASSERTION(aDocument == this, "unexpected doc");
  884. // Might not need this, but be safe for now.
  885. nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
  886. AddSubtreeToDocument(aChild);
  887. }
  888. void
  889. XULDocument::ContentRemoved(nsIDocument* aDocument,
  890. nsIContent* aContainer,
  891. nsIContent* aChild,
  892. int32_t aIndexInContainer,
  893. nsIContent* aPreviousSibling)
  894. {
  895. NS_ASSERTION(aDocument == this, "unexpected doc");
  896. // Might not need this, but be safe for now.
  897. nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
  898. RemoveSubtreeFromDocument(aChild);
  899. }
  900. //----------------------------------------------------------------------
  901. //
  902. // nsIXULDocument interface
  903. //
  904. void
  905. XULDocument::GetElementsForID(const nsAString& aID,
  906. nsCOMArray<nsIContent>& aElements)
  907. {
  908. aElements.Clear();
  909. nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aID);
  910. if (entry) {
  911. entry->AppendAllIdContent(&aElements);
  912. }
  913. nsRefMapEntry *refEntry = mRefMap.GetEntry(aID);
  914. if (refEntry) {
  915. refEntry->AppendAll(&aElements);
  916. }
  917. }
  918. nsresult
  919. XULDocument::AddForwardReference(nsForwardReference* aRef)
  920. {
  921. if (mResolutionPhase < aRef->GetPhase()) {
  922. if (!mForwardReferences.AppendElement(aRef)) {
  923. delete aRef;
  924. return NS_ERROR_OUT_OF_MEMORY;
  925. }
  926. }
  927. else {
  928. NS_ERROR("forward references have already been resolved");
  929. delete aRef;
  930. }
  931. return NS_OK;
  932. }
  933. nsresult
  934. XULDocument::ResolveForwardReferences()
  935. {
  936. if (mResolutionPhase == nsForwardReference::eDone)
  937. return NS_OK;
  938. NS_ASSERTION(mResolutionPhase == nsForwardReference::eStart,
  939. "nested ResolveForwardReferences()");
  940. // Resolve each outstanding 'forward' reference. We iterate
  941. // through the list of forward references until no more forward
  942. // references can be resolved. This annealing process is
  943. // guaranteed to converge because we've "closed the gate" to new
  944. // forward references.
  945. const nsForwardReference::Phase* pass = nsForwardReference::kPasses;
  946. while ((mResolutionPhase = *pass) != nsForwardReference::eDone) {
  947. uint32_t previous = 0;
  948. while (mForwardReferences.Length() &&
  949. mForwardReferences.Length() != previous) {
  950. previous = mForwardReferences.Length();
  951. for (uint32_t i = 0; i < mForwardReferences.Length(); ++i) {
  952. nsForwardReference* fwdref = mForwardReferences[i];
  953. if (fwdref->GetPhase() == *pass) {
  954. nsForwardReference::Result result = fwdref->Resolve();
  955. switch (result) {
  956. case nsForwardReference::eResolve_Succeeded:
  957. case nsForwardReference::eResolve_Error:
  958. mForwardReferences.RemoveElementAt(i);
  959. // fixup because we removed from list
  960. --i;
  961. break;
  962. case nsForwardReference::eResolve_Later:
  963. // do nothing. we'll try again later
  964. ;
  965. }
  966. if (mResolutionPhase == nsForwardReference::eStart) {
  967. // Resolve() loaded a dynamic overlay,
  968. // (see XULDocument::LoadOverlayInternal()).
  969. // Return for now, we will be called again.
  970. return NS_OK;
  971. }
  972. }
  973. }
  974. }
  975. ++pass;
  976. }
  977. mForwardReferences.Clear();
  978. return NS_OK;
  979. }
  980. //----------------------------------------------------------------------
  981. //
  982. // nsIDOMDocument interface
  983. //
  984. NS_IMETHODIMP
  985. XULDocument::GetElementsByAttribute(const nsAString& aAttribute,
  986. const nsAString& aValue,
  987. nsIDOMNodeList** aReturn)
  988. {
  989. *aReturn = GetElementsByAttribute(aAttribute, aValue).take();
  990. return NS_OK;
  991. }
  992. already_AddRefed<nsINodeList>
  993. XULDocument::GetElementsByAttribute(const nsAString& aAttribute,
  994. const nsAString& aValue)
  995. {
  996. nsCOMPtr<nsIAtom> attrAtom(NS_Atomize(aAttribute));
  997. void* attrValue = new nsString(aValue);
  998. RefPtr<nsContentList> list = new nsContentList(this,
  999. MatchAttribute,
  1000. nsContentUtils::DestroyMatchString,
  1001. attrValue,
  1002. true,
  1003. attrAtom,
  1004. kNameSpaceID_Unknown);
  1005. return list.forget();
  1006. }
  1007. NS_IMETHODIMP
  1008. XULDocument::GetElementsByAttributeNS(const nsAString& aNamespaceURI,
  1009. const nsAString& aAttribute,
  1010. const nsAString& aValue,
  1011. nsIDOMNodeList** aReturn)
  1012. {
  1013. ErrorResult rv;
  1014. *aReturn = GetElementsByAttributeNS(aNamespaceURI, aAttribute,
  1015. aValue, rv).take();
  1016. return rv.StealNSResult();
  1017. }
  1018. already_AddRefed<nsINodeList>
  1019. XULDocument::GetElementsByAttributeNS(const nsAString& aNamespaceURI,
  1020. const nsAString& aAttribute,
  1021. const nsAString& aValue,
  1022. ErrorResult& aRv)
  1023. {
  1024. nsCOMPtr<nsIAtom> attrAtom(NS_Atomize(aAttribute));
  1025. void* attrValue = new nsString(aValue);
  1026. int32_t nameSpaceId = kNameSpaceID_Wildcard;
  1027. if (!aNamespaceURI.EqualsLiteral("*")) {
  1028. nsresult rv =
  1029. nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI,
  1030. nameSpaceId);
  1031. if (NS_FAILED(rv)) {
  1032. aRv.Throw(rv);
  1033. return nullptr;
  1034. }
  1035. }
  1036. RefPtr<nsContentList> list = new nsContentList(this,
  1037. MatchAttribute,
  1038. nsContentUtils::DestroyMatchString,
  1039. attrValue,
  1040. true,
  1041. attrAtom,
  1042. nameSpaceId);
  1043. return list.forget();
  1044. }
  1045. NS_IMETHODIMP
  1046. XULDocument::Persist(const nsAString& aID,
  1047. const nsAString& aAttr)
  1048. {
  1049. // If we're currently reading persisted attributes out of the
  1050. // localstore, _don't_ re-enter and try to set them again!
  1051. if (mApplyingPersistedAttrs)
  1052. return NS_OK;
  1053. Element* element = nsDocument::GetElementById(aID);
  1054. if (!element)
  1055. return NS_OK;
  1056. nsCOMPtr<nsIAtom> tag;
  1057. int32_t nameSpaceID;
  1058. RefPtr<mozilla::dom::NodeInfo> ni = element->GetExistingAttrNameFromQName(aAttr);
  1059. nsresult rv;
  1060. if (ni) {
  1061. tag = ni->NameAtom();
  1062. nameSpaceID = ni->NamespaceID();
  1063. }
  1064. else {
  1065. // Make sure that this QName is going to be valid.
  1066. const char16_t *colon;
  1067. rv = nsContentUtils::CheckQName(PromiseFlatString(aAttr), true, &colon);
  1068. if (NS_FAILED(rv)) {
  1069. // There was an invalid character or it was malformed.
  1070. return NS_ERROR_INVALID_ARG;
  1071. }
  1072. if (colon) {
  1073. // We don't really handle namespace qualifiers in attribute names.
  1074. return NS_ERROR_NOT_IMPLEMENTED;
  1075. }
  1076. tag = NS_Atomize(aAttr);
  1077. NS_ENSURE_TRUE(tag, NS_ERROR_OUT_OF_MEMORY);
  1078. nameSpaceID = kNameSpaceID_None;
  1079. }
  1080. return Persist(element, nameSpaceID, tag);
  1081. }
  1082. nsresult
  1083. XULDocument::Persist(nsIContent* aElement, int32_t aNameSpaceID,
  1084. nsIAtom* aAttribute)
  1085. {
  1086. // For non-chrome documents, persistance is simply broken
  1087. if (!nsContentUtils::IsSystemPrincipal(NodePrincipal()))
  1088. return NS_ERROR_NOT_AVAILABLE;
  1089. if (!mLocalStore) {
  1090. mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
  1091. if (NS_WARN_IF(!mLocalStore)) {
  1092. return NS_ERROR_NOT_INITIALIZED;
  1093. }
  1094. }
  1095. nsAutoString id;
  1096. aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id);
  1097. nsAtomString attrstr(aAttribute);
  1098. nsAutoString valuestr;
  1099. aElement->GetAttr(kNameSpaceID_None, aAttribute, valuestr);
  1100. nsAutoCString utf8uri;
  1101. nsresult rv = mDocumentURI->GetSpec(utf8uri);
  1102. if (NS_WARN_IF(NS_FAILED(rv))) {
  1103. return rv;
  1104. }
  1105. NS_ConvertUTF8toUTF16 uri(utf8uri);
  1106. bool hasAttr;
  1107. rv = mLocalStore->HasValue(uri, id, attrstr, &hasAttr);
  1108. if (NS_WARN_IF(NS_FAILED(rv))) {
  1109. return rv;
  1110. }
  1111. if (hasAttr && valuestr.IsEmpty()) {
  1112. return mLocalStore->RemoveValue(uri, id, attrstr);
  1113. } else {
  1114. return mLocalStore->SetValue(uri, id, attrstr, valuestr);
  1115. }
  1116. }
  1117. nsresult
  1118. XULDocument::GetViewportSize(int32_t* aWidth,
  1119. int32_t* aHeight)
  1120. {
  1121. *aWidth = *aHeight = 0;
  1122. FlushPendingNotifications(Flush_Layout);
  1123. nsIPresShell *shell = GetShell();
  1124. NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
  1125. nsIFrame* frame = shell->GetRootFrame();
  1126. NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
  1127. nsSize size = frame->GetSize();
  1128. *aWidth = nsPresContext::AppUnitsToIntCSSPixels(size.width);
  1129. *aHeight = nsPresContext::AppUnitsToIntCSSPixels(size.height);
  1130. return NS_OK;
  1131. }
  1132. NS_IMETHODIMP
  1133. XULDocument::GetWidth(int32_t* aWidth)
  1134. {
  1135. NS_ENSURE_ARG_POINTER(aWidth);
  1136. int32_t height;
  1137. return GetViewportSize(aWidth, &height);
  1138. }
  1139. int32_t
  1140. XULDocument::GetWidth(ErrorResult& aRv)
  1141. {
  1142. int32_t width;
  1143. aRv = GetWidth(&width);
  1144. return width;
  1145. }
  1146. NS_IMETHODIMP
  1147. XULDocument::GetHeight(int32_t* aHeight)
  1148. {
  1149. NS_ENSURE_ARG_POINTER(aHeight);
  1150. int32_t width;
  1151. return GetViewportSize(&width, aHeight);
  1152. }
  1153. int32_t
  1154. XULDocument::GetHeight(ErrorResult& aRv)
  1155. {
  1156. int32_t height;
  1157. aRv = GetHeight(&height);
  1158. return height;
  1159. }
  1160. JSObject*
  1161. GetScopeObjectOfNode(nsIDOMNode* node)
  1162. {
  1163. MOZ_ASSERT(node, "Must not be called with null.");
  1164. // Window root occasionally keeps alive a node of a document whose
  1165. // window is already dead. If in this brief period someone calls
  1166. // GetPopupNode and we return that node, nsNodeSH::PreCreate will throw,
  1167. // because it will not know which scope this node belongs to. Returning
  1168. // an orphan node like that to JS would be a bug anyway, so to avoid
  1169. // this, let's do the same check as nsNodeSH::PreCreate does to
  1170. // determine the scope and if it fails let's just return null in
  1171. // XULDocument::GetPopupNode.
  1172. nsCOMPtr<nsINode> inode = do_QueryInterface(node);
  1173. MOZ_ASSERT(inode, "How can this happen?");
  1174. nsIDocument* doc = inode->OwnerDoc();
  1175. MOZ_ASSERT(inode, "This should never happen.");
  1176. nsIGlobalObject* global = doc->GetScopeObject();
  1177. return global ? global->GetGlobalJSObject() : nullptr;
  1178. }
  1179. //----------------------------------------------------------------------
  1180. //
  1181. // nsIDOMXULDocument interface
  1182. //
  1183. NS_IMETHODIMP
  1184. XULDocument::GetPopupNode(nsIDOMNode** aNode)
  1185. {
  1186. *aNode = nullptr;
  1187. nsCOMPtr<nsIDOMNode> node;
  1188. nsCOMPtr<nsPIWindowRoot> rootWin = GetWindowRoot();
  1189. if (rootWin)
  1190. node = rootWin->GetPopupNode(); // addref happens here
  1191. if (!node) {
  1192. nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
  1193. if (pm) {
  1194. node = pm->GetLastTriggerPopupNode(this);
  1195. }
  1196. }
  1197. if (node && nsContentUtils::CanCallerAccess(node)
  1198. && GetScopeObjectOfNode(node)) {
  1199. node.forget(aNode);
  1200. }
  1201. return NS_OK;
  1202. }
  1203. already_AddRefed<nsINode>
  1204. XULDocument::GetPopupNode()
  1205. {
  1206. nsCOMPtr<nsIDOMNode> node;
  1207. DebugOnly<nsresult> rv = GetPopupNode(getter_AddRefs(node));
  1208. MOZ_ASSERT(NS_SUCCEEDED(rv));
  1209. nsCOMPtr<nsINode> retval(do_QueryInterface(node));
  1210. return retval.forget();
  1211. }
  1212. NS_IMETHODIMP
  1213. XULDocument::SetPopupNode(nsIDOMNode* aNode)
  1214. {
  1215. if (aNode) {
  1216. // only allow real node objects
  1217. nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
  1218. NS_ENSURE_ARG(node);
  1219. }
  1220. nsCOMPtr<nsPIWindowRoot> rootWin = GetWindowRoot();
  1221. if (rootWin)
  1222. rootWin->SetPopupNode(aNode); // addref happens here
  1223. return NS_OK;
  1224. }
  1225. void
  1226. XULDocument::SetPopupNode(nsINode* aNode)
  1227. {
  1228. nsCOMPtr<nsIDOMNode> node(do_QueryInterface(aNode));
  1229. DebugOnly<nsresult> rv = SetPopupNode(node);
  1230. MOZ_ASSERT(NS_SUCCEEDED(rv));
  1231. }
  1232. // Returns the rangeOffset element from the XUL Popup Manager. This is for
  1233. // chrome callers only.
  1234. NS_IMETHODIMP
  1235. XULDocument::GetPopupRangeParent(nsIDOMNode** aRangeParent)
  1236. {
  1237. NS_ENSURE_ARG_POINTER(aRangeParent);
  1238. *aRangeParent = nullptr;
  1239. nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
  1240. if (!pm)
  1241. return NS_ERROR_FAILURE;
  1242. int32_t offset;
  1243. pm->GetMouseLocation(aRangeParent, &offset);
  1244. if (*aRangeParent && !nsContentUtils::CanCallerAccess(*aRangeParent)) {
  1245. NS_RELEASE(*aRangeParent);
  1246. return NS_ERROR_DOM_SECURITY_ERR;
  1247. }
  1248. return NS_OK;
  1249. }
  1250. already_AddRefed<nsINode>
  1251. XULDocument::GetPopupRangeParent(ErrorResult& aRv)
  1252. {
  1253. nsCOMPtr<nsIDOMNode> node;
  1254. aRv = GetPopupRangeParent(getter_AddRefs(node));
  1255. nsCOMPtr<nsINode> retval(do_QueryInterface(node));
  1256. return retval.forget();
  1257. }
  1258. // Returns the rangeOffset element from the XUL Popup Manager. We check the
  1259. // rangeParent to determine if the caller has rights to access to the data.
  1260. NS_IMETHODIMP
  1261. XULDocument::GetPopupRangeOffset(int32_t* aRangeOffset)
  1262. {
  1263. ErrorResult rv;
  1264. *aRangeOffset = GetPopupRangeOffset(rv);
  1265. return rv.StealNSResult();
  1266. }
  1267. int32_t
  1268. XULDocument::GetPopupRangeOffset(ErrorResult& aRv)
  1269. {
  1270. nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
  1271. if (!pm) {
  1272. aRv.Throw(NS_ERROR_FAILURE);
  1273. return 0;
  1274. }
  1275. int32_t offset;
  1276. nsCOMPtr<nsIDOMNode> parent;
  1277. pm->GetMouseLocation(getter_AddRefs(parent), &offset);
  1278. if (parent && !nsContentUtils::CanCallerAccess(parent)) {
  1279. aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
  1280. return 0;
  1281. }
  1282. return offset;
  1283. }
  1284. NS_IMETHODIMP
  1285. XULDocument::GetTooltipNode(nsIDOMNode** aNode)
  1286. {
  1287. *aNode = nullptr;
  1288. nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
  1289. if (pm) {
  1290. nsCOMPtr<nsIDOMNode> node = pm->GetLastTriggerTooltipNode(this);
  1291. if (node && nsContentUtils::CanCallerAccess(node))
  1292. node.forget(aNode);
  1293. }
  1294. return NS_OK;
  1295. }
  1296. already_AddRefed<nsINode>
  1297. XULDocument::GetTooltipNode()
  1298. {
  1299. nsCOMPtr<nsIDOMNode> node;
  1300. DebugOnly<nsresult> rv = GetTooltipNode(getter_AddRefs(node));
  1301. MOZ_ASSERT(NS_SUCCEEDED(rv));
  1302. nsCOMPtr<nsINode> retval(do_QueryInterface(node));
  1303. return retval.forget();
  1304. }
  1305. NS_IMETHODIMP
  1306. XULDocument::SetTooltipNode(nsIDOMNode* aNode)
  1307. {
  1308. // do nothing
  1309. return NS_OK;
  1310. }
  1311. NS_IMETHODIMP
  1312. XULDocument::GetCommandDispatcher(nsIDOMXULCommandDispatcher** aTracker)
  1313. {
  1314. *aTracker = mCommandDispatcher;
  1315. NS_IF_ADDREF(*aTracker);
  1316. return NS_OK;
  1317. }
  1318. Element*
  1319. XULDocument::GetRefById(const nsAString& aID)
  1320. {
  1321. if (nsRefMapEntry* refEntry = mRefMap.GetEntry(aID)) {
  1322. MOZ_ASSERT(refEntry->GetFirstElement());
  1323. return refEntry->GetFirstElement();
  1324. }
  1325. return nullptr;
  1326. }
  1327. nsresult
  1328. XULDocument::AddElementToDocumentPre(Element* aElement)
  1329. {
  1330. // Do a bunch of work that's necessary when an element gets added
  1331. // to the XUL Document.
  1332. nsresult rv;
  1333. // 1. Add the element to the resource-to-element map. Also add it to
  1334. // the id map, since it seems this can be called when creating
  1335. // elements from prototypes.
  1336. nsIAtom* id = aElement->GetID();
  1337. if (id) {
  1338. // FIXME: Shouldn't BindToTree take care of this?
  1339. nsAutoScriptBlocker scriptBlocker;
  1340. AddToIdTable(aElement, id);
  1341. }
  1342. rv = AddElementToRefMap(aElement);
  1343. if (NS_FAILED(rv)) return rv;
  1344. // 2. If the element is a 'command updater' (i.e., has a
  1345. // "commandupdater='true'" attribute), then add the element to the
  1346. // document's command dispatcher
  1347. if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::commandupdater,
  1348. nsGkAtoms::_true, eCaseMatters)) {
  1349. rv = nsXULContentUtils::SetCommandUpdater(this, aElement);
  1350. if (NS_FAILED(rv)) return rv;
  1351. }
  1352. // 3. Check for a broadcaster hookup attribute, in which case
  1353. // we'll hook the node up as a listener on a broadcaster.
  1354. bool listener, resolved;
  1355. rv = CheckBroadcasterHookup(aElement, &listener, &resolved);
  1356. if (NS_FAILED(rv)) return rv;
  1357. // If it's not there yet, we may be able to defer hookup until
  1358. // later.
  1359. if (listener && !resolved && (mResolutionPhase != nsForwardReference::eDone)) {
  1360. BroadcasterHookup* hookup = new BroadcasterHookup(this, aElement);
  1361. rv = AddForwardReference(hookup);
  1362. if (NS_FAILED(rv)) return rv;
  1363. }
  1364. return NS_OK;
  1365. }
  1366. nsresult
  1367. XULDocument::AddElementToDocumentPost(Element* aElement)
  1368. {
  1369. // We need to pay special attention to the keyset tag to set up a listener
  1370. if (aElement->NodeInfo()->Equals(nsGkAtoms::keyset, kNameSpaceID_XUL)) {
  1371. // Create our XUL key listener and hook it up.
  1372. nsXBLService::AttachGlobalKeyHandler(aElement);
  1373. }
  1374. // See if we need to attach a XUL template to this node
  1375. bool needsHookup;
  1376. nsresult rv = CheckTemplateBuilderHookup(aElement, &needsHookup);
  1377. if (NS_FAILED(rv))
  1378. return rv;
  1379. if (needsHookup) {
  1380. if (mResolutionPhase == nsForwardReference::eDone) {
  1381. rv = CreateTemplateBuilder(aElement);
  1382. if (NS_FAILED(rv))
  1383. return rv;
  1384. }
  1385. else {
  1386. TemplateBuilderHookup* hookup = new TemplateBuilderHookup(aElement);
  1387. rv = AddForwardReference(hookup);
  1388. if (NS_FAILED(rv))
  1389. return rv;
  1390. }
  1391. }
  1392. return NS_OK;
  1393. }
  1394. NS_IMETHODIMP
  1395. XULDocument::AddSubtreeToDocument(nsIContent* aContent)
  1396. {
  1397. NS_ASSERTION(aContent->GetUncomposedDoc() == this, "Element not in doc!");
  1398. // From here on we only care about elements.
  1399. if (!aContent->IsElement()) {
  1400. return NS_OK;
  1401. }
  1402. Element* aElement = aContent->AsElement();
  1403. // Do pre-order addition magic
  1404. nsresult rv = AddElementToDocumentPre(aElement);
  1405. if (NS_FAILED(rv)) return rv;
  1406. // Recurse to children
  1407. for (nsIContent* child = aElement->GetLastChild();
  1408. child;
  1409. child = child->GetPreviousSibling()) {
  1410. rv = AddSubtreeToDocument(child);
  1411. if (NS_FAILED(rv))
  1412. return rv;
  1413. }
  1414. // Do post-order addition magic
  1415. return AddElementToDocumentPost(aElement);
  1416. }
  1417. NS_IMETHODIMP
  1418. XULDocument::RemoveSubtreeFromDocument(nsIContent* aContent)
  1419. {
  1420. // From here on we only care about elements.
  1421. if (!aContent->IsElement()) {
  1422. return NS_OK;
  1423. }
  1424. Element* aElement = aContent->AsElement();
  1425. // Do a bunch of cleanup to remove an element from the XUL
  1426. // document.
  1427. nsresult rv;
  1428. if (aElement->NodeInfo()->Equals(nsGkAtoms::keyset, kNameSpaceID_XUL)) {
  1429. nsXBLService::DetachGlobalKeyHandler(aElement);
  1430. }
  1431. // 1. Remove any children from the document.
  1432. for (nsIContent* child = aElement->GetLastChild();
  1433. child;
  1434. child = child->GetPreviousSibling()) {
  1435. rv = RemoveSubtreeFromDocument(child);
  1436. if (NS_FAILED(rv))
  1437. return rv;
  1438. }
  1439. // 2. Remove the element from the resource-to-element map.
  1440. // Also remove it from the id map, since we added it in
  1441. // AddElementToDocumentPre().
  1442. RemoveElementFromRefMap(aElement);
  1443. nsIAtom* id = aElement->GetID();
  1444. if (id) {
  1445. // FIXME: Shouldn't UnbindFromTree take care of this?
  1446. nsAutoScriptBlocker scriptBlocker;
  1447. RemoveFromIdTable(aElement, id);
  1448. }
  1449. // 3. If the element is a 'command updater', then remove the
  1450. // element from the document's command dispatcher.
  1451. if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::commandupdater,
  1452. nsGkAtoms::_true, eCaseMatters)) {
  1453. nsCOMPtr<nsIDOMElement> domelement = do_QueryInterface(aElement);
  1454. NS_ASSERTION(domelement != nullptr, "not a DOM element");
  1455. if (! domelement)
  1456. return NS_ERROR_UNEXPECTED;
  1457. rv = mCommandDispatcher->RemoveCommandUpdater(domelement);
  1458. if (NS_FAILED(rv)) return rv;
  1459. }
  1460. // 4. Remove the element from our broadcaster map, since it is no longer
  1461. // in the document.
  1462. nsCOMPtr<Element> broadcaster, listener;
  1463. nsAutoString attribute, broadcasterID;
  1464. rv = FindBroadcaster(aElement, getter_AddRefs(listener),
  1465. broadcasterID, attribute, getter_AddRefs(broadcaster));
  1466. if (rv == NS_FINDBROADCASTER_FOUND) {
  1467. RemoveBroadcastListenerFor(*broadcaster, *listener, attribute);
  1468. }
  1469. return NS_OK;
  1470. }
  1471. NS_IMETHODIMP
  1472. XULDocument::SetTemplateBuilderFor(nsIContent* aContent,
  1473. nsIXULTemplateBuilder* aBuilder)
  1474. {
  1475. if (! mTemplateBuilderTable) {
  1476. if (!aBuilder) {
  1477. return NS_OK;
  1478. }
  1479. mTemplateBuilderTable = new BuilderTable;
  1480. }
  1481. if (aBuilder) {
  1482. mTemplateBuilderTable->Put(aContent, aBuilder);
  1483. }
  1484. else {
  1485. mTemplateBuilderTable->Remove(aContent);
  1486. }
  1487. return NS_OK;
  1488. }
  1489. NS_IMETHODIMP
  1490. XULDocument::GetTemplateBuilderFor(nsIContent* aContent,
  1491. nsIXULTemplateBuilder** aResult)
  1492. {
  1493. if (mTemplateBuilderTable) {
  1494. mTemplateBuilderTable->Get(aContent, aResult);
  1495. }
  1496. else
  1497. *aResult = nullptr;
  1498. return NS_OK;
  1499. }
  1500. static void
  1501. GetRefMapAttribute(Element* aElement, nsAutoString* aValue)
  1502. {
  1503. aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::ref, *aValue);
  1504. }
  1505. nsresult
  1506. XULDocument::AddElementToRefMap(Element* aElement)
  1507. {
  1508. // Look at the element's 'ref' attribute, and if set,
  1509. // add an entry in the resource-to-element map to the element.
  1510. nsAutoString value;
  1511. GetRefMapAttribute(aElement, &value);
  1512. if (!value.IsEmpty()) {
  1513. nsRefMapEntry *entry = mRefMap.PutEntry(value);
  1514. if (!entry)
  1515. return NS_ERROR_OUT_OF_MEMORY;
  1516. if (!entry->AddElement(aElement))
  1517. return NS_ERROR_OUT_OF_MEMORY;
  1518. }
  1519. return NS_OK;
  1520. }
  1521. void
  1522. XULDocument::RemoveElementFromRefMap(Element* aElement)
  1523. {
  1524. // Remove the element from the resource-to-element map.
  1525. nsAutoString value;
  1526. GetRefMapAttribute(aElement, &value);
  1527. if (!value.IsEmpty()) {
  1528. nsRefMapEntry *entry = mRefMap.GetEntry(value);
  1529. if (!entry)
  1530. return;
  1531. if (entry->RemoveElement(aElement)) {
  1532. mRefMap.RemoveEntry(entry);
  1533. }
  1534. }
  1535. }
  1536. //----------------------------------------------------------------------
  1537. //
  1538. // nsIDOMNode interface
  1539. //
  1540. nsresult
  1541. XULDocument::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const
  1542. {
  1543. // We don't allow cloning of a XUL document
  1544. *aResult = nullptr;
  1545. return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
  1546. }
  1547. //----------------------------------------------------------------------
  1548. //
  1549. // Implementation methods
  1550. //
  1551. nsresult
  1552. XULDocument::Init()
  1553. {
  1554. nsresult rv = XMLDocument::Init();
  1555. NS_ENSURE_SUCCESS(rv, rv);
  1556. // Create our command dispatcher and hook it up.
  1557. mCommandDispatcher = new nsXULCommandDispatcher(this);
  1558. if (gRefCnt++ == 0) {
  1559. // ensure that the XUL prototype cache is instantiated successfully,
  1560. // so that we can use nsXULPrototypeCache::GetInstance() without
  1561. // null-checks in the rest of the class.
  1562. nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
  1563. if (!cache) {
  1564. NS_ERROR("Could not instantiate nsXULPrototypeCache");
  1565. return NS_ERROR_FAILURE;
  1566. }
  1567. }
  1568. Preferences::RegisterCallback(XULDocument::DirectionChanged,
  1569. "intl.uidirection.", this);
  1570. return NS_OK;
  1571. }
  1572. nsresult
  1573. XULDocument::StartLayout(void)
  1574. {
  1575. mMayStartLayout = true;
  1576. nsCOMPtr<nsIPresShell> shell = GetShell();
  1577. if (shell) {
  1578. // Resize-reflow this time
  1579. nsPresContext *cx = shell->GetPresContext();
  1580. NS_ASSERTION(cx != nullptr, "no pres context");
  1581. if (! cx)
  1582. return NS_ERROR_UNEXPECTED;
  1583. nsCOMPtr<nsIDocShell> docShell = cx->GetDocShell();
  1584. NS_ASSERTION(docShell != nullptr, "container is not a docshell");
  1585. if (! docShell)
  1586. return NS_ERROR_UNEXPECTED;
  1587. nsresult rv = NS_OK;
  1588. nsRect r = cx->GetVisibleArea();
  1589. rv = shell->Initialize(r.width, r.height);
  1590. NS_ENSURE_SUCCESS(rv, rv);
  1591. }
  1592. return NS_OK;
  1593. }
  1594. /* static */
  1595. bool
  1596. XULDocument::MatchAttribute(Element* aElement,
  1597. int32_t aNamespaceID,
  1598. nsIAtom* aAttrName,
  1599. void* aData)
  1600. {
  1601. NS_PRECONDITION(aElement, "Must have content node to work with!");
  1602. nsString* attrValue = static_cast<nsString*>(aData);
  1603. if (aNamespaceID != kNameSpaceID_Unknown &&
  1604. aNamespaceID != kNameSpaceID_Wildcard) {
  1605. return attrValue->EqualsLiteral("*") ?
  1606. aElement->HasAttr(aNamespaceID, aAttrName) :
  1607. aElement->AttrValueIs(aNamespaceID, aAttrName, *attrValue,
  1608. eCaseMatters);
  1609. }
  1610. // Qualified name match. This takes more work.
  1611. uint32_t count = aElement->GetAttrCount();
  1612. for (uint32_t i = 0; i < count; ++i) {
  1613. const nsAttrName* name = aElement->GetAttrNameAt(i);
  1614. bool nameMatch;
  1615. if (name->IsAtom()) {
  1616. nameMatch = name->Atom() == aAttrName;
  1617. } else if (aNamespaceID == kNameSpaceID_Wildcard) {
  1618. nameMatch = name->NodeInfo()->Equals(aAttrName);
  1619. } else {
  1620. nameMatch = name->NodeInfo()->QualifiedNameEquals(aAttrName);
  1621. }
  1622. if (nameMatch) {
  1623. return attrValue->EqualsLiteral("*") ||
  1624. aElement->AttrValueIs(name->NamespaceID(), name->LocalName(),
  1625. *attrValue, eCaseMatters);
  1626. }
  1627. }
  1628. return false;
  1629. }
  1630. nsresult
  1631. XULDocument::PrepareToLoad(nsISupports* aContainer,
  1632. const char* aCommand,
  1633. nsIChannel* aChannel,
  1634. nsILoadGroup* aLoadGroup,
  1635. nsIParser** aResult)
  1636. {
  1637. // Get the document's principal
  1638. nsCOMPtr<nsIPrincipal> principal;
  1639. nsContentUtils::GetSecurityManager()->
  1640. GetChannelResultPrincipal(aChannel, getter_AddRefs(principal));
  1641. return PrepareToLoadPrototype(mDocumentURI, aCommand, principal, aResult);
  1642. }
  1643. nsresult
  1644. XULDocument::PrepareToLoadPrototype(nsIURI* aURI, const char* aCommand,
  1645. nsIPrincipal* aDocumentPrincipal,
  1646. nsIParser** aResult)
  1647. {
  1648. nsresult rv;
  1649. // Create a new prototype document.
  1650. rv = NS_NewXULPrototypeDocument(getter_AddRefs(mCurrentPrototype));
  1651. if (NS_FAILED(rv)) return rv;
  1652. rv = mCurrentPrototype->InitPrincipal(aURI, aDocumentPrincipal);
  1653. if (NS_FAILED(rv)) {
  1654. mCurrentPrototype = nullptr;
  1655. return rv;
  1656. }
  1657. // Bootstrap the master document prototype.
  1658. if (! mMasterPrototype) {
  1659. mMasterPrototype = mCurrentPrototype;
  1660. // Set our principal based on the master proto.
  1661. SetPrincipal(aDocumentPrincipal);
  1662. }
  1663. // Create a XUL content sink, a parser, and kick off a load for
  1664. // the overlay.
  1665. RefPtr<XULContentSinkImpl> sink = new XULContentSinkImpl();
  1666. rv = sink->Init(this, mCurrentPrototype);
  1667. NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to initialize datasource sink");
  1668. if (NS_FAILED(rv)) return rv;
  1669. nsCOMPtr<nsIParser> parser = do_CreateInstance(kParserCID, &rv);
  1670. NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create parser");
  1671. if (NS_FAILED(rv)) return rv;
  1672. parser->SetCommand(nsCRT::strcmp(aCommand, "view-source") ? eViewNormal :
  1673. eViewSource);
  1674. parser->SetDocumentCharset(NS_LITERAL_CSTRING("UTF-8"),
  1675. kCharsetFromDocTypeDefault);
  1676. parser->SetContentSink(sink); // grabs a reference to the parser
  1677. parser.forget(aResult);
  1678. return NS_OK;
  1679. }
  1680. nsresult
  1681. XULDocument::ApplyPersistentAttributes()
  1682. {
  1683. // For non-chrome documents, persistance is simply broken
  1684. if (!nsContentUtils::IsSystemPrincipal(NodePrincipal()))
  1685. return NS_ERROR_NOT_AVAILABLE;
  1686. // Add all of the 'persisted' attributes into the content
  1687. // model.
  1688. if (!mLocalStore) {
  1689. mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
  1690. if (NS_WARN_IF(!mLocalStore)) {
  1691. return NS_ERROR_NOT_INITIALIZED;
  1692. }
  1693. }
  1694. mApplyingPersistedAttrs = true;
  1695. ApplyPersistentAttributesInternal();
  1696. mApplyingPersistedAttrs = false;
  1697. // After we've applied persistence once, we should only reapply
  1698. // it to nodes created by overlays
  1699. mRestrictPersistence = true;
  1700. mPersistenceIds.Clear();
  1701. return NS_OK;
  1702. }
  1703. nsresult
  1704. XULDocument::ApplyPersistentAttributesInternal()
  1705. {
  1706. nsCOMArray<nsIContent> elements;
  1707. nsAutoCString utf8uri;
  1708. nsresult rv = mDocumentURI->GetSpec(utf8uri);
  1709. if (NS_WARN_IF(NS_FAILED(rv))) {
  1710. return rv;
  1711. }
  1712. NS_ConvertUTF8toUTF16 uri(utf8uri);
  1713. // Get a list of element IDs for which persisted values are available
  1714. nsCOMPtr<nsIStringEnumerator> ids;
  1715. rv = mLocalStore->GetIDsEnumerator(uri, getter_AddRefs(ids));
  1716. if (NS_WARN_IF(NS_FAILED(rv))) {
  1717. return rv;
  1718. }
  1719. while (1) {
  1720. bool hasmore = false;
  1721. ids->HasMore(&hasmore);
  1722. if (!hasmore) {
  1723. break;
  1724. }
  1725. nsAutoString id;
  1726. ids->GetNext(id);
  1727. if (mRestrictPersistence && !mPersistenceIds.Contains(id)) {
  1728. continue;
  1729. }
  1730. // This will clear the array if there are no elements.
  1731. GetElementsForID(id, elements);
  1732. if (!elements.Count()) {
  1733. continue;
  1734. }
  1735. rv = ApplyPersistentAttributesToElements(id, elements);
  1736. if (NS_WARN_IF(NS_FAILED(rv))) {
  1737. return rv;
  1738. }
  1739. }
  1740. return NS_OK;
  1741. }
  1742. nsresult
  1743. XULDocument::ApplyPersistentAttributesToElements(const nsAString &aID,
  1744. nsCOMArray<nsIContent>& aElements)
  1745. {
  1746. nsAutoCString utf8uri;
  1747. nsresult rv = mDocumentURI->GetSpec(utf8uri);
  1748. if (NS_WARN_IF(NS_FAILED(rv))) {
  1749. return rv;
  1750. }
  1751. NS_ConvertUTF8toUTF16 uri(utf8uri);
  1752. // Get a list of attributes for which persisted values are available
  1753. nsCOMPtr<nsIStringEnumerator> attrs;
  1754. rv = mLocalStore->GetAttributeEnumerator(uri, aID, getter_AddRefs(attrs));
  1755. if (NS_WARN_IF(NS_FAILED(rv))) {
  1756. return rv;
  1757. }
  1758. while (1) {
  1759. bool hasmore = PR_FALSE;
  1760. attrs->HasMore(&hasmore);
  1761. if (!hasmore) {
  1762. break;
  1763. }
  1764. nsAutoString attrstr;
  1765. attrs->GetNext(attrstr);
  1766. nsAutoString value;
  1767. rv = mLocalStore->GetValue(uri, aID, attrstr, value);
  1768. if (NS_WARN_IF(NS_FAILED(rv))) {
  1769. return rv;
  1770. }
  1771. nsCOMPtr<nsIAtom> attr = NS_Atomize(attrstr);
  1772. if (NS_WARN_IF(!attr)) {
  1773. return NS_ERROR_OUT_OF_MEMORY;
  1774. }
  1775. uint32_t cnt = aElements.Count();
  1776. for (int32_t i = int32_t(cnt) - 1; i >= 0; --i) {
  1777. nsCOMPtr<nsIContent> element = aElements.SafeObjectAt(i);
  1778. if (!element) {
  1779. continue;
  1780. }
  1781. rv = element->SetAttr(kNameSpaceID_None, attr, value, PR_TRUE);
  1782. }
  1783. }
  1784. return NS_OK;
  1785. }
  1786. void
  1787. XULDocument::TraceProtos(JSTracer* aTrc, uint32_t aGCNumber)
  1788. {
  1789. uint32_t i, count = mPrototypes.Length();
  1790. for (i = 0; i < count; ++i) {
  1791. mPrototypes[i]->TraceProtos(aTrc, aGCNumber);
  1792. }
  1793. if (mCurrentPrototype) {
  1794. mCurrentPrototype->TraceProtos(aTrc, aGCNumber);
  1795. }
  1796. }
  1797. //----------------------------------------------------------------------
  1798. //
  1799. // XULDocument::ContextStack
  1800. //
  1801. XULDocument::ContextStack::ContextStack()
  1802. : mTop(nullptr), mDepth(0)
  1803. {
  1804. }
  1805. XULDocument::ContextStack::~ContextStack()
  1806. {
  1807. while (mTop) {
  1808. Entry* doomed = mTop;
  1809. mTop = mTop->mNext;
  1810. NS_IF_RELEASE(doomed->mElement);
  1811. delete doomed;
  1812. }
  1813. }
  1814. nsresult
  1815. XULDocument::ContextStack::Push(nsXULPrototypeElement* aPrototype,
  1816. nsIContent* aElement)
  1817. {
  1818. Entry* entry = new Entry;
  1819. entry->mPrototype = aPrototype;
  1820. entry->mElement = aElement;
  1821. NS_IF_ADDREF(entry->mElement);
  1822. entry->mIndex = 0;
  1823. entry->mNext = mTop;
  1824. mTop = entry;
  1825. ++mDepth;
  1826. return NS_OK;
  1827. }
  1828. nsresult
  1829. XULDocument::ContextStack::Pop()
  1830. {
  1831. if (mDepth == 0)
  1832. return NS_ERROR_UNEXPECTED;
  1833. Entry* doomed = mTop;
  1834. mTop = mTop->mNext;
  1835. --mDepth;
  1836. NS_IF_RELEASE(doomed->mElement);
  1837. delete doomed;
  1838. return NS_OK;
  1839. }
  1840. nsresult
  1841. XULDocument::ContextStack::Peek(nsXULPrototypeElement** aPrototype,
  1842. nsIContent** aElement,
  1843. int32_t* aIndex)
  1844. {
  1845. if (mDepth == 0)
  1846. return NS_ERROR_UNEXPECTED;
  1847. *aPrototype = mTop->mPrototype;
  1848. *aElement = mTop->mElement;
  1849. NS_IF_ADDREF(*aElement);
  1850. *aIndex = mTop->mIndex;
  1851. return NS_OK;
  1852. }
  1853. nsresult
  1854. XULDocument::ContextStack::SetTopIndex(int32_t aIndex)
  1855. {
  1856. if (mDepth == 0)
  1857. return NS_ERROR_UNEXPECTED;
  1858. mTop->mIndex = aIndex;
  1859. return NS_OK;
  1860. }
  1861. //----------------------------------------------------------------------
  1862. //
  1863. // Content model walking routines
  1864. //
  1865. nsresult
  1866. XULDocument::PrepareToWalk()
  1867. {
  1868. // Prepare to walk the mCurrentPrototype
  1869. nsresult rv;
  1870. // Keep an owning reference to the prototype document so that its
  1871. // elements aren't yanked from beneath us.
  1872. mPrototypes.AppendElement(mCurrentPrototype);
  1873. // Get the prototype's root element and initialize the context
  1874. // stack for the prototype walk.
  1875. nsXULPrototypeElement* proto = mCurrentPrototype->GetRootElement();
  1876. if (! proto) {
  1877. if (MOZ_LOG_TEST(gXULLog, LogLevel::Error)) {
  1878. nsCOMPtr<nsIURI> url = mCurrentPrototype->GetURI();
  1879. nsAutoCString urlspec;
  1880. rv = url->GetSpec(urlspec);
  1881. if (NS_FAILED(rv)) return rv;
  1882. MOZ_LOG(gXULLog, LogLevel::Error,
  1883. ("xul: error parsing '%s'", urlspec.get()));
  1884. }
  1885. return NS_OK;
  1886. }
  1887. uint32_t piInsertionPoint = 0;
  1888. if (mState != eState_Master) {
  1889. int32_t indexOfRoot = IndexOf(GetRootElement());
  1890. NS_ASSERTION(indexOfRoot >= 0,
  1891. "No root content when preparing to walk overlay!");
  1892. piInsertionPoint = indexOfRoot;
  1893. }
  1894. const nsTArray<RefPtr<nsXULPrototypePI> >& processingInstructions =
  1895. mCurrentPrototype->GetProcessingInstructions();
  1896. uint32_t total = processingInstructions.Length();
  1897. for (uint32_t i = 0; i < total; ++i) {
  1898. rv = CreateAndInsertPI(processingInstructions[i],
  1899. this, piInsertionPoint + i);
  1900. if (NS_FAILED(rv)) return rv;
  1901. }
  1902. // Now check the chrome registry for any additional overlays.
  1903. rv = AddChromeOverlays();
  1904. if (NS_FAILED(rv)) return rv;
  1905. // Do one-time initialization if we're preparing to walk the
  1906. // master document's prototype.
  1907. RefPtr<Element> root;
  1908. if (mState == eState_Master) {
  1909. // Add the root element
  1910. rv = CreateElementFromPrototype(proto, getter_AddRefs(root), true);
  1911. if (NS_FAILED(rv)) return rv;
  1912. rv = AppendChildTo(root, false);
  1913. if (NS_FAILED(rv)) return rv;
  1914. rv = AddElementToRefMap(root);
  1915. if (NS_FAILED(rv)) return rv;
  1916. // Block onload until we've finished building the complete
  1917. // document content model.
  1918. BlockOnload();
  1919. }
  1920. // There'd better not be anything on the context stack at this
  1921. // point! This is the basis case for our "induction" in
  1922. // ResumeWalk(), below, which'll assume that there's always a
  1923. // content element on the context stack if either 1) we're in the
  1924. // "master" document, or 2) we're in an overlay, and we've got
  1925. // more than one prototype element (the single, root "overlay"
  1926. // element) on the stack.
  1927. NS_ASSERTION(mContextStack.Depth() == 0, "something's on the context stack already");
  1928. if (mContextStack.Depth() != 0)
  1929. return NS_ERROR_UNEXPECTED;
  1930. rv = mContextStack.Push(proto, root);
  1931. if (NS_FAILED(rv)) return rv;
  1932. return NS_OK;
  1933. }
  1934. nsresult
  1935. XULDocument::CreateAndInsertPI(const nsXULPrototypePI* aProtoPI,
  1936. nsINode* aParent, uint32_t aIndex)
  1937. {
  1938. NS_PRECONDITION(aProtoPI, "null ptr");
  1939. NS_PRECONDITION(aParent, "null ptr");
  1940. RefPtr<ProcessingInstruction> node =
  1941. NS_NewXMLProcessingInstruction(mNodeInfoManager, aProtoPI->mTarget,
  1942. aProtoPI->mData);
  1943. nsresult rv;
  1944. if (aProtoPI->mTarget.EqualsLiteral("xml-stylesheet")) {
  1945. rv = InsertXMLStylesheetPI(aProtoPI, aParent, aIndex, node);
  1946. } else if (aProtoPI->mTarget.EqualsLiteral("xul-overlay")) {
  1947. rv = InsertXULOverlayPI(aProtoPI, aParent, aIndex, node);
  1948. } else {
  1949. // No special processing, just add the PI to the document.
  1950. rv = aParent->InsertChildAt(node, aIndex, false);
  1951. }
  1952. return rv;
  1953. }
  1954. nsresult
  1955. XULDocument::InsertXMLStylesheetPI(const nsXULPrototypePI* aProtoPI,
  1956. nsINode* aParent,
  1957. uint32_t aIndex,
  1958. nsIContent* aPINode)
  1959. {
  1960. nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(aPINode));
  1961. NS_ASSERTION(ssle, "passed XML Stylesheet node does not "
  1962. "implement nsIStyleSheetLinkingElement!");
  1963. nsresult rv;
  1964. ssle->InitStyleLinkElement(false);
  1965. // We want to be notified when the style sheet finishes loading, so
  1966. // disable style sheet loading for now.
  1967. ssle->SetEnableUpdates(false);
  1968. ssle->OverrideBaseURI(mCurrentPrototype->GetURI());
  1969. rv = aParent->InsertChildAt(aPINode, aIndex, false);
  1970. if (NS_FAILED(rv)) return rv;
  1971. ssle->SetEnableUpdates(true);
  1972. // load the stylesheet if necessary, passing ourselves as
  1973. // nsICSSObserver
  1974. bool willNotify;
  1975. bool isAlternate;
  1976. rv = ssle->UpdateStyleSheet(this, &willNotify, &isAlternate);
  1977. if (NS_SUCCEEDED(rv) && willNotify && !isAlternate) {
  1978. ++mPendingSheets;
  1979. }
  1980. // Ignore errors from UpdateStyleSheet; we don't want failure to
  1981. // do that to break the XUL document load. But do propagate out
  1982. // NS_ERROR_OUT_OF_MEMORY.
  1983. if (rv == NS_ERROR_OUT_OF_MEMORY) {
  1984. return rv;
  1985. }
  1986. return NS_OK;
  1987. }
  1988. nsresult
  1989. XULDocument::InsertXULOverlayPI(const nsXULPrototypePI* aProtoPI,
  1990. nsINode* aParent,
  1991. uint32_t aIndex,
  1992. nsIContent* aPINode)
  1993. {
  1994. nsresult rv;
  1995. rv = aParent->InsertChildAt(aPINode, aIndex, false);
  1996. if (NS_FAILED(rv)) return rv;
  1997. // xul-overlay PI is special only in prolog
  1998. if (!nsContentUtils::InProlog(aPINode)) {
  1999. return NS_OK;
  2000. }
  2001. nsAutoString href;
  2002. nsContentUtils::GetPseudoAttributeValue(aProtoPI->mData,
  2003. nsGkAtoms::href,
  2004. href);
  2005. // If there was no href, we can't do anything with this PI
  2006. if (href.IsEmpty()) {
  2007. return NS_OK;
  2008. }
  2009. // Add the overlay to our list of overlays that need to be processed.
  2010. nsCOMPtr<nsIURI> uri;
  2011. rv = NS_NewURI(getter_AddRefs(uri), href, nullptr,
  2012. mCurrentPrototype->GetURI());
  2013. if (NS_SUCCEEDED(rv)) {
  2014. // We insert overlays into mUnloadedOverlays at the same index in
  2015. // document order, so they end up in the reverse of the document
  2016. // order in mUnloadedOverlays.
  2017. // This is needed because the code in ResumeWalk loads the overlays
  2018. // by processing the last item of mUnloadedOverlays and removing it
  2019. // from the array.
  2020. mUnloadedOverlays.InsertElementAt(0, uri);
  2021. rv = NS_OK;
  2022. } else if (rv == NS_ERROR_MALFORMED_URI) {
  2023. // The URL is bad, move along. Don't propagate for now.
  2024. // XXX report this to the Error Console (bug 359846)
  2025. rv = NS_OK;
  2026. }
  2027. return rv;
  2028. }
  2029. nsresult
  2030. XULDocument::AddChromeOverlays()
  2031. {
  2032. nsresult rv;
  2033. nsCOMPtr<nsIURI> docUri = mCurrentPrototype->GetURI();
  2034. /* overlays only apply to chrome or about URIs */
  2035. if (!IsOverlayAllowed(docUri)) return NS_OK;
  2036. nsCOMPtr<nsIXULOverlayProvider> chromeReg =
  2037. mozilla::services::GetXULOverlayProviderService();
  2038. // In embedding situations, the chrome registry may not provide overlays,
  2039. // or even exist at all; that's OK.
  2040. NS_ENSURE_TRUE(chromeReg, NS_OK);
  2041. nsCOMPtr<nsISimpleEnumerator> overlays;
  2042. rv = chromeReg->GetXULOverlays(docUri, getter_AddRefs(overlays));
  2043. NS_ENSURE_SUCCESS(rv, rv);
  2044. bool moreOverlays;
  2045. nsCOMPtr<nsISupports> next;
  2046. nsCOMPtr<nsIURI> uri;
  2047. while (NS_SUCCEEDED(rv = overlays->HasMoreElements(&moreOverlays)) &&
  2048. moreOverlays) {
  2049. rv = overlays->GetNext(getter_AddRefs(next));
  2050. if (NS_FAILED(rv) || !next) break;
  2051. uri = do_QueryInterface(next);
  2052. if (!uri) {
  2053. NS_ERROR("Chrome registry handed me a non-nsIURI object!");
  2054. continue;
  2055. }
  2056. // Same comment as in XULDocument::InsertXULOverlayPI
  2057. mUnloadedOverlays.InsertElementAt(0, uri);
  2058. }
  2059. return rv;
  2060. }
  2061. NS_IMETHODIMP
  2062. XULDocument::LoadOverlay(const nsAString& aURL, nsIObserver* aObserver)
  2063. {
  2064. nsresult rv;
  2065. nsCOMPtr<nsIURI> uri;
  2066. rv = NS_NewURI(getter_AddRefs(uri), aURL, nullptr);
  2067. if (NS_FAILED(rv)) return rv;
  2068. if (aObserver) {
  2069. nsIObserver* obs = nullptr;
  2070. if (!mOverlayLoadObservers) {
  2071. mOverlayLoadObservers = new nsInterfaceHashtable<nsURIHashKey,nsIObserver>;
  2072. }
  2073. obs = mOverlayLoadObservers->GetWeak(uri);
  2074. if (obs) {
  2075. // We don't support loading the same overlay twice into the same
  2076. // document - that doesn't make sense anyway.
  2077. return NS_ERROR_FAILURE;
  2078. }
  2079. mOverlayLoadObservers->Put(uri, aObserver);
  2080. }
  2081. bool shouldReturn, failureFromContent;
  2082. rv = LoadOverlayInternal(uri, true, &shouldReturn, &failureFromContent);
  2083. if (NS_FAILED(rv) && mOverlayLoadObservers)
  2084. mOverlayLoadObservers->Remove(uri); // remove the observer if LoadOverlayInternal generated an error
  2085. return rv;
  2086. }
  2087. nsresult
  2088. XULDocument::LoadOverlayInternal(nsIURI* aURI, bool aIsDynamic,
  2089. bool* aShouldReturn,
  2090. bool* aFailureFromContent)
  2091. {
  2092. nsresult rv;
  2093. *aShouldReturn = false;
  2094. *aFailureFromContent = false;
  2095. if (MOZ_LOG_TEST(gXULLog, LogLevel::Debug)) {
  2096. nsCOMPtr<nsIURI> uri;
  2097. mChannel->GetOriginalURI(getter_AddRefs(uri));
  2098. MOZ_LOG(gXULLog, LogLevel::Debug,
  2099. ("xul: %s loading overlay %s",
  2100. uri ? uri->GetSpecOrDefault().get() : "",
  2101. aURI->GetSpecOrDefault().get()));
  2102. }
  2103. if (aIsDynamic)
  2104. mResolutionPhase = nsForwardReference::eStart;
  2105. // Look in the prototype cache for the prototype document with
  2106. // the specified overlay URI. Only use the cache if the containing
  2107. // document is chrome otherwise it may not have a system principal and
  2108. // the cached document will, see bug 565610.
  2109. bool overlayIsChrome = IsChromeURI(aURI);
  2110. bool documentIsChrome = IsChromeURI(mDocumentURI);
  2111. mCurrentPrototype = overlayIsChrome && documentIsChrome ?
  2112. nsXULPrototypeCache::GetInstance()->GetPrototype(aURI) : nullptr;
  2113. // Same comment as nsChromeProtocolHandler::NewChannel and
  2114. // XULDocument::StartDocumentLoad
  2115. // - Ben Goodger
  2116. //
  2117. // We don't abort on failure here because there are too many valid
  2118. // cases that can return failure, and the null-ness of |proto| is
  2119. // enough to trigger the fail-safe parse-from-disk solution.
  2120. // Example failure cases (for reference) include:
  2121. //
  2122. // NS_ERROR_NOT_AVAILABLE: the URI was not found in the FastLoad file,
  2123. // parse from disk
  2124. // other: the FastLoad file, XUL.mfl, could not be found, probably
  2125. // due to being accessed before a profile has been selected
  2126. // (e.g. loading chrome for the profile manager itself).
  2127. // The .xul file must be parsed from disk.
  2128. bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
  2129. if (useXULCache && mCurrentPrototype) {
  2130. bool loaded;
  2131. rv = mCurrentPrototype->AwaitLoadDone(this, &loaded);
  2132. if (NS_FAILED(rv)) return rv;
  2133. if (! loaded) {
  2134. // Return to the main event loop and eagerly await the
  2135. // prototype overlay load's completion. When the content
  2136. // sink completes, it will trigger an EndLoad(), which'll
  2137. // wind us back up here, in ResumeWalk().
  2138. *aShouldReturn = true;
  2139. return NS_OK;
  2140. }
  2141. MOZ_LOG(gXULLog, LogLevel::Debug, ("xul: overlay was cached"));
  2142. // Found the overlay's prototype in the cache, fully loaded. If
  2143. // this is a dynamic overlay, this will call ResumeWalk.
  2144. // Otherwise, we'll return to ResumeWalk, which called us.
  2145. return OnPrototypeLoadDone(aIsDynamic);
  2146. }
  2147. else {
  2148. // Not there. Initiate a load.
  2149. MOZ_LOG(gXULLog, LogLevel::Debug, ("xul: overlay was not cached"));
  2150. if (mIsGoingAway) {
  2151. MOZ_LOG(gXULLog, LogLevel::Debug, ("xul: ...and document already destroyed"));
  2152. return NS_ERROR_NOT_AVAILABLE;
  2153. }
  2154. // We'll set the right principal on the proto doc when we get
  2155. // OnStartRequest from the parser, so just pass in a null principal for
  2156. // now.
  2157. nsCOMPtr<nsIParser> parser;
  2158. rv = PrepareToLoadPrototype(aURI, "view", nullptr, getter_AddRefs(parser));
  2159. if (NS_FAILED(rv)) return rv;
  2160. // Predicate mIsWritingFastLoad on the XUL cache being enabled,
  2161. // so we don't have to re-check whether the cache is enabled all
  2162. // the time.
  2163. mIsWritingFastLoad = useXULCache;
  2164. nsCOMPtr<nsIStreamListener> listener = do_QueryInterface(parser);
  2165. if (! listener)
  2166. return NS_ERROR_UNEXPECTED;
  2167. // Add an observer to the parser; this'll get called when
  2168. // Necko fires its On[Start|Stop]Request() notifications,
  2169. // and will let us recover from a missing overlay.
  2170. RefPtr<ParserObserver> parserObserver =
  2171. new ParserObserver(this, mCurrentPrototype);
  2172. parser->Parse(aURI, parserObserver);
  2173. parserObserver = nullptr;
  2174. nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
  2175. nsCOMPtr<nsIChannel> channel;
  2176. // Set the owner of the channel to be our principal so
  2177. // that the overlay's JSObjects etc end up being created
  2178. // with the right principal and in the correct
  2179. // compartment.
  2180. rv = NS_NewChannel(getter_AddRefs(channel),
  2181. aURI,
  2182. NodePrincipal(),
  2183. nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS |
  2184. nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
  2185. nsIContentPolicy::TYPE_OTHER,
  2186. group);
  2187. if (NS_SUCCEEDED(rv)) {
  2188. rv = channel->AsyncOpen2(listener);
  2189. }
  2190. if (NS_FAILED(rv)) {
  2191. // Abandon this prototype
  2192. mCurrentPrototype = nullptr;
  2193. // The parser won't get an OnStartRequest and
  2194. // OnStopRequest, so it needs a Terminate.
  2195. parser->Terminate();
  2196. // Just move on to the next overlay.
  2197. ReportMissingOverlay(aURI);
  2198. // XXX the error could indicate an internal error as well...
  2199. *aFailureFromContent = true;
  2200. return rv;
  2201. }
  2202. // If it's a 'chrome:' prototype document, then put it into
  2203. // the prototype cache; other XUL documents will be reloaded
  2204. // each time. We must do this after AsyncOpen,
  2205. // or chrome code will wrongly create a cached chrome channel
  2206. // instead of a real one. Prototypes are only cached when the
  2207. // document to be overlayed is chrome to avoid caching overlay
  2208. // scripts with incorrect principals, see bug 565610.
  2209. if (useXULCache && overlayIsChrome && documentIsChrome) {
  2210. nsXULPrototypeCache::GetInstance()->PutPrototype(mCurrentPrototype);
  2211. }
  2212. // Return to the main event loop and eagerly await the
  2213. // overlay load's completion. When the content sink
  2214. // completes, it will trigger an EndLoad(), which'll wind
  2215. // us back in ResumeWalk().
  2216. if (!aIsDynamic)
  2217. *aShouldReturn = true;
  2218. }
  2219. return NS_OK;
  2220. }
  2221. nsresult
  2222. XULDocument::ResumeWalk()
  2223. {
  2224. // Walk the prototype and build the delegate content model. The
  2225. // walk is performed in a top-down, left-to-right fashion. That
  2226. // is, a parent is built before any of its children; a node is
  2227. // only built after all of its siblings to the left are fully
  2228. // constructed.
  2229. //
  2230. // It is interruptable so that transcluded documents (e.g.,
  2231. // <html:script src="..." />) can be properly re-loaded if the
  2232. // cached copy of the document becomes stale.
  2233. nsresult rv;
  2234. nsCOMPtr<nsIURI> overlayURI =
  2235. mCurrentPrototype ? mCurrentPrototype->GetURI() : nullptr;
  2236. while (1) {
  2237. // Begin (or resume) walking the current prototype.
  2238. while (mContextStack.Depth() > 0) {
  2239. // Look at the top of the stack to determine what we're
  2240. // currently working on.
  2241. // This will always be a node already constructed and
  2242. // inserted to the actual document.
  2243. nsXULPrototypeElement* proto;
  2244. nsCOMPtr<nsIContent> element;
  2245. int32_t indx; // all children of proto before indx (not
  2246. // inclusive) have already been constructed
  2247. rv = mContextStack.Peek(&proto, getter_AddRefs(element), &indx);
  2248. if (NS_FAILED(rv)) return rv;
  2249. if (indx >= (int32_t)proto->mChildren.Length()) {
  2250. if (element) {
  2251. // We've processed all of the prototype's children. If
  2252. // we're in the master prototype, do post-order
  2253. // document-level hookup. (An overlay will get its
  2254. // document hookup done when it's successfully
  2255. // resolved.)
  2256. if (mState == eState_Master) {
  2257. AddElementToDocumentPost(element->AsElement());
  2258. if (element->NodeInfo()->Equals(nsGkAtoms::style,
  2259. kNameSpaceID_XHTML) ||
  2260. element->NodeInfo()->Equals(nsGkAtoms::style,
  2261. kNameSpaceID_SVG)) {
  2262. // XXX sucks that we have to do this -
  2263. // see bug 370111
  2264. nsCOMPtr<nsIStyleSheetLinkingElement> ssle =
  2265. do_QueryInterface(element);
  2266. NS_ASSERTION(ssle, "<html:style> doesn't implement "
  2267. "nsIStyleSheetLinkingElement?");
  2268. bool willNotify;
  2269. bool isAlternate;
  2270. ssle->UpdateStyleSheet(nullptr, &willNotify,
  2271. &isAlternate);
  2272. }
  2273. }
  2274. }
  2275. // Now pop the context stack back up to the parent
  2276. // element and continue the prototype walk.
  2277. mContextStack.Pop();
  2278. continue;
  2279. }
  2280. // Grab the next child, and advance the current context stack
  2281. // to the next sibling to our right.
  2282. nsXULPrototypeNode* childproto = proto->mChildren[indx];
  2283. mContextStack.SetTopIndex(++indx);
  2284. // Whether we're in the "first ply" of an overlay:
  2285. // the "hookup" nodes. In the case !processingOverlayHookupNodes,
  2286. // we're in the master document -or- we're in an overlay, and far
  2287. // enough down into the overlay's content that we can simply build
  2288. // the delegates and attach them to the parent node.
  2289. bool processingOverlayHookupNodes = (mState == eState_Overlay) &&
  2290. (mContextStack.Depth() == 1);
  2291. NS_ASSERTION(element || processingOverlayHookupNodes,
  2292. "no element on context stack");
  2293. switch (childproto->mType) {
  2294. case nsXULPrototypeNode::eType_Element: {
  2295. // An 'element', which may contain more content.
  2296. nsXULPrototypeElement* protoele =
  2297. static_cast<nsXULPrototypeElement*>(childproto);
  2298. RefPtr<Element> child;
  2299. if (!processingOverlayHookupNodes) {
  2300. rv = CreateElementFromPrototype(protoele,
  2301. getter_AddRefs(child),
  2302. false);
  2303. if (NS_FAILED(rv)) return rv;
  2304. // ...and append it to the content model.
  2305. rv = element->AppendChildTo(child, false);
  2306. if (NS_FAILED(rv)) return rv;
  2307. // If we're only restoring persisted things on
  2308. // some elements, store the ID here to do that.
  2309. if (mRestrictPersistence) {
  2310. nsIAtom* id = child->GetID();
  2311. if (id) {
  2312. mPersistenceIds.PutEntry(nsDependentAtomString(id));
  2313. }
  2314. }
  2315. // do pre-order document-level hookup, but only if
  2316. // we're in the master document. For an overlay,
  2317. // this will happen when the overlay is
  2318. // successfully resolved.
  2319. if (mState == eState_Master)
  2320. AddElementToDocumentPre(child);
  2321. }
  2322. else {
  2323. // We're in the "first ply" of an overlay: the
  2324. // "hookup" nodes. Create an 'overlay' element so
  2325. // that we can continue to build content, and
  2326. // enter a forward reference so we can hook it up
  2327. // later.
  2328. rv = CreateOverlayElement(protoele, getter_AddRefs(child));
  2329. if (NS_FAILED(rv)) return rv;
  2330. }
  2331. // If it has children, push the element onto the context
  2332. // stack and begin to process them.
  2333. if (protoele->mChildren.Length() > 0) {
  2334. rv = mContextStack.Push(protoele, child);
  2335. if (NS_FAILED(rv)) return rv;
  2336. }
  2337. else {
  2338. if (mState == eState_Master) {
  2339. // If there are no children, and we're in the
  2340. // master document, do post-order document hookup
  2341. // immediately.
  2342. AddElementToDocumentPost(child);
  2343. }
  2344. }
  2345. }
  2346. break;
  2347. case nsXULPrototypeNode::eType_Script: {
  2348. // A script reference. Execute the script immediately;
  2349. // this may have side effects in the content model.
  2350. nsXULPrototypeScript* scriptproto =
  2351. static_cast<nsXULPrototypeScript*>(childproto);
  2352. if (scriptproto->mSrcURI) {
  2353. // A transcluded script reference; this may
  2354. // "block" our prototype walk if the script isn't
  2355. // cached, or the cached copy of the script is
  2356. // stale and must be reloaded.
  2357. bool blocked;
  2358. rv = LoadScript(scriptproto, &blocked);
  2359. // If the script cannot be loaded, just keep going!
  2360. if (NS_SUCCEEDED(rv) && blocked)
  2361. return NS_OK;
  2362. }
  2363. else if (scriptproto->HasScriptObject()) {
  2364. // An inline script
  2365. rv = ExecuteScript(scriptproto);
  2366. if (NS_FAILED(rv)) return rv;
  2367. }
  2368. }
  2369. break;
  2370. case nsXULPrototypeNode::eType_Text: {
  2371. // A simple text node.
  2372. if (!processingOverlayHookupNodes) {
  2373. // This does mean that text nodes that are direct children
  2374. // of <overlay> get ignored.
  2375. RefPtr<nsTextNode> text =
  2376. new nsTextNode(mNodeInfoManager);
  2377. nsXULPrototypeText* textproto =
  2378. static_cast<nsXULPrototypeText*>(childproto);
  2379. text->SetText(textproto->mValue, false);
  2380. rv = element->AppendChildTo(text, false);
  2381. NS_ENSURE_SUCCESS(rv, rv);
  2382. }
  2383. }
  2384. break;
  2385. case nsXULPrototypeNode::eType_PI: {
  2386. nsXULPrototypePI* piProto =
  2387. static_cast<nsXULPrototypePI*>(childproto);
  2388. // <?xul-overlay?> and <?xml-stylesheet?> don't have effect
  2389. // outside the prolog, like they used to. Issue a warning.
  2390. if (piProto->mTarget.EqualsLiteral("xml-stylesheet") ||
  2391. piProto->mTarget.EqualsLiteral("xul-overlay")) {
  2392. const char16_t* params[] = { piProto->mTarget.get() };
  2393. nsContentUtils::ReportToConsole(
  2394. nsIScriptError::warningFlag,
  2395. NS_LITERAL_CSTRING("XUL Document"), nullptr,
  2396. nsContentUtils::eXUL_PROPERTIES,
  2397. "PINotInProlog",
  2398. params, ArrayLength(params),
  2399. overlayURI);
  2400. }
  2401. nsIContent* parent = processingOverlayHookupNodes ?
  2402. GetRootElement() : element.get();
  2403. if (parent) {
  2404. // an inline script could have removed the root element
  2405. rv = CreateAndInsertPI(piProto, parent,
  2406. parent->GetChildCount());
  2407. NS_ENSURE_SUCCESS(rv, rv);
  2408. }
  2409. }
  2410. break;
  2411. default:
  2412. NS_NOTREACHED("Unexpected nsXULPrototypeNode::Type value");
  2413. }
  2414. }
  2415. // Once we get here, the context stack will have been
  2416. // depleted. That means that the entire prototype has been
  2417. // walked and content has been constructed.
  2418. // If we're not already, mark us as now processing overlays.
  2419. mState = eState_Overlay;
  2420. // If there are no overlay URIs, then we're done.
  2421. uint32_t count = mUnloadedOverlays.Length();
  2422. if (! count)
  2423. break;
  2424. nsCOMPtr<nsIURI> uri = mUnloadedOverlays[count-1];
  2425. mUnloadedOverlays.RemoveElementAt(count - 1);
  2426. bool shouldReturn, failureFromContent;
  2427. rv = LoadOverlayInternal(uri, false, &shouldReturn,
  2428. &failureFromContent);
  2429. if (failureFromContent)
  2430. // The failure |rv| was the result of a problem in the content
  2431. // rather than an unexpected problem in our implementation, so
  2432. // just continue with the next overlay.
  2433. continue;
  2434. if (NS_FAILED(rv))
  2435. return rv;
  2436. if (mOverlayLoadObservers) {
  2437. nsIObserver *obs = mOverlayLoadObservers->GetWeak(overlayURI);
  2438. if (obs) {
  2439. // This overlay has an unloaded overlay, so it will never
  2440. // notify. The best we can do is to notify for the unloaded
  2441. // overlay instead, assuming nobody is already notifiable
  2442. // for it. Note that this will confuse the observer.
  2443. if (!mOverlayLoadObservers->GetWeak(uri))
  2444. mOverlayLoadObservers->Put(uri, obs);
  2445. mOverlayLoadObservers->Remove(overlayURI);
  2446. }
  2447. }
  2448. if (shouldReturn)
  2449. return NS_OK;
  2450. overlayURI.swap(uri);
  2451. }
  2452. // If we get here, there is nothing left for us to walk. The content
  2453. // model is built and ready for layout.
  2454. rv = ResolveForwardReferences();
  2455. if (NS_FAILED(rv)) return rv;
  2456. ApplyPersistentAttributes();
  2457. mStillWalking = false;
  2458. if (mPendingSheets == 0) {
  2459. rv = DoneWalking();
  2460. }
  2461. return rv;
  2462. }
  2463. nsresult
  2464. XULDocument::DoneWalking()
  2465. {
  2466. NS_PRECONDITION(mPendingSheets == 0, "there are sheets to be loaded");
  2467. NS_PRECONDITION(!mStillWalking, "walk not done");
  2468. // XXXldb This is where we should really be setting the chromehidden
  2469. // attribute.
  2470. {
  2471. mozAutoDocUpdate updateBatch(this, UPDATE_STYLE, true);
  2472. uint32_t count = mOverlaySheets.Length();
  2473. for (uint32_t i = 0; i < count; ++i) {
  2474. AddStyleSheet(mOverlaySheets[i]);
  2475. }
  2476. }
  2477. mOverlaySheets.Clear();
  2478. if (!mDocumentLoaded) {
  2479. // Make sure we don't reenter here from StartLayout(). Note that
  2480. // setting mDocumentLoaded to true here means that if StartLayout()
  2481. // causes ResumeWalk() to be reentered, we'll take the other branch of
  2482. // the |if (!mDocumentLoaded)| check above and since
  2483. // mInitialLayoutComplete will be false will follow the else branch
  2484. // there too. See the big comment there for how such reentry can
  2485. // happen.
  2486. mDocumentLoaded = true;
  2487. NotifyPossibleTitleChange(false);
  2488. // Before starting layout, check whether we're a toplevel chrome
  2489. // window. If we are, set our chrome flags now, so that we don't have
  2490. // to restyle the whole frame tree after StartLayout.
  2491. nsCOMPtr<nsIDocShellTreeItem> item = GetDocShell();
  2492. if (item) {
  2493. nsCOMPtr<nsIDocShellTreeOwner> owner;
  2494. item->GetTreeOwner(getter_AddRefs(owner));
  2495. nsCOMPtr<nsIXULWindow> xulWin = do_GetInterface(owner);
  2496. if (xulWin) {
  2497. nsCOMPtr<nsIDocShell> xulWinShell;
  2498. xulWin->GetDocShell(getter_AddRefs(xulWinShell));
  2499. if (SameCOMIdentity(xulWinShell, item)) {
  2500. // We're the chrome document! Apply our chrome flags now.
  2501. xulWin->ApplyChromeFlags();
  2502. }
  2503. }
  2504. }
  2505. StartLayout();
  2506. if (mIsWritingFastLoad && IsChromeURI(mDocumentURI))
  2507. nsXULPrototypeCache::GetInstance()->WritePrototype(mMasterPrototype);
  2508. NS_ASSERTION(mDelayFrameLoaderInitialization,
  2509. "mDelayFrameLoaderInitialization should be true!");
  2510. mDelayFrameLoaderInitialization = false;
  2511. NS_WARNING_ASSERTION(
  2512. mUpdateNestLevel == 0,
  2513. "Constructing XUL document in middle of an update?");
  2514. if (mUpdateNestLevel == 0) {
  2515. MaybeInitializeFinalizeFrameLoaders();
  2516. }
  2517. NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad, (this));
  2518. // DispatchContentLoadedEvents undoes the onload-blocking we
  2519. // did in PrepareToWalk().
  2520. DispatchContentLoadedEvents();
  2521. mInitialLayoutComplete = true;
  2522. // Walk the set of pending load notifications and notify any observers.
  2523. // See below for detail.
  2524. if (mPendingOverlayLoadNotifications) {
  2525. nsInterfaceHashtable<nsURIHashKey,nsIObserver>* observers =
  2526. mOverlayLoadObservers.get();
  2527. for (auto iter = mPendingOverlayLoadNotifications->Iter();
  2528. !iter.Done();
  2529. iter.Next()) {
  2530. nsIURI* aKey = iter.Key();
  2531. iter.Data()->Observe(aKey, "xul-overlay-merged",
  2532. EmptyString().get());
  2533. if (observers) {
  2534. observers->Remove(aKey);
  2535. }
  2536. iter.Remove();
  2537. }
  2538. }
  2539. }
  2540. else {
  2541. if (mOverlayLoadObservers) {
  2542. nsCOMPtr<nsIURI> overlayURI = mCurrentPrototype->GetURI();
  2543. nsCOMPtr<nsIObserver> obs;
  2544. if (mInitialLayoutComplete) {
  2545. // We have completed initial layout, so just send the notification.
  2546. mOverlayLoadObservers->Get(overlayURI, getter_AddRefs(obs));
  2547. if (obs)
  2548. obs->Observe(overlayURI, "xul-overlay-merged", EmptyString().get());
  2549. mOverlayLoadObservers->Remove(overlayURI);
  2550. }
  2551. else {
  2552. // If we have not yet displayed the document for the first time
  2553. // (i.e. we came in here as the result of a dynamic overlay load
  2554. // which was spawned by a binding-attached event caused by
  2555. // StartLayout() on the master prototype - we must remember that
  2556. // this overlay has been merged and tell the listeners after
  2557. // StartLayout() is completely finished rather than doing so
  2558. // immediately - otherwise we may be executing code that needs to
  2559. // access XBL Binding implementations on nodes for which frames
  2560. // have not yet been constructed because their bindings have not
  2561. // yet been attached. This can be a race condition because dynamic
  2562. // overlay loading can take varying amounts of time depending on
  2563. // whether or not the overlay prototype is in the XUL cache. The
  2564. // most likely effect of this bug is odd UI initialization due to
  2565. // methods and properties that do not work.
  2566. // XXXbz really, we shouldn't be firing binding constructors
  2567. // until after StartLayout returns!
  2568. if (!mPendingOverlayLoadNotifications) {
  2569. mPendingOverlayLoadNotifications =
  2570. new nsInterfaceHashtable<nsURIHashKey,nsIObserver>;
  2571. }
  2572. mPendingOverlayLoadNotifications->Get(overlayURI, getter_AddRefs(obs));
  2573. if (!obs) {
  2574. mOverlayLoadObservers->Get(overlayURI, getter_AddRefs(obs));
  2575. NS_ASSERTION(obs, "null overlay load observer?");
  2576. mPendingOverlayLoadNotifications->Put(overlayURI, obs);
  2577. }
  2578. }
  2579. }
  2580. }
  2581. return NS_OK;
  2582. }
  2583. NS_IMETHODIMP
  2584. XULDocument::StyleSheetLoaded(StyleSheet* aSheet,
  2585. bool aWasAlternate,
  2586. nsresult aStatus)
  2587. {
  2588. if (!aWasAlternate) {
  2589. // Don't care about when alternate sheets finish loading
  2590. NS_ASSERTION(mPendingSheets > 0,
  2591. "Unexpected StyleSheetLoaded notification");
  2592. --mPendingSheets;
  2593. if (!mStillWalking && mPendingSheets == 0) {
  2594. return DoneWalking();
  2595. }
  2596. }
  2597. return NS_OK;
  2598. }
  2599. void
  2600. XULDocument::MaybeBroadcast()
  2601. {
  2602. // Only broadcast when not in an update and when safe to run scripts.
  2603. if (mUpdateNestLevel == 0 &&
  2604. (mDelayedAttrChangeBroadcasts.Length() ||
  2605. mDelayedBroadcasters.Length())) {
  2606. if (!nsContentUtils::IsSafeToRunScript()) {
  2607. if (!mInDestructor) {
  2608. nsContentUtils::AddScriptRunner(
  2609. NewRunnableMethod(this, &XULDocument::MaybeBroadcast));
  2610. }
  2611. return;
  2612. }
  2613. if (!mHandlingDelayedAttrChange) {
  2614. mHandlingDelayedAttrChange = true;
  2615. for (uint32_t i = 0; i < mDelayedAttrChangeBroadcasts.Length(); ++i) {
  2616. nsIAtom* attrName = mDelayedAttrChangeBroadcasts[i].mAttrName;
  2617. if (mDelayedAttrChangeBroadcasts[i].mNeedsAttrChange) {
  2618. nsCOMPtr<nsIContent> listener =
  2619. do_QueryInterface(mDelayedAttrChangeBroadcasts[i].mListener);
  2620. const nsString& value = mDelayedAttrChangeBroadcasts[i].mAttr;
  2621. if (mDelayedAttrChangeBroadcasts[i].mSetAttr) {
  2622. listener->SetAttr(kNameSpaceID_None, attrName, value,
  2623. true);
  2624. } else {
  2625. listener->UnsetAttr(kNameSpaceID_None, attrName,
  2626. true);
  2627. }
  2628. }
  2629. ExecuteOnBroadcastHandlerFor(mDelayedAttrChangeBroadcasts[i].mBroadcaster,
  2630. mDelayedAttrChangeBroadcasts[i].mListener,
  2631. attrName);
  2632. }
  2633. mDelayedAttrChangeBroadcasts.Clear();
  2634. mHandlingDelayedAttrChange = false;
  2635. }
  2636. uint32_t length = mDelayedBroadcasters.Length();
  2637. if (length) {
  2638. bool oldValue = mHandlingDelayedBroadcasters;
  2639. mHandlingDelayedBroadcasters = true;
  2640. nsTArray<nsDelayedBroadcastUpdate> delayedBroadcasters;
  2641. mDelayedBroadcasters.SwapElements(delayedBroadcasters);
  2642. for (uint32_t i = 0; i < length; ++i) {
  2643. SynchronizeBroadcastListener(delayedBroadcasters[i].mBroadcaster,
  2644. delayedBroadcasters[i].mListener,
  2645. delayedBroadcasters[i].mAttr);
  2646. }
  2647. mHandlingDelayedBroadcasters = oldValue;
  2648. }
  2649. }
  2650. }
  2651. void
  2652. XULDocument::EndUpdate(nsUpdateType aUpdateType)
  2653. {
  2654. XMLDocument::EndUpdate(aUpdateType);
  2655. MaybeBroadcast();
  2656. }
  2657. void
  2658. XULDocument::ReportMissingOverlay(nsIURI* aURI)
  2659. {
  2660. NS_PRECONDITION(aURI, "Must have a URI");
  2661. NS_ConvertUTF8toUTF16 utfSpec(aURI->GetSpecOrDefault());
  2662. const char16_t* params[] = { utfSpec.get() };
  2663. nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
  2664. NS_LITERAL_CSTRING("XUL Document"), this,
  2665. nsContentUtils::eXUL_PROPERTIES,
  2666. "MissingOverlay",
  2667. params, ArrayLength(params));
  2668. }
  2669. nsresult
  2670. XULDocument::LoadScript(nsXULPrototypeScript* aScriptProto, bool* aBlock)
  2671. {
  2672. // Load a transcluded script
  2673. nsresult rv;
  2674. bool isChromeDoc = IsChromeURI(mDocumentURI);
  2675. if (isChromeDoc && aScriptProto->HasScriptObject()) {
  2676. rv = ExecuteScript(aScriptProto);
  2677. // Ignore return value from execution, and don't block
  2678. *aBlock = false;
  2679. return NS_OK;
  2680. }
  2681. // Try the XUL script cache, in case two XUL documents source the same
  2682. // .js file (e.g., strres.js from navigator.xul and utilityOverlay.xul).
  2683. // XXXbe the cache relies on aScriptProto's GC root!
  2684. bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
  2685. if (isChromeDoc && useXULCache) {
  2686. JSScript* newScriptObject =
  2687. nsXULPrototypeCache::GetInstance()->GetScript(
  2688. aScriptProto->mSrcURI);
  2689. if (newScriptObject) {
  2690. // The script language for a proto must remain constant - we
  2691. // can't just change it for this unexpected language.
  2692. aScriptProto->Set(newScriptObject);
  2693. }
  2694. if (aScriptProto->HasScriptObject()) {
  2695. rv = ExecuteScript(aScriptProto);
  2696. // Ignore return value from execution, and don't block
  2697. *aBlock = false;
  2698. return NS_OK;
  2699. }
  2700. }
  2701. // Release script objects from FastLoad since we decided against using them
  2702. aScriptProto->UnlinkJSObjects();
  2703. // Set the current script prototype so that OnStreamComplete can report
  2704. // the right file if there are errors in the script.
  2705. NS_ASSERTION(!mCurrentScriptProto,
  2706. "still loading a script when starting another load?");
  2707. mCurrentScriptProto = aScriptProto;
  2708. if (isChromeDoc && aScriptProto->mSrcLoading) {
  2709. // Another XULDocument load has started, which is still in progress.
  2710. // Remember to ResumeWalk this document when the load completes.
  2711. mNextSrcLoadWaiter = aScriptProto->mSrcLoadWaiters;
  2712. aScriptProto->mSrcLoadWaiters = this;
  2713. NS_ADDREF_THIS();
  2714. }
  2715. else {
  2716. nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
  2717. // Note: the loader will keep itself alive while it's loading.
  2718. nsCOMPtr<nsIStreamLoader> loader;
  2719. rv = NS_NewStreamLoader(getter_AddRefs(loader),
  2720. aScriptProto->mSrcURI,
  2721. this, // aObserver
  2722. this, // aRequestingContext
  2723. nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS,
  2724. nsIContentPolicy::TYPE_INTERNAL_SCRIPT,
  2725. group);
  2726. if (NS_FAILED(rv)) {
  2727. mCurrentScriptProto = nullptr;
  2728. return rv;
  2729. }
  2730. aScriptProto->mSrcLoading = true;
  2731. }
  2732. // Block until OnStreamComplete resumes us.
  2733. *aBlock = true;
  2734. return NS_OK;
  2735. }
  2736. NS_IMETHODIMP
  2737. XULDocument::OnStreamComplete(nsIStreamLoader* aLoader,
  2738. nsISupports* context,
  2739. nsresult aStatus,
  2740. uint32_t stringLen,
  2741. const uint8_t* string)
  2742. {
  2743. nsCOMPtr<nsIRequest> request;
  2744. aLoader->GetRequest(getter_AddRefs(request));
  2745. nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
  2746. #ifdef DEBUG
  2747. // print a load error on bad status
  2748. if (NS_FAILED(aStatus)) {
  2749. if (channel) {
  2750. nsCOMPtr<nsIURI> uri;
  2751. channel->GetURI(getter_AddRefs(uri));
  2752. if (uri) {
  2753. printf("Failed to load %s\n", uri->GetSpecOrDefault().get());
  2754. }
  2755. }
  2756. }
  2757. #endif
  2758. // This is the completion routine that will be called when a
  2759. // transcluded script completes. Compile and execute the script
  2760. // if the load was successful, then continue building content
  2761. // from the prototype.
  2762. nsresult rv = aStatus;
  2763. NS_ASSERTION(mCurrentScriptProto && mCurrentScriptProto->mSrcLoading,
  2764. "script source not loading on unichar stream complete?");
  2765. if (!mCurrentScriptProto) {
  2766. // XXX Wallpaper for bug 270042
  2767. return NS_OK;
  2768. }
  2769. if (NS_SUCCEEDED(aStatus)) {
  2770. // If the including XUL document is a FastLoad document, and we're
  2771. // compiling an out-of-line script (one with src=...), then we must
  2772. // be writing a new FastLoad file. If we were reading this script
  2773. // from the FastLoad file, XULContentSinkImpl::OpenScript (over in
  2774. // nsXULContentSink.cpp) would have already deserialized a non-null
  2775. // script->mScriptObject, causing control flow at the top of LoadScript
  2776. // not to reach here.
  2777. nsCOMPtr<nsIURI> uri = mCurrentScriptProto->mSrcURI;
  2778. // XXX should also check nsIHttpChannel::requestSucceeded
  2779. MOZ_ASSERT(!mOffThreadCompiling && (mOffThreadCompileStringLength == 0 &&
  2780. !mOffThreadCompileStringBuf),
  2781. "XULDocument can't load multiple scripts at once");
  2782. rv = ScriptLoader::ConvertToUTF16(channel, string, stringLen,
  2783. EmptyString(), this,
  2784. mOffThreadCompileStringBuf,
  2785. mOffThreadCompileStringLength);
  2786. if (NS_SUCCEEDED(rv)) {
  2787. // Attempt to give ownership of the buffer to the JS engine. If
  2788. // we hit offthread compilation, however, we will have to take it
  2789. // back below in order to keep the memory alive until compilation
  2790. // completes.
  2791. JS::SourceBufferHolder srcBuf(mOffThreadCompileStringBuf,
  2792. mOffThreadCompileStringLength,
  2793. JS::SourceBufferHolder::GiveOwnership);
  2794. mOffThreadCompileStringBuf = nullptr;
  2795. mOffThreadCompileStringLength = 0;
  2796. rv = mCurrentScriptProto->Compile(srcBuf, uri, 1, this, this);
  2797. if (NS_SUCCEEDED(rv) && !mCurrentScriptProto->HasScriptObject()) {
  2798. // We will be notified via OnOffThreadCompileComplete when the
  2799. // compile finishes. Keep the contents of the compiled script
  2800. // alive until the compilation finishes.
  2801. mOffThreadCompiling = true;
  2802. // If the JS engine did not take the source buffer, then take
  2803. // it back here to ensure it remains alive.
  2804. mOffThreadCompileStringBuf = srcBuf.take();
  2805. if (mOffThreadCompileStringBuf) {
  2806. mOffThreadCompileStringLength = srcBuf.length();
  2807. }
  2808. BlockOnload();
  2809. return NS_OK;
  2810. }
  2811. }
  2812. }
  2813. return OnScriptCompileComplete(mCurrentScriptProto->GetScriptObject(), rv);
  2814. }
  2815. NS_IMETHODIMP
  2816. XULDocument::OnScriptCompileComplete(JSScript* aScript, nsresult aStatus)
  2817. {
  2818. // When compiling off thread the script will not have been attached to the
  2819. // script proto yet.
  2820. if (aScript && !mCurrentScriptProto->HasScriptObject())
  2821. mCurrentScriptProto->Set(aScript);
  2822. // Allow load events to be fired once off thread compilation finishes.
  2823. if (mOffThreadCompiling) {
  2824. mOffThreadCompiling = false;
  2825. UnblockOnload(false);
  2826. }
  2827. // After compilation finishes the script's characters are no longer needed.
  2828. if (mOffThreadCompileStringBuf) {
  2829. js_free(mOffThreadCompileStringBuf);
  2830. mOffThreadCompileStringBuf = nullptr;
  2831. mOffThreadCompileStringLength = 0;
  2832. }
  2833. // Clear mCurrentScriptProto now, but save it first for use below in
  2834. // the execute code, and in the while loop that resumes walks of other
  2835. // documents that raced to load this script.
  2836. nsXULPrototypeScript* scriptProto = mCurrentScriptProto;
  2837. mCurrentScriptProto = nullptr;
  2838. // Clear the prototype's loading flag before executing the script or
  2839. // resuming document walks, in case any of those control flows starts a
  2840. // new script load.
  2841. scriptProto->mSrcLoading = false;
  2842. nsresult rv = aStatus;
  2843. if (NS_SUCCEEDED(rv)) {
  2844. rv = ExecuteScript(scriptProto);
  2845. // If the XUL cache is enabled, save the script object there in
  2846. // case different XUL documents source the same script.
  2847. //
  2848. // But don't save the script in the cache unless the master XUL
  2849. // document URL is a chrome: URL. It is valid for a URL such as
  2850. // about:config to translate into a master document URL, whose
  2851. // prototype document nodes -- including prototype scripts that
  2852. // hold GC roots protecting their mJSObject pointers -- are not
  2853. // cached in the XUL prototype cache. See StartDocumentLoad,
  2854. // the fillXULCache logic.
  2855. //
  2856. // A document such as about:config is free to load a script via
  2857. // a URL such as chrome://global/content/config.js, and we must
  2858. // not cache that script object without a prototype cache entry
  2859. // containing a companion nsXULPrototypeScript node that owns a
  2860. // GC root protecting the script object. Otherwise, the script
  2861. // cache entry will dangle once the uncached prototype document
  2862. // is released when its owning XULDocument is unloaded.
  2863. //
  2864. // (See http://bugzilla.mozilla.org/show_bug.cgi?id=98207 for
  2865. // the true crime story.)
  2866. bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
  2867. if (useXULCache && IsChromeURI(mDocumentURI) && scriptProto->HasScriptObject()) {
  2868. JS::Rooted<JSScript*> script(RootingCx(), scriptProto->GetScriptObject());
  2869. nsXULPrototypeCache::GetInstance()->PutScript(
  2870. scriptProto->mSrcURI, script);
  2871. }
  2872. if (mIsWritingFastLoad && mCurrentPrototype != mMasterPrototype) {
  2873. // If we are loading an overlay script, try to serialize
  2874. // it to the FastLoad file here. Master scripts will be
  2875. // serialized when the master prototype document gets
  2876. // written, at the bottom of ResumeWalk. That way, master
  2877. // out-of-line scripts are serialized in the same order that
  2878. // they'll be read, in the FastLoad file, which reduces the
  2879. // number of seeks that dump the underlying stream's buffer.
  2880. //
  2881. // Ignore the return value, as we don't need to propagate
  2882. // a failure to write to the FastLoad file, because this
  2883. // method aborts that whole process on error.
  2884. scriptProto->SerializeOutOfLine(nullptr, mCurrentPrototype);
  2885. }
  2886. // ignore any evaluation errors
  2887. }
  2888. rv = ResumeWalk();
  2889. // Load a pointer to the prototype-script's list of XULDocuments who
  2890. // raced to load the same script
  2891. XULDocument** docp = &scriptProto->mSrcLoadWaiters;
  2892. // Resume walking other documents that waited for this one's load, first
  2893. // executing the script we just compiled, in each doc's script context
  2894. XULDocument* doc;
  2895. while ((doc = *docp) != nullptr) {
  2896. NS_ASSERTION(doc->mCurrentScriptProto == scriptProto,
  2897. "waiting for wrong script to load?");
  2898. doc->mCurrentScriptProto = nullptr;
  2899. // Unlink doc from scriptProto's list before executing and resuming
  2900. *docp = doc->mNextSrcLoadWaiter;
  2901. doc->mNextSrcLoadWaiter = nullptr;
  2902. // Execute only if we loaded and compiled successfully, then resume
  2903. if (NS_SUCCEEDED(aStatus) && scriptProto->HasScriptObject()) {
  2904. doc->ExecuteScript(scriptProto);
  2905. }
  2906. doc->ResumeWalk();
  2907. NS_RELEASE(doc);
  2908. }
  2909. return rv;
  2910. }
  2911. nsresult
  2912. XULDocument::ExecuteScript(nsXULPrototypeScript *aScript)
  2913. {
  2914. NS_PRECONDITION(aScript != nullptr, "null ptr");
  2915. NS_ENSURE_TRUE(aScript, NS_ERROR_NULL_POINTER);
  2916. NS_ENSURE_TRUE(mScriptGlobalObject, NS_ERROR_NOT_INITIALIZED);
  2917. nsresult rv;
  2918. rv = mScriptGlobalObject->EnsureScriptEnvironment();
  2919. NS_ENSURE_SUCCESS(rv, rv);
  2920. // Execute the precompiled script with the given version
  2921. nsAutoMicroTask mt;
  2922. // We're about to run script via JS::CloneAndExecuteScript, so we need an
  2923. // AutoEntryScript. This is Gecko specific and not in any spec.
  2924. AutoEntryScript aes(mScriptGlobalObject, "precompiled XUL <script> element");
  2925. JSContext* cx = aes.cx();
  2926. JS::Rooted<JSScript*> scriptObject(cx, aScript->GetScriptObject());
  2927. NS_ENSURE_TRUE(scriptObject, NS_ERROR_UNEXPECTED);
  2928. JS::Rooted<JSObject*> baseGlobal(cx, JS::CurrentGlobalOrNull(cx));
  2929. NS_ENSURE_TRUE(xpc::Scriptability::Get(baseGlobal).Allowed(), NS_OK);
  2930. JSAddonId* addonId = mCurrentPrototype ? MapURIToAddonID(mCurrentPrototype->GetURI()) : nullptr;
  2931. JS::Rooted<JSObject*> global(cx, xpc::GetAddonScope(cx, baseGlobal, addonId));
  2932. NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
  2933. JS::ExposeObjectToActiveJS(global);
  2934. JSAutoCompartment ac(cx, global);
  2935. // The script is in the compilation scope. Clone it into the target scope
  2936. // and execute it. On failure, ~AutoScriptEntry will handle exceptions, so
  2937. // there is no need to manually check the return value.
  2938. JS::RootedValue rval(cx);
  2939. JS::CloneAndExecuteScript(cx, scriptObject, &rval);
  2940. return NS_OK;
  2941. }
  2942. nsresult
  2943. XULDocument::CreateElementFromPrototype(nsXULPrototypeElement* aPrototype,
  2944. Element** aResult,
  2945. bool aIsRoot)
  2946. {
  2947. // Create a content model element from a prototype element.
  2948. NS_PRECONDITION(aPrototype != nullptr, "null ptr");
  2949. if (! aPrototype)
  2950. return NS_ERROR_NULL_POINTER;
  2951. *aResult = nullptr;
  2952. nsresult rv = NS_OK;
  2953. if (MOZ_LOG_TEST(gXULLog, LogLevel::Debug)) {
  2954. MOZ_LOG(gXULLog, LogLevel::Debug,
  2955. ("xul: creating <%s> from prototype",
  2956. NS_ConvertUTF16toUTF8(aPrototype->mNodeInfo->QualifiedName()).get()));
  2957. }
  2958. RefPtr<Element> result;
  2959. if (aPrototype->mNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) {
  2960. // If it's a XUL element, it'll be lightweight until somebody
  2961. // monkeys with it.
  2962. rv = nsXULElement::Create(aPrototype, this, true, aIsRoot, getter_AddRefs(result));
  2963. if (NS_FAILED(rv)) return rv;
  2964. }
  2965. else {
  2966. // If it's not a XUL element, it's gonna be heavyweight no matter
  2967. // what. So we need to copy everything out of the prototype
  2968. // into the element. Get a nodeinfo from our nodeinfo manager
  2969. // for this node.
  2970. RefPtr<mozilla::dom::NodeInfo> newNodeInfo;
  2971. newNodeInfo = mNodeInfoManager->GetNodeInfo(aPrototype->mNodeInfo->NameAtom(),
  2972. aPrototype->mNodeInfo->GetPrefixAtom(),
  2973. aPrototype->mNodeInfo->NamespaceID(),
  2974. nsIDOMNode::ELEMENT_NODE);
  2975. if (!newNodeInfo) return NS_ERROR_OUT_OF_MEMORY;
  2976. RefPtr<mozilla::dom::NodeInfo> xtfNi = newNodeInfo;
  2977. rv = NS_NewElement(getter_AddRefs(result), newNodeInfo.forget(),
  2978. NOT_FROM_PARSER);
  2979. if (NS_FAILED(rv))
  2980. return rv;
  2981. rv = AddAttributes(aPrototype, result);
  2982. if (NS_FAILED(rv)) return rv;
  2983. }
  2984. result.forget(aResult);
  2985. return NS_OK;
  2986. }
  2987. nsresult
  2988. XULDocument::CreateOverlayElement(nsXULPrototypeElement* aPrototype,
  2989. Element** aResult)
  2990. {
  2991. nsresult rv;
  2992. RefPtr<Element> element;
  2993. rv = CreateElementFromPrototype(aPrototype, getter_AddRefs(element), false);
  2994. if (NS_FAILED(rv)) return rv;
  2995. OverlayForwardReference* fwdref =
  2996. new OverlayForwardReference(this, element);
  2997. // transferring ownership to ya...
  2998. rv = AddForwardReference(fwdref);
  2999. if (NS_FAILED(rv)) return rv;
  3000. element.forget(aResult);
  3001. return NS_OK;
  3002. }
  3003. nsresult
  3004. XULDocument::AddAttributes(nsXULPrototypeElement* aPrototype,
  3005. nsIContent* aElement)
  3006. {
  3007. nsresult rv;
  3008. for (uint32_t i = 0; i < aPrototype->mNumAttributes; ++i) {
  3009. nsXULPrototypeAttribute* protoattr = &(aPrototype->mAttributes[i]);
  3010. nsAutoString valueStr;
  3011. protoattr->mValue.ToString(valueStr);
  3012. rv = aElement->SetAttr(protoattr->mName.NamespaceID(),
  3013. protoattr->mName.LocalName(),
  3014. protoattr->mName.GetPrefix(),
  3015. valueStr,
  3016. false);
  3017. if (NS_FAILED(rv)) return rv;
  3018. }
  3019. return NS_OK;
  3020. }
  3021. nsresult
  3022. XULDocument::CheckTemplateBuilderHookup(nsIContent* aElement,
  3023. bool* aNeedsHookup)
  3024. {
  3025. // See if the element already has a `database' attribute. If it
  3026. // does, then the template builder has already been created.
  3027. //
  3028. // XXX This approach will crash and burn (well, maybe not _that_
  3029. // bad) if aElement is not a XUL element.
  3030. //
  3031. // XXXvarga Do we still want to support non XUL content?
  3032. nsCOMPtr<nsIDOMXULElement> xulElement = do_QueryInterface(aElement);
  3033. if (xulElement) {
  3034. nsCOMPtr<nsIRDFCompositeDataSource> ds;
  3035. xulElement->GetDatabase(getter_AddRefs(ds));
  3036. if (ds) {
  3037. *aNeedsHookup = false;
  3038. return NS_OK;
  3039. }
  3040. }
  3041. // Check aElement for a 'datasources' attribute, if it has
  3042. // one a XUL template builder needs to be hooked up.
  3043. *aNeedsHookup = aElement->HasAttr(kNameSpaceID_None,
  3044. nsGkAtoms::datasources);
  3045. return NS_OK;
  3046. }
  3047. /* static */ nsresult
  3048. XULDocument::CreateTemplateBuilder(nsIContent* aElement)
  3049. {
  3050. // Check if need to construct a tree builder or content builder.
  3051. bool isTreeBuilder = false;
  3052. // return successful if the element is not is a document, as an inline
  3053. // script could have removed it
  3054. nsIDocument* document = aElement->GetUncomposedDoc();
  3055. NS_ENSURE_TRUE(document, NS_OK);
  3056. int32_t nameSpaceID;
  3057. nsIAtom* baseTag = document->BindingManager()->
  3058. ResolveTag(aElement, &nameSpaceID);
  3059. if ((nameSpaceID == kNameSpaceID_XUL) && (baseTag == nsGkAtoms::tree)) {
  3060. // By default, we build content for a tree and then we attach
  3061. // the tree content view. However, if the `dont-build-content'
  3062. // flag is set, then we we'll attach a tree builder which
  3063. // directly implements the tree view.
  3064. nsAutoString flags;
  3065. aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::flags, flags);
  3066. if (flags.Find(NS_LITERAL_STRING("dont-build-content")) >= 0) {
  3067. isTreeBuilder = true;
  3068. }
  3069. }
  3070. if (isTreeBuilder) {
  3071. // Create and initialize a tree builder.
  3072. nsCOMPtr<nsIXULTemplateBuilder> builder =
  3073. do_CreateInstance("@mozilla.org/xul/xul-tree-builder;1");
  3074. if (! builder)
  3075. return NS_ERROR_FAILURE;
  3076. builder->Init(aElement);
  3077. // Create a <treechildren> if one isn't there already.
  3078. // XXXvarga what about attributes?
  3079. nsCOMPtr<nsIContent> bodyContent;
  3080. nsXULContentUtils::FindChildByTag(aElement, kNameSpaceID_XUL,
  3081. nsGkAtoms::treechildren,
  3082. getter_AddRefs(bodyContent));
  3083. if (! bodyContent) {
  3084. bodyContent =
  3085. document->CreateElem(nsDependentAtomString(nsGkAtoms::treechildren),
  3086. nullptr, kNameSpaceID_XUL);
  3087. aElement->AppendChildTo(bodyContent, false);
  3088. }
  3089. }
  3090. else {
  3091. // Create and initialize a content builder.
  3092. nsCOMPtr<nsIXULTemplateBuilder> builder
  3093. = do_CreateInstance("@mozilla.org/xul/xul-template-builder;1");
  3094. if (! builder)
  3095. return NS_ERROR_FAILURE;
  3096. builder->Init(aElement);
  3097. builder->CreateContents(aElement, false);
  3098. }
  3099. return NS_OK;
  3100. }
  3101. nsresult
  3102. XULDocument::AddPrototypeSheets()
  3103. {
  3104. nsresult rv;
  3105. const nsCOMArray<nsIURI>& sheets = mCurrentPrototype->GetStyleSheetReferences();
  3106. for (int32_t i = 0; i < sheets.Count(); i++) {
  3107. nsCOMPtr<nsIURI> uri = sheets[i];
  3108. RefPtr<StyleSheet> incompleteSheet;
  3109. rv = CSSLoader()->LoadSheet(uri,
  3110. mCurrentPrototype->DocumentPrincipal(),
  3111. EmptyCString(), this,
  3112. &incompleteSheet);
  3113. // XXXldb We need to prevent bogus sheets from being held in the
  3114. // prototype's list, but until then, don't propagate the failure
  3115. // from LoadSheet (and thus exit the loop).
  3116. if (NS_SUCCEEDED(rv)) {
  3117. ++mPendingSheets;
  3118. if (!mOverlaySheets.AppendElement(incompleteSheet)) {
  3119. return NS_ERROR_OUT_OF_MEMORY;
  3120. }
  3121. }
  3122. }
  3123. return NS_OK;
  3124. }
  3125. //----------------------------------------------------------------------
  3126. //
  3127. // XULDocument::OverlayForwardReference
  3128. //
  3129. nsForwardReference::Result
  3130. XULDocument::OverlayForwardReference::Resolve()
  3131. {
  3132. // Resolve a forward reference from an overlay element; attempt to
  3133. // hook it up into the main document.
  3134. nsresult rv;
  3135. nsCOMPtr<nsIContent> target;
  3136. nsIPresShell *shell = mDocument->GetShell();
  3137. bool notify = shell && shell->DidInitialize();
  3138. nsAutoString id;
  3139. mOverlay->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id);
  3140. if (id.IsEmpty()) {
  3141. // mOverlay is a direct child of <overlay> and has no id.
  3142. // Insert it under the root element in the base document.
  3143. Element* root = mDocument->GetRootElement();
  3144. if (!root) {
  3145. return eResolve_Error;
  3146. }
  3147. rv = mDocument->InsertElement(root, mOverlay, notify);
  3148. if (NS_FAILED(rv)) return eResolve_Error;
  3149. target = mOverlay;
  3150. }
  3151. else {
  3152. // The hook-up element has an id, try to match it with an element
  3153. // with the same id in the base document.
  3154. target = mDocument->GetElementById(id);
  3155. // If we can't find the element in the document, defer the hookup
  3156. // until later.
  3157. if (!target)
  3158. return eResolve_Later;
  3159. rv = Merge(target, mOverlay, notify);
  3160. if (NS_FAILED(rv)) return eResolve_Error;
  3161. }
  3162. // Check if 'target' is still in our document --- it might not be!
  3163. if (!notify && target->GetUncomposedDoc() == mDocument) {
  3164. // Add child and any descendants to the element map
  3165. // XXX this is bogus, the content in 'target' might already be
  3166. // in the document
  3167. rv = mDocument->AddSubtreeToDocument(target);
  3168. if (NS_FAILED(rv)) return eResolve_Error;
  3169. }
  3170. if (MOZ_LOG_TEST(gXULLog, LogLevel::Debug)) {
  3171. nsAutoCString idC;
  3172. idC.AssignWithConversion(id);
  3173. MOZ_LOG(gXULLog, LogLevel::Debug,
  3174. ("xul: overlay resolved '%s'",
  3175. idC.get()));
  3176. }
  3177. mResolved = true;
  3178. return eResolve_Succeeded;
  3179. }
  3180. nsresult
  3181. XULDocument::OverlayForwardReference::Merge(nsIContent* aTargetNode,
  3182. nsIContent* aOverlayNode,
  3183. bool aNotify)
  3184. {
  3185. // This function is given:
  3186. // aTargetNode: the node in the document whose 'id' attribute
  3187. // matches a toplevel node in our overlay.
  3188. // aOverlayNode: the node in the overlay document that matches
  3189. // a node in the actual document.
  3190. // aNotify: whether or not content manipulation methods should
  3191. // use the aNotify parameter. After the initial
  3192. // reflow (i.e. in the dynamic overlay merge case),
  3193. // we want all the content manipulation methods we
  3194. // call to notify so that frames are constructed
  3195. // etc. Otherwise do not, since that's during initial
  3196. // document construction before StartLayout has been
  3197. // called which will do everything for us.
  3198. //
  3199. // This function merges the tree from the overlay into the tree in
  3200. // the document, overwriting attributes and appending child content
  3201. // nodes appropriately. (See XUL overlay reference for details)
  3202. nsresult rv;
  3203. // Merge attributes from the overlay content node to that of the
  3204. // actual document.
  3205. uint32_t i;
  3206. const nsAttrName* name;
  3207. for (i = 0; (name = aOverlayNode->GetAttrNameAt(i)); ++i) {
  3208. // We don't want to swap IDs, they should be the same.
  3209. if (name->Equals(nsGkAtoms::id))
  3210. continue;
  3211. // In certain cases merging command or observes is unsafe, so don't.
  3212. if (!aNotify) {
  3213. if (aTargetNode->NodeInfo()->Equals(nsGkAtoms::observes,
  3214. kNameSpaceID_XUL))
  3215. continue;
  3216. if (name->Equals(nsGkAtoms::observes) &&
  3217. aTargetNode->HasAttr(kNameSpaceID_None, nsGkAtoms::observes))
  3218. continue;
  3219. if (name->Equals(nsGkAtoms::command) &&
  3220. aTargetNode->HasAttr(kNameSpaceID_None, nsGkAtoms::command) &&
  3221. !aTargetNode->NodeInfo()->Equals(nsGkAtoms::key,
  3222. kNameSpaceID_XUL) &&
  3223. !aTargetNode->NodeInfo()->Equals(nsGkAtoms::menuitem,
  3224. kNameSpaceID_XUL))
  3225. continue;
  3226. }
  3227. int32_t nameSpaceID = name->NamespaceID();
  3228. nsIAtom* attr = name->LocalName();
  3229. nsIAtom* prefix = name->GetPrefix();
  3230. nsAutoString value;
  3231. aOverlayNode->GetAttr(nameSpaceID, attr, value);
  3232. // Element in the overlay has the 'removeelement' attribute set
  3233. // so remove it from the actual document.
  3234. if (attr == nsGkAtoms::removeelement &&
  3235. value.EqualsLiteral("true")) {
  3236. nsCOMPtr<nsINode> parent = aTargetNode->GetParentNode();
  3237. if (!parent) return NS_ERROR_FAILURE;
  3238. rv = RemoveElement(parent, aTargetNode);
  3239. if (NS_FAILED(rv)) return rv;
  3240. return NS_OK;
  3241. }
  3242. rv = aTargetNode->SetAttr(nameSpaceID, attr, prefix, value, aNotify);
  3243. if (!NS_FAILED(rv) && !aNotify)
  3244. rv = mDocument->BroadcastAttributeChangeFromOverlay(aTargetNode,
  3245. nameSpaceID,
  3246. attr, prefix,
  3247. value);
  3248. if (NS_FAILED(rv)) return rv;
  3249. }
  3250. // Walk our child nodes, looking for elements that have the 'id'
  3251. // attribute set. If we find any, we must do a parent check in the
  3252. // actual document to ensure that the structure matches that of
  3253. // the actual document. If it does, we can call ourselves and attempt
  3254. // to merge inside that subtree. If not, we just append the tree to
  3255. // the parent like any other.
  3256. uint32_t childCount = aOverlayNode->GetChildCount();
  3257. // This must be a strong reference since it will be the only
  3258. // reference to a content object during part of this loop.
  3259. nsCOMPtr<nsIContent> currContent;
  3260. for (i = 0; i < childCount; ++i) {
  3261. currContent = aOverlayNode->GetFirstChild();
  3262. nsIAtom *idAtom = currContent->GetID();
  3263. nsIContent *elementInDocument = nullptr;
  3264. if (idAtom) {
  3265. nsDependentAtomString id(idAtom);
  3266. if (!id.IsEmpty()) {
  3267. nsIDocument *doc = aTargetNode->GetUncomposedDoc();
  3268. //XXXsmaug should we use ShadowRoot::GetElementById()
  3269. // if doc is null?
  3270. if (!doc) return NS_ERROR_FAILURE;
  3271. elementInDocument = doc->GetElementById(id);
  3272. }
  3273. }
  3274. // The item has an 'id' attribute set, and we need to check with
  3275. // the actual document to see if an item with this id exists at
  3276. // this locale. If so, we want to merge the subtree under that
  3277. // node. Otherwise, we just do an append as if the element had
  3278. // no id attribute.
  3279. if (elementInDocument) {
  3280. // Given two parents, aTargetNode and aOverlayNode, we want
  3281. // to call merge on currContent if we find an associated
  3282. // node in the document with the same id as currContent that
  3283. // also has aTargetNode as its parent.
  3284. nsIContent *elementParent = elementInDocument->GetParent();
  3285. nsIAtom *parentID = elementParent->GetID();
  3286. if (parentID &&
  3287. aTargetNode->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id,
  3288. nsDependentAtomString(parentID),
  3289. eCaseMatters)) {
  3290. // The element matches. "Go Deep!"
  3291. rv = Merge(elementInDocument, currContent, aNotify);
  3292. if (NS_FAILED(rv)) return rv;
  3293. aOverlayNode->RemoveChildAt(0, false);
  3294. continue;
  3295. }
  3296. }
  3297. aOverlayNode->RemoveChildAt(0, false);
  3298. rv = InsertElement(aTargetNode, currContent, aNotify);
  3299. if (NS_FAILED(rv)) return rv;
  3300. }
  3301. return NS_OK;
  3302. }
  3303. XULDocument::OverlayForwardReference::~OverlayForwardReference()
  3304. {
  3305. if (MOZ_LOG_TEST(gXULLog, LogLevel::Warning) && !mResolved) {
  3306. nsAutoString id;
  3307. mOverlay->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id);
  3308. nsAutoCString idC;
  3309. idC.AssignWithConversion(id);
  3310. nsIURI *protoURI = mDocument->mCurrentPrototype->GetURI();
  3311. nsCOMPtr<nsIURI> docURI;
  3312. mDocument->mChannel->GetOriginalURI(getter_AddRefs(docURI));
  3313. MOZ_LOG(gXULLog, LogLevel::Warning,
  3314. ("xul: %s overlay failed to resolve '%s' in %s",
  3315. protoURI->GetSpecOrDefault().get(), idC.get(),
  3316. docURI ? docURI->GetSpecOrDefault().get() : ""));
  3317. }
  3318. }
  3319. //----------------------------------------------------------------------
  3320. //
  3321. // XULDocument::BroadcasterHookup
  3322. //
  3323. nsForwardReference::Result
  3324. XULDocument::BroadcasterHookup::Resolve()
  3325. {
  3326. nsresult rv;
  3327. bool listener;
  3328. rv = mDocument->CheckBroadcasterHookup(mObservesElement, &listener, &mResolved);
  3329. if (NS_FAILED(rv)) return eResolve_Error;
  3330. return mResolved ? eResolve_Succeeded : eResolve_Later;
  3331. }
  3332. XULDocument::BroadcasterHookup::~BroadcasterHookup()
  3333. {
  3334. if (MOZ_LOG_TEST(gXULLog, LogLevel::Warning) && !mResolved) {
  3335. // Tell the world we failed
  3336. nsAutoString broadcasterID;
  3337. nsAutoString attribute;
  3338. if (mObservesElement->IsXULElement(nsGkAtoms::observes)) {
  3339. mObservesElement->GetAttr(kNameSpaceID_None, nsGkAtoms::element, broadcasterID);
  3340. mObservesElement->GetAttr(kNameSpaceID_None, nsGkAtoms::attribute, attribute);
  3341. }
  3342. else {
  3343. mObservesElement->GetAttr(kNameSpaceID_None, nsGkAtoms::observes, broadcasterID);
  3344. attribute.Assign('*');
  3345. }
  3346. nsAutoCString attributeC,broadcasteridC;
  3347. attributeC.AssignWithConversion(attribute);
  3348. broadcasteridC.AssignWithConversion(broadcasterID);
  3349. MOZ_LOG(gXULLog, LogLevel::Warning,
  3350. ("xul: broadcaster hookup failed <%s attribute='%s'> to %s",
  3351. nsAtomCString(mObservesElement->NodeInfo()->NameAtom()).get(),
  3352. attributeC.get(),
  3353. broadcasteridC.get()));
  3354. }
  3355. }
  3356. //----------------------------------------------------------------------
  3357. //
  3358. // XULDocument::TemplateBuilderHookup
  3359. //
  3360. nsForwardReference::Result
  3361. XULDocument::TemplateBuilderHookup::Resolve()
  3362. {
  3363. bool needsHookup;
  3364. nsresult rv = CheckTemplateBuilderHookup(mElement, &needsHookup);
  3365. if (NS_FAILED(rv))
  3366. return eResolve_Error;
  3367. if (needsHookup) {
  3368. rv = CreateTemplateBuilder(mElement);
  3369. if (NS_FAILED(rv))
  3370. return eResolve_Error;
  3371. }
  3372. return eResolve_Succeeded;
  3373. }
  3374. //----------------------------------------------------------------------
  3375. nsresult
  3376. XULDocument::BroadcastAttributeChangeFromOverlay(nsIContent* aNode,
  3377. int32_t aNameSpaceID,
  3378. nsIAtom* aAttribute,
  3379. nsIAtom* aPrefix,
  3380. const nsAString& aValue)
  3381. {
  3382. nsresult rv = NS_OK;
  3383. if (!mBroadcasterMap || !CanBroadcast(aNameSpaceID, aAttribute))
  3384. return rv;
  3385. if (!aNode->IsElement())
  3386. return rv;
  3387. auto entry = static_cast<BroadcasterMapEntry*>
  3388. (mBroadcasterMap->Search(aNode->AsElement()));
  3389. if (!entry)
  3390. return rv;
  3391. // We've got listeners: push the value.
  3392. for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
  3393. BroadcastListener* bl = entry->mListeners[i];
  3394. if ((bl->mAttribute != aAttribute) &&
  3395. (bl->mAttribute != nsGkAtoms::_asterisk))
  3396. continue;
  3397. nsCOMPtr<nsIContent> l = do_QueryReferent(bl->mListener);
  3398. if (l) {
  3399. rv = l->SetAttr(aNameSpaceID, aAttribute,
  3400. aPrefix, aValue, false);
  3401. if (NS_FAILED(rv)) return rv;
  3402. }
  3403. }
  3404. return rv;
  3405. }
  3406. nsresult
  3407. XULDocument::FindBroadcaster(Element* aElement,
  3408. Element** aListener,
  3409. nsString& aBroadcasterID,
  3410. nsString& aAttribute,
  3411. Element** aBroadcaster)
  3412. {
  3413. mozilla::dom::NodeInfo *ni = aElement->NodeInfo();
  3414. *aListener = nullptr;
  3415. *aBroadcaster = nullptr;
  3416. if (ni->Equals(nsGkAtoms::observes, kNameSpaceID_XUL)) {
  3417. // It's an <observes> element, which means that the actual
  3418. // listener is the _parent_ node. This element should have an
  3419. // 'element' attribute that specifies the ID of the
  3420. // broadcaster element, and an 'attribute' element, which
  3421. // specifies the name of the attribute to observe.
  3422. nsIContent* parent = aElement->GetParent();
  3423. if (!parent) {
  3424. // <observes> is the root element
  3425. return NS_FINDBROADCASTER_NOT_FOUND;
  3426. }
  3427. // If we're still parented by an 'overlay' tag, then we haven't
  3428. // made it into the real document yet. Defer hookup.
  3429. if (parent->NodeInfo()->Equals(nsGkAtoms::overlay,
  3430. kNameSpaceID_XUL)) {
  3431. return NS_FINDBROADCASTER_AWAIT_OVERLAYS;
  3432. }
  3433. *aListener = parent->IsElement() ? parent->AsElement() : nullptr;
  3434. NS_IF_ADDREF(*aListener);
  3435. aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::element, aBroadcasterID);
  3436. if (aBroadcasterID.IsEmpty()) {
  3437. return NS_FINDBROADCASTER_NOT_FOUND;
  3438. }
  3439. aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::attribute, aAttribute);
  3440. }
  3441. else {
  3442. // It's a generic element, which means that we'll use the
  3443. // value of the 'observes' attribute to determine the ID of
  3444. // the broadcaster element, and we'll watch _all_ of its
  3445. // values.
  3446. aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::observes, aBroadcasterID);
  3447. // Bail if there's no aBroadcasterID
  3448. if (aBroadcasterID.IsEmpty()) {
  3449. // Try the command attribute next.
  3450. aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::command, aBroadcasterID);
  3451. if (!aBroadcasterID.IsEmpty()) {
  3452. // We've got something in the command attribute. We
  3453. // only treat this as a normal broadcaster if we are
  3454. // not a menuitem or a key.
  3455. if (ni->Equals(nsGkAtoms::menuitem, kNameSpaceID_XUL) ||
  3456. ni->Equals(nsGkAtoms::key, kNameSpaceID_XUL)) {
  3457. return NS_FINDBROADCASTER_NOT_FOUND;
  3458. }
  3459. }
  3460. else {
  3461. return NS_FINDBROADCASTER_NOT_FOUND;
  3462. }
  3463. }
  3464. *aListener = aElement;
  3465. NS_ADDREF(*aListener);
  3466. aAttribute.Assign('*');
  3467. }
  3468. // Make sure we got a valid listener.
  3469. NS_ENSURE_TRUE(*aListener, NS_ERROR_UNEXPECTED);
  3470. // Try to find the broadcaster element in the document.
  3471. *aBroadcaster = GetElementById(aBroadcasterID);
  3472. // If we can't find the broadcaster, then we'll need to defer the
  3473. // hookup. We may need to resolve some of the other overlays
  3474. // first.
  3475. if (! *aBroadcaster) {
  3476. return NS_FINDBROADCASTER_AWAIT_OVERLAYS;
  3477. }
  3478. NS_ADDREF(*aBroadcaster);
  3479. return NS_FINDBROADCASTER_FOUND;
  3480. }
  3481. nsresult
  3482. XULDocument::CheckBroadcasterHookup(Element* aElement,
  3483. bool* aNeedsHookup,
  3484. bool* aDidResolve)
  3485. {
  3486. // Resolve a broadcaster hookup. Look at the element that we're
  3487. // trying to resolve: it could be an '<observes>' element, or just
  3488. // a vanilla element with an 'observes' attribute on it.
  3489. nsresult rv;
  3490. *aDidResolve = false;
  3491. nsCOMPtr<Element> listener;
  3492. nsAutoString broadcasterID;
  3493. nsAutoString attribute;
  3494. nsCOMPtr<Element> broadcaster;
  3495. rv = FindBroadcaster(aElement, getter_AddRefs(listener),
  3496. broadcasterID, attribute, getter_AddRefs(broadcaster));
  3497. switch (rv) {
  3498. case NS_FINDBROADCASTER_NOT_FOUND:
  3499. *aNeedsHookup = false;
  3500. return NS_OK;
  3501. case NS_FINDBROADCASTER_AWAIT_OVERLAYS:
  3502. *aNeedsHookup = true;
  3503. return NS_OK;
  3504. case NS_FINDBROADCASTER_FOUND:
  3505. break;
  3506. default:
  3507. return rv;
  3508. }
  3509. NS_ENSURE_ARG(broadcaster && listener);
  3510. ErrorResult domRv;
  3511. AddBroadcastListenerFor(*broadcaster, *listener, attribute, domRv);
  3512. if (domRv.Failed()) {
  3513. return domRv.StealNSResult();
  3514. }
  3515. // Tell the world we succeeded
  3516. if (MOZ_LOG_TEST(gXULLog, LogLevel::Debug)) {
  3517. nsCOMPtr<nsIContent> content =
  3518. do_QueryInterface(listener);
  3519. NS_ASSERTION(content != nullptr, "not an nsIContent");
  3520. if (! content)
  3521. return rv;
  3522. nsAutoCString attributeC,broadcasteridC;
  3523. attributeC.AssignWithConversion(attribute);
  3524. broadcasteridC.AssignWithConversion(broadcasterID);
  3525. MOZ_LOG(gXULLog, LogLevel::Debug,
  3526. ("xul: broadcaster hookup <%s attribute='%s'> to %s",
  3527. nsAtomCString(content->NodeInfo()->NameAtom()).get(),
  3528. attributeC.get(),
  3529. broadcasteridC.get()));
  3530. }
  3531. *aNeedsHookup = false;
  3532. *aDidResolve = true;
  3533. return NS_OK;
  3534. }
  3535. nsresult
  3536. XULDocument::InsertElement(nsINode* aParent, nsIContent* aChild,
  3537. bool aNotify)
  3538. {
  3539. // Insert aChild appropriately into aParent, accounting for a
  3540. // 'pos' attribute set on aChild.
  3541. nsAutoString posStr;
  3542. bool wasInserted = false;
  3543. // insert after an element of a given id
  3544. aChild->GetAttr(kNameSpaceID_None, nsGkAtoms::insertafter, posStr);
  3545. bool isInsertAfter = true;
  3546. if (posStr.IsEmpty()) {
  3547. aChild->GetAttr(kNameSpaceID_None, nsGkAtoms::insertbefore, posStr);
  3548. isInsertAfter = false;
  3549. }
  3550. if (!posStr.IsEmpty()) {
  3551. nsIDocument *document = aParent->OwnerDoc();
  3552. nsIContent *content = nullptr;
  3553. char* str = ToNewCString(posStr);
  3554. char* rest;
  3555. char* token = nsCRT::strtok(str, ", ", &rest);
  3556. while (token) {
  3557. content = document->GetElementById(NS_ConvertASCIItoUTF16(token));
  3558. if (content)
  3559. break;
  3560. token = nsCRT::strtok(rest, ", ", &rest);
  3561. }
  3562. free(str);
  3563. if (content) {
  3564. int32_t pos = aParent->IndexOf(content);
  3565. if (pos != -1) {
  3566. pos = isInsertAfter ? pos + 1 : pos;
  3567. nsresult rv = aParent->InsertChildAt(aChild, pos, aNotify);
  3568. if (NS_FAILED(rv))
  3569. return rv;
  3570. wasInserted = true;
  3571. }
  3572. }
  3573. }
  3574. if (!wasInserted) {
  3575. aChild->GetAttr(kNameSpaceID_None, nsGkAtoms::position, posStr);
  3576. if (!posStr.IsEmpty()) {
  3577. nsresult rv;
  3578. // Positions are one-indexed.
  3579. int32_t pos = posStr.ToInteger(&rv);
  3580. // Note: if the insertion index (which is |pos - 1|) would be less
  3581. // than 0 or greater than the number of children aParent has, then
  3582. // don't insert, since the position is bogus. Just skip on to
  3583. // appending.
  3584. if (NS_SUCCEEDED(rv) && pos > 0 &&
  3585. uint32_t(pos - 1) <= aParent->GetChildCount()) {
  3586. rv = aParent->InsertChildAt(aChild, pos - 1, aNotify);
  3587. if (NS_SUCCEEDED(rv))
  3588. wasInserted = true;
  3589. // If the insertion fails, then we should still
  3590. // attempt an append. Thus, rather than returning rv
  3591. // immediately, we fall through to the final
  3592. // "catch-all" case that just does an AppendChildTo.
  3593. }
  3594. }
  3595. }
  3596. if (!wasInserted) {
  3597. return aParent->AppendChildTo(aChild, aNotify);
  3598. }
  3599. return NS_OK;
  3600. }
  3601. nsresult
  3602. XULDocument::RemoveElement(nsINode* aParent, nsINode* aChild)
  3603. {
  3604. int32_t nodeOffset = aParent->IndexOf(aChild);
  3605. aParent->RemoveChildAt(nodeOffset, true);
  3606. return NS_OK;
  3607. }
  3608. //----------------------------------------------------------------------
  3609. //
  3610. // CachedChromeStreamListener
  3611. //
  3612. XULDocument::CachedChromeStreamListener::CachedChromeStreamListener(XULDocument* aDocument, bool aProtoLoaded)
  3613. : mDocument(aDocument),
  3614. mProtoLoaded(aProtoLoaded)
  3615. {
  3616. }
  3617. XULDocument::CachedChromeStreamListener::~CachedChromeStreamListener()
  3618. {
  3619. }
  3620. NS_IMPL_ISUPPORTS(XULDocument::CachedChromeStreamListener,
  3621. nsIRequestObserver, nsIStreamListener)
  3622. NS_IMETHODIMP
  3623. XULDocument::CachedChromeStreamListener::OnStartRequest(nsIRequest *request,
  3624. nsISupports* acontext)
  3625. {
  3626. return NS_ERROR_PARSED_DATA_CACHED;
  3627. }
  3628. NS_IMETHODIMP
  3629. XULDocument::CachedChromeStreamListener::OnStopRequest(nsIRequest *request,
  3630. nsISupports* aContext,
  3631. nsresult aStatus)
  3632. {
  3633. if (! mProtoLoaded)
  3634. return NS_OK;
  3635. return mDocument->OnPrototypeLoadDone(true);
  3636. }
  3637. NS_IMETHODIMP
  3638. XULDocument::CachedChromeStreamListener::OnDataAvailable(nsIRequest *request,
  3639. nsISupports* aContext,
  3640. nsIInputStream* aInStr,
  3641. uint64_t aSourceOffset,
  3642. uint32_t aCount)
  3643. {
  3644. NS_NOTREACHED("CachedChromeStream doesn't receive data");
  3645. return NS_ERROR_UNEXPECTED;
  3646. }
  3647. //----------------------------------------------------------------------
  3648. //
  3649. // ParserObserver
  3650. //
  3651. XULDocument::ParserObserver::ParserObserver(XULDocument* aDocument,
  3652. nsXULPrototypeDocument* aPrototype)
  3653. : mDocument(aDocument), mPrototype(aPrototype)
  3654. {
  3655. }
  3656. XULDocument::ParserObserver::~ParserObserver()
  3657. {
  3658. }
  3659. NS_IMPL_ISUPPORTS(XULDocument::ParserObserver, nsIRequestObserver)
  3660. NS_IMETHODIMP
  3661. XULDocument::ParserObserver::OnStartRequest(nsIRequest *request,
  3662. nsISupports* aContext)
  3663. {
  3664. // Guard against buggy channels calling OnStartRequest multiple times.
  3665. if (mPrototype) {
  3666. nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
  3667. nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
  3668. if (channel && secMan) {
  3669. nsCOMPtr<nsIPrincipal> principal;
  3670. secMan->GetChannelResultPrincipal(channel, getter_AddRefs(principal));
  3671. // Failure there is ok -- it'll just set a (safe) null principal
  3672. mPrototype->SetDocumentPrincipal(principal);
  3673. }
  3674. // Make sure to avoid cycles
  3675. mPrototype = nullptr;
  3676. }
  3677. return NS_OK;
  3678. }
  3679. NS_IMETHODIMP
  3680. XULDocument::ParserObserver::OnStopRequest(nsIRequest *request,
  3681. nsISupports* aContext,
  3682. nsresult aStatus)
  3683. {
  3684. nsresult rv = NS_OK;
  3685. if (NS_FAILED(aStatus)) {
  3686. // If an overlay load fails, we need to nudge the prototype
  3687. // walk along.
  3688. nsCOMPtr<nsIChannel> aChannel = do_QueryInterface(request);
  3689. if (aChannel) {
  3690. nsCOMPtr<nsIURI> uri;
  3691. aChannel->GetOriginalURI(getter_AddRefs(uri));
  3692. if (uri) {
  3693. mDocument->ReportMissingOverlay(uri);
  3694. }
  3695. }
  3696. rv = mDocument->ResumeWalk();
  3697. }
  3698. // Drop the reference to the document to break cycle between the
  3699. // document, the parser, the content sink, and the parser
  3700. // observer.
  3701. mDocument = nullptr;
  3702. return rv;
  3703. }
  3704. already_AddRefed<nsPIWindowRoot>
  3705. XULDocument::GetWindowRoot()
  3706. {
  3707. if (!mDocumentContainer) {
  3708. return nullptr;
  3709. }
  3710. nsCOMPtr<nsPIDOMWindowOuter> piWin = mDocumentContainer->GetWindow();
  3711. return piWin ? piWin->GetTopWindowRoot() : nullptr;
  3712. }
  3713. bool
  3714. XULDocument::IsDocumentRightToLeft()
  3715. {
  3716. // setting the localedir attribute on the root element forces a
  3717. // specific direction for the document.
  3718. Element* element = GetRootElement();
  3719. if (element) {
  3720. static nsIContent::AttrValuesArray strings[] =
  3721. {&nsGkAtoms::ltr, &nsGkAtoms::rtl, nullptr};
  3722. switch (element->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::localedir,
  3723. strings, eCaseMatters)) {
  3724. case 0: return false;
  3725. case 1: return true;
  3726. default: break; // otherwise, not a valid value, so fall through
  3727. }
  3728. }
  3729. // otherwise, get the locale from the chrome registry and
  3730. // look up the intl.uidirection.<locale> preference
  3731. nsCOMPtr<nsIXULChromeRegistry> reg =
  3732. mozilla::services::GetXULChromeRegistryService();
  3733. if (!reg)
  3734. return false;
  3735. nsAutoCString package;
  3736. bool isChrome;
  3737. if (NS_SUCCEEDED(mDocumentURI->SchemeIs("chrome", &isChrome)) &&
  3738. isChrome) {
  3739. mDocumentURI->GetHostPort(package);
  3740. }
  3741. else {
  3742. // use the 'global' package for about and resource uris.
  3743. // otherwise, just default to left-to-right.
  3744. bool isAbout, isResource;
  3745. if (NS_SUCCEEDED(mDocumentURI->SchemeIs("about", &isAbout)) &&
  3746. isAbout) {
  3747. package.AssignLiteral("global");
  3748. }
  3749. else if (NS_SUCCEEDED(mDocumentURI->SchemeIs("resource", &isResource)) &&
  3750. isResource) {
  3751. package.AssignLiteral("global");
  3752. }
  3753. else {
  3754. return false;
  3755. }
  3756. }
  3757. bool isRTL = false;
  3758. reg->IsLocaleRTL(package, &isRTL);
  3759. return isRTL;
  3760. }
  3761. void
  3762. XULDocument::ResetDocumentDirection()
  3763. {
  3764. DocumentStatesChanged(NS_DOCUMENT_STATE_RTL_LOCALE);
  3765. }
  3766. void
  3767. XULDocument::DirectionChanged(const char* aPrefName, void* aData)
  3768. {
  3769. // Reset the direction and restyle the document if necessary.
  3770. XULDocument* doc = (XULDocument *)aData;
  3771. if (doc) {
  3772. doc->ResetDocumentDirection();
  3773. }
  3774. }
  3775. int
  3776. XULDocument::GetDocumentLWTheme()
  3777. {
  3778. if (mDocLWTheme == Doc_Theme_Uninitialized) {
  3779. mDocLWTheme = Doc_Theme_None; // No lightweight theme by default
  3780. Element* element = GetRootElement();
  3781. nsAutoString hasLWTheme;
  3782. if (element &&
  3783. element->GetAttr(kNameSpaceID_None, nsGkAtoms::lwtheme, hasLWTheme) &&
  3784. !(hasLWTheme.IsEmpty()) &&
  3785. hasLWTheme.EqualsLiteral("true")) {
  3786. mDocLWTheme = Doc_Theme_Neutral;
  3787. nsAutoString lwTheme;
  3788. element->GetAttr(kNameSpaceID_None, nsGkAtoms::lwthemetextcolor, lwTheme);
  3789. if (!(lwTheme.IsEmpty())) {
  3790. if (lwTheme.EqualsLiteral("dark"))
  3791. mDocLWTheme = Doc_Theme_Dark;
  3792. else if (lwTheme.EqualsLiteral("bright"))
  3793. mDocLWTheme = Doc_Theme_Bright;
  3794. }
  3795. }
  3796. }
  3797. return mDocLWTheme;
  3798. }
  3799. NS_IMETHODIMP
  3800. XULDocument::GetBoxObjectFor(nsIDOMElement* aElement, nsIBoxObject** aResult)
  3801. {
  3802. ErrorResult rv;
  3803. nsCOMPtr<Element> el = do_QueryInterface(aElement);
  3804. *aResult = GetBoxObjectFor(el, rv).take();
  3805. return rv.StealNSResult();
  3806. }
  3807. JSObject*
  3808. XULDocument::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
  3809. {
  3810. return XULDocumentBinding::Wrap(aCx, this, aGivenProto);
  3811. }
  3812. } // namespace dom
  3813. } // namespace mozilla