1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579 |
- /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- /*
- An implementation for the XUL document. This implementation serves
- as the basis for generating an NGLayout content model.
- Notes
- -----
- 1. We do some monkey business in the document observer methods to
- keep the element map in sync for HTML elements. Why don't we just
- do it for _all_ elements? Well, in the case of XUL elements,
- which may be lazily created during frame construction, the
- document observer methods will never be called because we'll be
- adding the XUL nodes into the content model "quietly".
- */
- #include "mozilla/ArrayUtils.h"
- #include "XULDocument.h"
- #include "nsError.h"
- #include "nsIBoxObject.h"
- #include "nsIChromeRegistry.h"
- #include "nsView.h"
- #include "nsViewManager.h"
- #include "nsIContentViewer.h"
- #include "nsIDOMXULElement.h"
- #include "nsIStreamListener.h"
- #include "nsITimer.h"
- #include "nsDocShell.h"
- #include "nsGkAtoms.h"
- #include "nsXMLContentSink.h"
- #include "nsXULContentSink.h"
- #include "nsXULContentUtils.h"
- #include "nsIXULOverlayProvider.h"
- #include "nsIStringEnumerator.h"
- #include "nsNetUtil.h"
- #include "nsParserCIID.h"
- #include "nsPIBoxObject.h"
- #include "mozilla/dom/BoxObject.h"
- #include "nsXPIDLString.h"
- #include "nsPIDOMWindow.h"
- #include "nsPIWindowRoot.h"
- #include "nsXULCommandDispatcher.h"
- #include "nsXULElement.h"
- #include "mozilla/Logging.h"
- #include "rdf.h"
- #include "nsIFrame.h"
- #include "nsXBLService.h"
- #include "nsCExternalHandlerService.h"
- #include "nsMimeTypes.h"
- #include "nsIObjectInputStream.h"
- #include "nsIObjectOutputStream.h"
- #include "nsContentList.h"
- #include "nsIScriptGlobalObject.h"
- #include "nsIScriptSecurityManager.h"
- #include "nsNodeInfoManager.h"
- #include "nsContentCreatorFunctions.h"
- #include "nsContentUtils.h"
- #include "nsIParser.h"
- #include "nsCharsetSource.h"
- #include "mozilla/StyleSheetInlines.h"
- #include "mozilla/css/Loader.h"
- #include "nsIScriptError.h"
- #include "nsIStyleSheetLinkingElement.h"
- #include "nsIObserverService.h"
- #include "nsNodeUtils.h"
- #include "nsIDocShellTreeOwner.h"
- #include "nsIXULWindow.h"
- #include "nsXULPopupManager.h"
- #include "nsCCUncollectableMarker.h"
- #include "nsURILoader.h"
- #include "mozilla/AddonPathService.h"
- #include "mozilla/BasicEvents.h"
- #include "mozilla/dom/Element.h"
- #include "mozilla/dom/NodeInfoInlines.h"
- #include "mozilla/dom/ProcessingInstruction.h"
- #include "mozilla/dom/ScriptSettings.h"
- #include "mozilla/dom/XULDocumentBinding.h"
- #include "mozilla/EventDispatcher.h"
- #include "mozilla/LoadInfo.h"
- #include "mozilla/Preferences.h"
- #include "nsTextNode.h"
- #include "nsJSUtils.h"
- #include "mozilla/dom/URL.h"
- #include "nsIContentPolicy.h"
- #include "mozAutoDocUpdate.h"
- #include "xpcpublic.h"
- #include "mozilla/StyleSheet.h"
- #include "mozilla/StyleSheetInlines.h"
- using namespace mozilla;
- using namespace mozilla::dom;
- //----------------------------------------------------------------------
- //
- // CIDs
- //
- static NS_DEFINE_CID(kParserCID, NS_PARSER_CID);
- static bool IsOverlayAllowed(nsIURI* aURI)
- {
- bool canOverlay = false;
- if (NS_SUCCEEDED(aURI->SchemeIs("about", &canOverlay)) && canOverlay)
- return true;
- if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &canOverlay)) && canOverlay)
- return true;
- return false;
- }
- //----------------------------------------------------------------------
- //
- // Miscellaneous Constants
- //
- const nsForwardReference::Phase nsForwardReference::kPasses[] = {
- nsForwardReference::eConstruction,
- nsForwardReference::eHookup,
- nsForwardReference::eDone
- };
- //----------------------------------------------------------------------
- //
- // Statics
- //
- int32_t XULDocument::gRefCnt = 0;
- LazyLogModule XULDocument::gXULLog("XULDocument");
- //----------------------------------------------------------------------
- struct BroadcastListener {
- nsWeakPtr mListener;
- nsCOMPtr<nsIAtom> mAttribute;
- };
- struct BroadcasterMapEntry : public PLDHashEntryHdr
- {
- Element* mBroadcaster; // [WEAK]
- nsTArray<BroadcastListener*> mListeners; // [OWNING] of BroadcastListener objects
- };
- Element*
- nsRefMapEntry::GetFirstElement()
- {
- return mRefContentList.SafeElementAt(0);
- }
- void
- nsRefMapEntry::AppendAll(nsCOMArray<nsIContent>* aElements)
- {
- for (size_t i = 0; i < mRefContentList.Length(); ++i) {
- aElements->AppendObject(mRefContentList[i]);
- }
- }
- bool
- nsRefMapEntry::AddElement(Element* aElement)
- {
- if (mRefContentList.Contains(aElement)) {
- return true;
- }
- return mRefContentList.AppendElement(aElement);
- }
- bool
- nsRefMapEntry::RemoveElement(Element* aElement)
- {
- mRefContentList.RemoveElement(aElement);
- return mRefContentList.IsEmpty();
- }
- //----------------------------------------------------------------------
- //
- // ctors & dtors
- //
- namespace mozilla {
- namespace dom {
- XULDocument::XULDocument(void)
- : XMLDocument("application/vnd.mozilla.xul+xml"),
- mDocLWTheme(Doc_Theme_Uninitialized),
- mState(eState_Master),
- mResolutionPhase(nsForwardReference::eStart)
- {
- // NOTE! nsDocument::operator new() zeroes out all members, so don't
- // bother initializing members to 0.
- // Override the default in nsDocument
- mCharacterSet.AssignLiteral("UTF-8");
- mDefaultElementType = kNameSpaceID_XUL;
- mType = eXUL;
- mDelayFrameLoaderInitialization = true;
- mAllowXULXBL = eTriTrue;
- }
- XULDocument::~XULDocument()
- {
- NS_ASSERTION(mNextSrcLoadWaiter == nullptr,
- "unreferenced document still waiting for script source to load?");
- // In case we failed somewhere early on and the forward observer
- // decls never got resolved.
- mForwardReferences.Clear();
- // Likewise for any references we have to IDs where we might
- // look for persisted data:
- mPersistenceIds.Clear();
- // Destroy our broadcaster map.
- delete mBroadcasterMap;
- delete mTemplateBuilderTable;
- Preferences::UnregisterCallback(XULDocument::DirectionChanged,
- "intl.uidirection.", this);
- if (mOffThreadCompileStringBuf) {
- js_free(mOffThreadCompileStringBuf);
- }
- }
- } // namespace dom
- } // namespace mozilla
- nsresult
- NS_NewXULDocument(nsIXULDocument** result)
- {
- NS_PRECONDITION(result != nullptr, "null ptr");
- if (! result)
- return NS_ERROR_NULL_POINTER;
- RefPtr<XULDocument> doc = new XULDocument();
- nsresult rv;
- if (NS_FAILED(rv = doc->Init())) {
- return rv;
- }
- doc.forget(result);
- return NS_OK;
- }
- namespace mozilla {
- namespace dom {
- //----------------------------------------------------------------------
- //
- // nsISupports interface
- //
- NS_IMPL_CYCLE_COLLECTION_CLASS(XULDocument)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XULDocument, XMLDocument)
- NS_ASSERTION(!nsCCUncollectableMarker::InGeneration(cb, tmp->GetMarkedCCGeneration()),
- "Shouldn't traverse XULDocument!");
- // XXX tmp->mForwardReferences?
- // XXX tmp->mContextStack?
- // An element will only have a template builder as long as it's in the
- // document, so we'll traverse the table here instead of from the element.
- if (tmp->mTemplateBuilderTable) {
- for (auto iter = tmp->mTemplateBuilderTable->Iter();
- !iter.Done();
- iter.Next()) {
- NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mTemplateBuilderTable key");
- cb.NoteXPCOMChild(iter.Key());
- NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mTemplateBuilderTable value");
- cb.NoteXPCOMChild(iter.UserData());
- }
- }
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCurrentPrototype)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMasterPrototype)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCommandDispatcher)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrototypes)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStore)
- if (tmp->mOverlayLoadObservers) {
- for (auto iter = tmp->mOverlayLoadObservers->Iter();
- !iter.Done();
- iter.Next()) {
- NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mOverlayLoadObservers value");
- cb.NoteXPCOMChild(iter.Data());
- }
- }
- if (tmp->mPendingOverlayLoadNotifications) {
- for (auto iter = tmp->mPendingOverlayLoadNotifications->Iter();
- !iter.Done();
- iter.Next()) {
- NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mPendingOverlayLoadNotifications value");
- cb.NoteXPCOMChild(iter.Data());
- }
- }
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
- NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XULDocument, XMLDocument)
- delete tmp->mTemplateBuilderTable;
- tmp->mTemplateBuilderTable = nullptr;
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mCommandDispatcher)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStore)
- //XXX We should probably unlink all the objects we traverse.
- NS_IMPL_CYCLE_COLLECTION_UNLINK_END
- NS_IMPL_ADDREF_INHERITED(XULDocument, XMLDocument)
- NS_IMPL_RELEASE_INHERITED(XULDocument, XMLDocument)
- // QueryInterface implementation for XULDocument
- NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(XULDocument)
- NS_INTERFACE_TABLE_INHERITED(XULDocument, nsIXULDocument,
- nsIDOMXULDocument, nsIStreamLoaderObserver,
- nsICSSLoaderObserver, nsIOffThreadScriptReceiver)
- NS_INTERFACE_TABLE_TAIL_INHERITING(XMLDocument)
- //----------------------------------------------------------------------
- //
- // nsIDocument interface
- //
- void
- XULDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup)
- {
- NS_NOTREACHED("Reset");
- }
- void
- XULDocument::ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup,
- nsIPrincipal* aPrincipal)
- {
- NS_NOTREACHED("ResetToURI");
- }
- void
- XULDocument::SetContentType(const nsAString& aContentType)
- {
- NS_ASSERTION(aContentType.EqualsLiteral("application/vnd.mozilla.xul+xml"),
- "xul-documents always has content-type application/vnd.mozilla.xul+xml");
- // Don't do anything, xul always has the mimetype
- // application/vnd.mozilla.xul+xml
- }
- // This is called when the master document begins loading, whether it's
- // being cached or not.
- nsresult
- XULDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
- nsILoadGroup* aLoadGroup,
- nsISupports* aContainer,
- nsIStreamListener **aDocListener,
- bool aReset, nsIContentSink* aSink)
- {
- if (MOZ_LOG_TEST(gXULLog, LogLevel::Warning)) {
- nsCOMPtr<nsIURI> uri;
- nsresult rv = aChannel->GetOriginalURI(getter_AddRefs(uri));
- if (NS_SUCCEEDED(rv)) {
- nsAutoCString urlspec;
- rv = uri->GetSpec(urlspec);
- if (NS_SUCCEEDED(rv)) {
- MOZ_LOG(gXULLog, LogLevel::Warning,
- ("xul: load document '%s'", urlspec.get()));
- }
- }
- }
- // NOTE: If this ever starts calling nsDocument::StartDocumentLoad
- // we'll possibly need to reset our content type afterwards.
- mStillWalking = true;
- mMayStartLayout = false;
- mDocumentLoadGroup = do_GetWeakReference(aLoadGroup);
- mChannel = aChannel;
- // Get the URI. Note that this should match nsDocShell::OnLoadingSite
- nsresult rv =
- NS_GetFinalChannelURI(aChannel, getter_AddRefs(mDocumentURI));
- NS_ENSURE_SUCCESS(rv, rv);
-
- ResetStylesheetsToURI(mDocumentURI);
- RetrieveRelevantHeaders(aChannel);
- // Look in the chrome cache: we've got this puppy loaded
- // already.
- nsXULPrototypeDocument* proto = IsChromeURI(mDocumentURI) ?
- nsXULPrototypeCache::GetInstance()->GetPrototype(mDocumentURI) :
- nullptr;
- // Same comment as nsChromeProtocolHandler::NewChannel and
- // XULDocument::ResumeWalk
- // - Ben Goodger
- //
- // We don't abort on failure here because there are too many valid
- // cases that can return failure, and the null-ness of |proto| is enough
- // to trigger the fail-safe parse-from-disk solution. Example failure cases
- // (for reference) include:
- //
- // NS_ERROR_NOT_AVAILABLE: the URI cannot be found in the startup cache,
- // parse from disk
- // other: the startup cache file could not be found, probably
- // due to being accessed before a profile has been selected (e.g.
- // loading chrome for the profile manager itself). This must be
- // parsed from disk.
- if (proto) {
- // If we're racing with another document to load proto, wait till the
- // load has finished loading before trying to add cloned style sheets.
- // XULDocument::EndLoad will call proto->NotifyLoadDone, which will
- // find all racing documents and notify them via OnPrototypeLoadDone,
- // which will add style sheet clones to each document.
- bool loaded;
- rv = proto->AwaitLoadDone(this, &loaded);
- if (NS_FAILED(rv)) return rv;
- mMasterPrototype = mCurrentPrototype = proto;
- // Set up the right principal on ourselves.
- SetPrincipal(proto->DocumentPrincipal());
- // We need a listener, even if proto is not yet loaded, in which
- // event the listener's OnStopRequest method does nothing, and all
- // the interesting work happens below XULDocument::EndLoad, from
- // the call there to mCurrentPrototype->NotifyLoadDone().
- *aDocListener = new CachedChromeStreamListener(this, loaded);
- }
- else {
- bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
- bool fillXULCache = (useXULCache && IsChromeURI(mDocumentURI));
- // It's just a vanilla document load. Create a parser to deal
- // with the stream n' stuff.
- nsCOMPtr<nsIParser> parser;
- rv = PrepareToLoad(aContainer, aCommand, aChannel, aLoadGroup,
- getter_AddRefs(parser));
- if (NS_FAILED(rv)) return rv;
- // Predicate mIsWritingFastLoad on the XUL cache being enabled,
- // so we don't have to re-check whether the cache is enabled all
- // the time.
- mIsWritingFastLoad = useXULCache;
- nsCOMPtr<nsIStreamListener> listener = do_QueryInterface(parser, &rv);
- NS_ASSERTION(NS_SUCCEEDED(rv), "parser doesn't support nsIStreamListener");
- if (NS_FAILED(rv)) return rv;
- *aDocListener = listener;
- parser->Parse(mDocumentURI);
- // Put the current prototype, created under PrepareToLoad, into the
- // XUL prototype cache now. We can't do this under PrepareToLoad or
- // overlay loading will break; search for PutPrototype in ResumeWalk
- // and see the comment there.
- if (fillXULCache) {
- nsXULPrototypeCache::GetInstance()->PutPrototype(mCurrentPrototype);
- }
- }
- NS_IF_ADDREF(*aDocListener);
- return NS_OK;
- }
- // This gets invoked after a prototype for this document or one of
- // its overlays is fully built in the content sink.
- void
- XULDocument::EndLoad()
- {
- // This can happen if an overlay fails to load
- if (!mCurrentPrototype)
- return;
- nsresult rv;
- // Whack the prototype document into the cache so that the next
- // time somebody asks for it, they don't need to load it by hand.
- nsCOMPtr<nsIURI> uri = mCurrentPrototype->GetURI();
- bool isChrome = IsChromeURI(uri);
- // Remember if the XUL cache is on
- bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
- // If the current prototype is an overlay document (non-master prototype)
- // and we're filling the FastLoad disk cache, tell the cache we're done
- // loading it, and write the prototype. The master prototype is put into
- // the cache earlier in XULDocument::StartDocumentLoad.
- if (useXULCache && mIsWritingFastLoad && isChrome &&
- mMasterPrototype != mCurrentPrototype) {
- nsXULPrototypeCache::GetInstance()->WritePrototype(mCurrentPrototype);
- }
- if (IsOverlayAllowed(uri)) {
- nsCOMPtr<nsIXULOverlayProvider> reg =
- mozilla::services::GetXULOverlayProviderService();
- if (reg) {
- nsCOMPtr<nsISimpleEnumerator> overlays;
- rv = reg->GetStyleOverlays(uri, getter_AddRefs(overlays));
- if (NS_FAILED(rv)) return;
- bool moreSheets;
- nsCOMPtr<nsISupports> next;
- nsCOMPtr<nsIURI> sheetURI;
- while (NS_SUCCEEDED(rv = overlays->HasMoreElements(&moreSheets)) &&
- moreSheets) {
- overlays->GetNext(getter_AddRefs(next));
- sheetURI = do_QueryInterface(next);
- if (!sheetURI) {
- NS_ERROR("Chrome registry handed me a non-nsIURI object!");
- continue;
- }
- if (IsChromeURI(sheetURI)) {
- mCurrentPrototype->AddStyleSheetReference(sheetURI);
- }
- }
- }
- if (isChrome && useXULCache) {
- // If it's a chrome prototype document, then notify any
- // documents that raced to load the prototype, and awaited
- // its load completion via proto->AwaitLoadDone().
- rv = mCurrentPrototype->NotifyLoadDone();
- if (NS_FAILED(rv)) return;
- }
- }
- OnPrototypeLoadDone(true);
- if (MOZ_LOG_TEST(gXULLog, LogLevel::Warning)) {
- nsAutoCString urlspec;
- rv = uri->GetSpec(urlspec);
- if (NS_SUCCEEDED(rv)) {
- MOZ_LOG(gXULLog, LogLevel::Warning,
- ("xul: Finished loading document '%s'", urlspec.get()));
- }
- }
- }
- NS_IMETHODIMP
- XULDocument::OnPrototypeLoadDone(bool aResumeWalk)
- {
- nsresult rv;
- // Add the style overlays from chrome registry, if any.
- rv = AddPrototypeSheets();
- if (NS_FAILED(rv)) return rv;
- rv = PrepareToWalk();
- NS_ASSERTION(NS_SUCCEEDED(rv), "unable to prepare for walk");
- if (NS_FAILED(rv)) return rv;
- if (aResumeWalk) {
- rv = ResumeWalk();
- }
- return rv;
- }
- // called when an error occurs parsing a document
- bool
- XULDocument::OnDocumentParserError()
- {
- // don't report errors that are from overlays
- if (mCurrentPrototype && mMasterPrototype != mCurrentPrototype) {
- nsCOMPtr<nsIURI> uri = mCurrentPrototype->GetURI();
- if (IsChromeURI(uri)) {
- nsCOMPtr<nsIObserverService> os =
- mozilla::services::GetObserverService();
- if (os)
- os->NotifyObservers(uri, "xul-overlay-parsererror",
- EmptyString().get());
- }
- return false;
- }
- return true;
- }
- static void
- ClearBroadcasterMapEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
- {
- BroadcasterMapEntry* entry =
- static_cast<BroadcasterMapEntry*>(aEntry);
- for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
- delete entry->mListeners[i];
- }
- entry->mListeners.Clear();
- // N.B. that we need to manually run the dtor because we
- // constructed the nsTArray object in-place.
- entry->mListeners.~nsTArray<BroadcastListener*>();
- }
- static bool
- CanBroadcast(int32_t aNameSpaceID, nsIAtom* aAttribute)
- {
- // Don't push changes to the |id|, |ref|, |persist|, |command| or
- // |observes| attribute.
- if (aNameSpaceID == kNameSpaceID_None) {
- if ((aAttribute == nsGkAtoms::id) ||
- (aAttribute == nsGkAtoms::ref) ||
- (aAttribute == nsGkAtoms::persist) ||
- (aAttribute == nsGkAtoms::command) ||
- (aAttribute == nsGkAtoms::observes)) {
- return false;
- }
- }
- return true;
- }
- struct nsAttrNameInfo
- {
- nsAttrNameInfo(int32_t aNamespaceID, nsIAtom* aName, nsIAtom* aPrefix) :
- mNamespaceID(aNamespaceID), mName(aName), mPrefix(aPrefix) {}
- nsAttrNameInfo(const nsAttrNameInfo& aOther) :
- mNamespaceID(aOther.mNamespaceID), mName(aOther.mName),
- mPrefix(aOther.mPrefix) {}
- int32_t mNamespaceID;
- nsCOMPtr<nsIAtom> mName;
- nsCOMPtr<nsIAtom> mPrefix;
- };
- void
- XULDocument::SynchronizeBroadcastListener(Element *aBroadcaster,
- Element *aListener,
- const nsAString &aAttr)
- {
- if (!nsContentUtils::IsSafeToRunScript()) {
- nsDelayedBroadcastUpdate delayedUpdate(aBroadcaster, aListener,
- aAttr);
- mDelayedBroadcasters.AppendElement(delayedUpdate);
- MaybeBroadcast();
- return;
- }
- bool notify = mDocumentLoaded || mHandlingDelayedBroadcasters;
- if (aAttr.EqualsLiteral("*")) {
- uint32_t count = aBroadcaster->GetAttrCount();
- nsTArray<nsAttrNameInfo> attributes(count);
- for (uint32_t i = 0; i < count; ++i) {
- const nsAttrName* attrName = aBroadcaster->GetAttrNameAt(i);
- int32_t nameSpaceID = attrName->NamespaceID();
- nsIAtom* name = attrName->LocalName();
- // _Don't_ push the |id|, |ref|, or |persist| attribute's value!
- if (! CanBroadcast(nameSpaceID, name))
- continue;
- attributes.AppendElement(nsAttrNameInfo(nameSpaceID, name,
- attrName->GetPrefix()));
- }
- count = attributes.Length();
- while (count-- > 0) {
- int32_t nameSpaceID = attributes[count].mNamespaceID;
- nsIAtom* name = attributes[count].mName;
- nsAutoString value;
- if (aBroadcaster->GetAttr(nameSpaceID, name, value)) {
- aListener->SetAttr(nameSpaceID, name, attributes[count].mPrefix,
- value, notify);
- }
- #if 0
- // XXX we don't fire the |onbroadcast| handler during
- // initial hookup: doing so would potentially run the
- // |onbroadcast| handler before the |onload| handler,
- // which could define JS properties that mask XBL
- // properties, etc.
- ExecuteOnBroadcastHandlerFor(aBroadcaster, aListener, name);
- #endif
- }
- }
- else {
- // Find out if the attribute is even present at all.
- nsCOMPtr<nsIAtom> name = NS_Atomize(aAttr);
- nsAutoString value;
- if (aBroadcaster->GetAttr(kNameSpaceID_None, name, value)) {
- aListener->SetAttr(kNameSpaceID_None, name, value, notify);
- } else {
- aListener->UnsetAttr(kNameSpaceID_None, name, notify);
- }
- #if 0
- // XXX we don't fire the |onbroadcast| handler during initial
- // hookup: doing so would potentially run the |onbroadcast|
- // handler before the |onload| handler, which could define JS
- // properties that mask XBL properties, etc.
- ExecuteOnBroadcastHandlerFor(aBroadcaster, aListener, name);
- #endif
- }
- }
- NS_IMETHODIMP
- XULDocument::AddBroadcastListenerFor(nsIDOMElement* aBroadcaster,
- nsIDOMElement* aListener,
- const nsAString& aAttr)
- {
- ErrorResult rv;
- nsCOMPtr<Element> broadcaster = do_QueryInterface(aBroadcaster);
- nsCOMPtr<Element> listener = do_QueryInterface(aListener);
- NS_ENSURE_ARG(broadcaster && listener);
- AddBroadcastListenerFor(*broadcaster, *listener, aAttr, rv);
- return rv.StealNSResult();
- }
- void
- XULDocument::AddBroadcastListenerFor(Element& aBroadcaster, Element& aListener,
- const nsAString& aAttr, ErrorResult& aRv)
- {
- nsresult rv =
- nsContentUtils::CheckSameOrigin(this, &aBroadcaster);
- if (NS_FAILED(rv)) {
- aRv.Throw(rv);
- return;
- }
- rv = nsContentUtils::CheckSameOrigin(this, &aListener);
- if (NS_FAILED(rv)) {
- aRv.Throw(rv);
- return;
- }
- static const PLDHashTableOps gOps = {
- PLDHashTable::HashVoidPtrKeyStub,
- PLDHashTable::MatchEntryStub,
- PLDHashTable::MoveEntryStub,
- ClearBroadcasterMapEntry,
- nullptr
- };
- if (! mBroadcasterMap) {
- mBroadcasterMap = new PLDHashTable(&gOps, sizeof(BroadcasterMapEntry));
- }
- auto entry = static_cast<BroadcasterMapEntry*>
- (mBroadcasterMap->Search(&aBroadcaster));
- if (!entry) {
- entry = static_cast<BroadcasterMapEntry*>
- (mBroadcasterMap->Add(&aBroadcaster, fallible));
- if (! entry) {
- aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
- return;
- }
- entry->mBroadcaster = &aBroadcaster;
- // N.B. placement new to construct the nsTArray object in-place
- new (&entry->mListeners) nsTArray<BroadcastListener*>();
- }
- // Only add the listener if it's not there already!
- nsCOMPtr<nsIAtom> attr = NS_Atomize(aAttr);
- for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
- BroadcastListener* bl = entry->mListeners[i];
- nsCOMPtr<Element> blListener = do_QueryReferent(bl->mListener);
- if (blListener == &aListener && bl->mAttribute == attr)
- return;
- }
- BroadcastListener* bl = new BroadcastListener;
- bl->mListener = do_GetWeakReference(&aListener);
- bl->mAttribute = attr;
- entry->mListeners.AppendElement(bl);
- SynchronizeBroadcastListener(&aBroadcaster, &aListener, aAttr);
- }
- NS_IMETHODIMP
- XULDocument::RemoveBroadcastListenerFor(nsIDOMElement* aBroadcaster,
- nsIDOMElement* aListener,
- const nsAString& aAttr)
- {
- nsCOMPtr<Element> broadcaster = do_QueryInterface(aBroadcaster);
- nsCOMPtr<Element> listener = do_QueryInterface(aListener);
- NS_ENSURE_ARG(broadcaster && listener);
- RemoveBroadcastListenerFor(*broadcaster, *listener, aAttr);
- return NS_OK;
- }
- void
- XULDocument::RemoveBroadcastListenerFor(Element& aBroadcaster,
- Element& aListener,
- const nsAString& aAttr)
- {
- // If we haven't added any broadcast listeners, then there sure
- // aren't any to remove.
- if (! mBroadcasterMap)
- return;
- auto entry = static_cast<BroadcasterMapEntry*>
- (mBroadcasterMap->Search(&aBroadcaster));
- if (entry) {
- nsCOMPtr<nsIAtom> attr = NS_Atomize(aAttr);
- for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
- BroadcastListener* bl = entry->mListeners[i];
- nsCOMPtr<Element> blListener = do_QueryReferent(bl->mListener);
- if (blListener == &aListener && bl->mAttribute == attr) {
- entry->mListeners.RemoveElementAt(i);
- delete bl;
- if (entry->mListeners.IsEmpty())
- mBroadcasterMap->RemoveEntry(entry);
- break;
- }
- }
- }
- }
- nsresult
- XULDocument::ExecuteOnBroadcastHandlerFor(Element* aBroadcaster,
- Element* aListener,
- nsIAtom* aAttr)
- {
- // Now we execute the onchange handler in the context of the
- // observer. We need to find the observer in order to
- // execute the handler.
- for (nsIContent* child = aListener->GetFirstChild();
- child;
- child = child->GetNextSibling()) {
- // Look for an <observes> element beneath the listener. This
- // ought to have an |element| attribute that refers to
- // aBroadcaster, and an |attribute| element that tells us what
- // attriubtes we're listening for.
- if (!child->NodeInfo()->Equals(nsGkAtoms::observes, kNameSpaceID_XUL))
- continue;
- // Is this the element that was listening to us?
- nsAutoString listeningToID;
- child->GetAttr(kNameSpaceID_None, nsGkAtoms::element, listeningToID);
- nsAutoString broadcasterID;
- aBroadcaster->GetAttr(kNameSpaceID_None, nsGkAtoms::id, broadcasterID);
- if (listeningToID != broadcasterID)
- continue;
- // We are observing the broadcaster, but is this the right
- // attribute?
- nsAutoString listeningToAttribute;
- child->GetAttr(kNameSpaceID_None, nsGkAtoms::attribute,
- listeningToAttribute);
- if (!aAttr->Equals(listeningToAttribute) &&
- !listeningToAttribute.EqualsLiteral("*")) {
- continue;
- }
- // This is the right <observes> element. Execute the
- // |onbroadcast| event handler
- WidgetEvent event(true, eXULBroadcast);
- nsCOMPtr<nsIPresShell> shell = GetShell();
- if (shell) {
- RefPtr<nsPresContext> aPresContext = shell->GetPresContext();
- // Handle the DOM event
- nsEventStatus status = nsEventStatus_eIgnore;
- EventDispatcher::Dispatch(child, aPresContext, &event, nullptr,
- &status);
- }
- }
- return NS_OK;
- }
- void
- XULDocument::AttributeWillChange(nsIDocument* aDocument,
- Element* aElement, int32_t aNameSpaceID,
- nsIAtom* aAttribute, int32_t aModType,
- const nsAttrValue* aNewValue)
- {
- MOZ_ASSERT(aElement, "Null content!");
- NS_PRECONDITION(aAttribute, "Must have an attribute that's changing!");
- // XXXbz check aNameSpaceID, dammit!
- // See if we need to update our ref map.
- if (aAttribute == nsGkAtoms::ref) {
- // Might not need this, but be safe for now.
- nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
- RemoveElementFromRefMap(aElement);
- }
- }
- static bool
- ShouldPersistAttribute(Element* aElement, nsIAtom* aAttribute)
- {
- if (aElement->IsXULElement(nsGkAtoms::window)) {
- // This is not an element of the top document, its owner is
- // not an nsXULWindow. Persist it.
- if (aElement->OwnerDoc()->GetParentDocument()) {
- return true;
- }
- // The following attributes of xul:window should be handled in
- // nsXULWindow::SavePersistentAttributes instead of here.
- if (aAttribute == nsGkAtoms::screenX ||
- aAttribute == nsGkAtoms::screenY ||
- aAttribute == nsGkAtoms::width ||
- aAttribute == nsGkAtoms::height ||
- aAttribute == nsGkAtoms::sizemode) {
- return false;
- }
- }
- return true;
- }
- void
- XULDocument::AttributeChanged(nsIDocument* aDocument,
- Element* aElement, int32_t aNameSpaceID,
- nsIAtom* aAttribute, int32_t aModType,
- const nsAttrValue* aOldValue)
- {
- NS_ASSERTION(aDocument == this, "unexpected doc");
- // Might not need this, but be safe for now.
- nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
- // XXXbz check aNameSpaceID, dammit!
- // See if we need to update our ref map.
- if (aAttribute == nsGkAtoms::ref) {
- AddElementToRefMap(aElement);
- }
- // Synchronize broadcast listeners
- if (mBroadcasterMap &&
- CanBroadcast(aNameSpaceID, aAttribute)) {
- auto entry = static_cast<BroadcasterMapEntry*>
- (mBroadcasterMap->Search(aElement));
- if (entry) {
- // We've got listeners: push the value.
- nsAutoString value;
- bool attrSet = aElement->GetAttr(kNameSpaceID_None, aAttribute, value);
- for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
- BroadcastListener* bl = entry->mListeners[i];
- if ((bl->mAttribute == aAttribute) ||
- (bl->mAttribute == nsGkAtoms::_asterisk)) {
- nsCOMPtr<Element> listenerEl
- = do_QueryReferent(bl->mListener);
- if (listenerEl) {
- nsAutoString currentValue;
- bool hasAttr = listenerEl->GetAttr(kNameSpaceID_None,
- aAttribute,
- currentValue);
- // We need to update listener only if we're
- // (1) removing an existing attribute,
- // (2) adding a new attribute or
- // (3) changing the value of an attribute.
- bool needsAttrChange =
- attrSet != hasAttr || !value.Equals(currentValue);
- nsDelayedBroadcastUpdate delayedUpdate(aElement,
- listenerEl,
- aAttribute,
- value,
- attrSet,
- needsAttrChange);
- size_t index =
- mDelayedAttrChangeBroadcasts.IndexOf(delayedUpdate,
- 0, nsDelayedBroadcastUpdate::Comparator());
- if (index != mDelayedAttrChangeBroadcasts.NoIndex) {
- if (mHandlingDelayedAttrChange) {
- NS_WARNING("Broadcasting loop!");
- continue;
- }
- mDelayedAttrChangeBroadcasts.RemoveElementAt(index);
- }
- mDelayedAttrChangeBroadcasts.AppendElement(delayedUpdate);
- }
- }
- }
- }
- }
- // checks for modifications in broadcasters
- bool listener, resolved;
- CheckBroadcasterHookup(aElement, &listener, &resolved);
- // See if there is anything we need to persist in the localstore.
- //
- // XXX Namespace handling broken :-(
- nsAutoString persist;
- aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::persist, persist);
- // Persistence of attributes of xul:window is handled in nsXULWindow.
- if (ShouldPersistAttribute(aElement, aAttribute) && !persist.IsEmpty() &&
- // XXXldb This should check that it's a token, not just a substring.
- persist.Find(nsDependentAtomString(aAttribute)) >= 0) {
- nsContentUtils::AddScriptRunner(NewRunnableMethod
- <nsIContent*, int32_t, nsIAtom*>
- (this, &XULDocument::DoPersist, aElement, kNameSpaceID_None,
- aAttribute));
- }
- }
- void
- XULDocument::ContentAppended(nsIDocument* aDocument,
- nsIContent* aContainer,
- nsIContent* aFirstNewContent,
- int32_t aNewIndexInContainer)
- {
- NS_ASSERTION(aDocument == this, "unexpected doc");
-
- // Might not need this, but be safe for now.
- nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
- // Update our element map
- nsresult rv = NS_OK;
- for (nsIContent* cur = aFirstNewContent; cur && NS_SUCCEEDED(rv);
- cur = cur->GetNextSibling()) {
- rv = AddSubtreeToDocument(cur);
- }
- }
- void
- XULDocument::ContentInserted(nsIDocument* aDocument,
- nsIContent* aContainer,
- nsIContent* aChild,
- int32_t aIndexInContainer)
- {
- NS_ASSERTION(aDocument == this, "unexpected doc");
- // Might not need this, but be safe for now.
- nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
- AddSubtreeToDocument(aChild);
- }
- void
- XULDocument::ContentRemoved(nsIDocument* aDocument,
- nsIContent* aContainer,
- nsIContent* aChild,
- int32_t aIndexInContainer,
- nsIContent* aPreviousSibling)
- {
- NS_ASSERTION(aDocument == this, "unexpected doc");
- // Might not need this, but be safe for now.
- nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
- RemoveSubtreeFromDocument(aChild);
- }
- //----------------------------------------------------------------------
- //
- // nsIXULDocument interface
- //
- void
- XULDocument::GetElementsForID(const nsAString& aID,
- nsCOMArray<nsIContent>& aElements)
- {
- aElements.Clear();
- nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aID);
- if (entry) {
- entry->AppendAllIdContent(&aElements);
- }
- nsRefMapEntry *refEntry = mRefMap.GetEntry(aID);
- if (refEntry) {
- refEntry->AppendAll(&aElements);
- }
- }
- nsresult
- XULDocument::AddForwardReference(nsForwardReference* aRef)
- {
- if (mResolutionPhase < aRef->GetPhase()) {
- if (!mForwardReferences.AppendElement(aRef)) {
- delete aRef;
- return NS_ERROR_OUT_OF_MEMORY;
- }
- }
- else {
- NS_ERROR("forward references have already been resolved");
- delete aRef;
- }
- return NS_OK;
- }
- nsresult
- XULDocument::ResolveForwardReferences()
- {
- if (mResolutionPhase == nsForwardReference::eDone)
- return NS_OK;
- NS_ASSERTION(mResolutionPhase == nsForwardReference::eStart,
- "nested ResolveForwardReferences()");
-
- // Resolve each outstanding 'forward' reference. We iterate
- // through the list of forward references until no more forward
- // references can be resolved. This annealing process is
- // guaranteed to converge because we've "closed the gate" to new
- // forward references.
- const nsForwardReference::Phase* pass = nsForwardReference::kPasses;
- while ((mResolutionPhase = *pass) != nsForwardReference::eDone) {
- uint32_t previous = 0;
- while (mForwardReferences.Length() &&
- mForwardReferences.Length() != previous) {
- previous = mForwardReferences.Length();
- for (uint32_t i = 0; i < mForwardReferences.Length(); ++i) {
- nsForwardReference* fwdref = mForwardReferences[i];
- if (fwdref->GetPhase() == *pass) {
- nsForwardReference::Result result = fwdref->Resolve();
- switch (result) {
- case nsForwardReference::eResolve_Succeeded:
- case nsForwardReference::eResolve_Error:
- mForwardReferences.RemoveElementAt(i);
- // fixup because we removed from list
- --i;
- break;
- case nsForwardReference::eResolve_Later:
- // do nothing. we'll try again later
- ;
- }
- if (mResolutionPhase == nsForwardReference::eStart) {
- // Resolve() loaded a dynamic overlay,
- // (see XULDocument::LoadOverlayInternal()).
- // Return for now, we will be called again.
- return NS_OK;
- }
- }
- }
- }
- ++pass;
- }
- mForwardReferences.Clear();
- return NS_OK;
- }
- //----------------------------------------------------------------------
- //
- // nsIDOMDocument interface
- //
- NS_IMETHODIMP
- XULDocument::GetElementsByAttribute(const nsAString& aAttribute,
- const nsAString& aValue,
- nsIDOMNodeList** aReturn)
- {
- *aReturn = GetElementsByAttribute(aAttribute, aValue).take();
- return NS_OK;
- }
- already_AddRefed<nsINodeList>
- XULDocument::GetElementsByAttribute(const nsAString& aAttribute,
- const nsAString& aValue)
- {
- nsCOMPtr<nsIAtom> attrAtom(NS_Atomize(aAttribute));
- void* attrValue = new nsString(aValue);
- RefPtr<nsContentList> list = new nsContentList(this,
- MatchAttribute,
- nsContentUtils::DestroyMatchString,
- attrValue,
- true,
- attrAtom,
- kNameSpaceID_Unknown);
-
- return list.forget();
- }
- NS_IMETHODIMP
- XULDocument::GetElementsByAttributeNS(const nsAString& aNamespaceURI,
- const nsAString& aAttribute,
- const nsAString& aValue,
- nsIDOMNodeList** aReturn)
- {
- ErrorResult rv;
- *aReturn = GetElementsByAttributeNS(aNamespaceURI, aAttribute,
- aValue, rv).take();
- return rv.StealNSResult();
- }
- already_AddRefed<nsINodeList>
- XULDocument::GetElementsByAttributeNS(const nsAString& aNamespaceURI,
- const nsAString& aAttribute,
- const nsAString& aValue,
- ErrorResult& aRv)
- {
- nsCOMPtr<nsIAtom> attrAtom(NS_Atomize(aAttribute));
- void* attrValue = new nsString(aValue);
- int32_t nameSpaceId = kNameSpaceID_Wildcard;
- if (!aNamespaceURI.EqualsLiteral("*")) {
- nsresult rv =
- nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI,
- nameSpaceId);
- if (NS_FAILED(rv)) {
- aRv.Throw(rv);
- return nullptr;
- }
- }
- RefPtr<nsContentList> list = new nsContentList(this,
- MatchAttribute,
- nsContentUtils::DestroyMatchString,
- attrValue,
- true,
- attrAtom,
- nameSpaceId);
- return list.forget();
- }
- NS_IMETHODIMP
- XULDocument::Persist(const nsAString& aID,
- const nsAString& aAttr)
- {
- // If we're currently reading persisted attributes out of the
- // localstore, _don't_ re-enter and try to set them again!
- if (mApplyingPersistedAttrs)
- return NS_OK;
- Element* element = nsDocument::GetElementById(aID);
- if (!element)
- return NS_OK;
- nsCOMPtr<nsIAtom> tag;
- int32_t nameSpaceID;
- RefPtr<mozilla::dom::NodeInfo> ni = element->GetExistingAttrNameFromQName(aAttr);
- nsresult rv;
- if (ni) {
- tag = ni->NameAtom();
- nameSpaceID = ni->NamespaceID();
- }
- else {
- // Make sure that this QName is going to be valid.
- const char16_t *colon;
- rv = nsContentUtils::CheckQName(PromiseFlatString(aAttr), true, &colon);
- if (NS_FAILED(rv)) {
- // There was an invalid character or it was malformed.
- return NS_ERROR_INVALID_ARG;
- }
- if (colon) {
- // We don't really handle namespace qualifiers in attribute names.
- return NS_ERROR_NOT_IMPLEMENTED;
- }
- tag = NS_Atomize(aAttr);
- NS_ENSURE_TRUE(tag, NS_ERROR_OUT_OF_MEMORY);
- nameSpaceID = kNameSpaceID_None;
- }
- return Persist(element, nameSpaceID, tag);
- }
- nsresult
- XULDocument::Persist(nsIContent* aElement, int32_t aNameSpaceID,
- nsIAtom* aAttribute)
- {
- // For non-chrome documents, persistance is simply broken
- if (!nsContentUtils::IsSystemPrincipal(NodePrincipal()))
- return NS_ERROR_NOT_AVAILABLE;
- if (!mLocalStore) {
- mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
- if (NS_WARN_IF(!mLocalStore)) {
- return NS_ERROR_NOT_INITIALIZED;
- }
- }
- nsAutoString id;
- aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id);
- nsAtomString attrstr(aAttribute);
- nsAutoString valuestr;
- aElement->GetAttr(kNameSpaceID_None, aAttribute, valuestr);
- nsAutoCString utf8uri;
- nsresult rv = mDocumentURI->GetSpec(utf8uri);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- NS_ConvertUTF8toUTF16 uri(utf8uri);
- bool hasAttr;
- rv = mLocalStore->HasValue(uri, id, attrstr, &hasAttr);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (hasAttr && valuestr.IsEmpty()) {
- return mLocalStore->RemoveValue(uri, id, attrstr);
- } else {
- return mLocalStore->SetValue(uri, id, attrstr, valuestr);
- }
- }
- nsresult
- XULDocument::GetViewportSize(int32_t* aWidth,
- int32_t* aHeight)
- {
- *aWidth = *aHeight = 0;
- FlushPendingNotifications(Flush_Layout);
- nsIPresShell *shell = GetShell();
- NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
- nsIFrame* frame = shell->GetRootFrame();
- NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
- nsSize size = frame->GetSize();
- *aWidth = nsPresContext::AppUnitsToIntCSSPixels(size.width);
- *aHeight = nsPresContext::AppUnitsToIntCSSPixels(size.height);
- return NS_OK;
- }
- NS_IMETHODIMP
- XULDocument::GetWidth(int32_t* aWidth)
- {
- NS_ENSURE_ARG_POINTER(aWidth);
- int32_t height;
- return GetViewportSize(aWidth, &height);
- }
- int32_t
- XULDocument::GetWidth(ErrorResult& aRv)
- {
- int32_t width;
- aRv = GetWidth(&width);
- return width;
- }
- NS_IMETHODIMP
- XULDocument::GetHeight(int32_t* aHeight)
- {
- NS_ENSURE_ARG_POINTER(aHeight);
- int32_t width;
- return GetViewportSize(&width, aHeight);
- }
- int32_t
- XULDocument::GetHeight(ErrorResult& aRv)
- {
- int32_t height;
- aRv = GetHeight(&height);
- return height;
- }
- JSObject*
- GetScopeObjectOfNode(nsIDOMNode* node)
- {
- MOZ_ASSERT(node, "Must not be called with null.");
- // Window root occasionally keeps alive a node of a document whose
- // window is already dead. If in this brief period someone calls
- // GetPopupNode and we return that node, nsNodeSH::PreCreate will throw,
- // because it will not know which scope this node belongs to. Returning
- // an orphan node like that to JS would be a bug anyway, so to avoid
- // this, let's do the same check as nsNodeSH::PreCreate does to
- // determine the scope and if it fails let's just return null in
- // XULDocument::GetPopupNode.
- nsCOMPtr<nsINode> inode = do_QueryInterface(node);
- MOZ_ASSERT(inode, "How can this happen?");
- nsIDocument* doc = inode->OwnerDoc();
- MOZ_ASSERT(inode, "This should never happen.");
- nsIGlobalObject* global = doc->GetScopeObject();
- return global ? global->GetGlobalJSObject() : nullptr;
- }
- //----------------------------------------------------------------------
- //
- // nsIDOMXULDocument interface
- //
- NS_IMETHODIMP
- XULDocument::GetPopupNode(nsIDOMNode** aNode)
- {
- *aNode = nullptr;
- nsCOMPtr<nsIDOMNode> node;
- nsCOMPtr<nsPIWindowRoot> rootWin = GetWindowRoot();
- if (rootWin)
- node = rootWin->GetPopupNode(); // addref happens here
- if (!node) {
- nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
- if (pm) {
- node = pm->GetLastTriggerPopupNode(this);
- }
- }
- if (node && nsContentUtils::CanCallerAccess(node)
- && GetScopeObjectOfNode(node)) {
- node.forget(aNode);
- }
- return NS_OK;
- }
- already_AddRefed<nsINode>
- XULDocument::GetPopupNode()
- {
- nsCOMPtr<nsIDOMNode> node;
- DebugOnly<nsresult> rv = GetPopupNode(getter_AddRefs(node));
- MOZ_ASSERT(NS_SUCCEEDED(rv));
- nsCOMPtr<nsINode> retval(do_QueryInterface(node));
- return retval.forget();
- }
- NS_IMETHODIMP
- XULDocument::SetPopupNode(nsIDOMNode* aNode)
- {
- if (aNode) {
- // only allow real node objects
- nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
- NS_ENSURE_ARG(node);
- }
- nsCOMPtr<nsPIWindowRoot> rootWin = GetWindowRoot();
- if (rootWin)
- rootWin->SetPopupNode(aNode); // addref happens here
- return NS_OK;
- }
- void
- XULDocument::SetPopupNode(nsINode* aNode)
- {
- nsCOMPtr<nsIDOMNode> node(do_QueryInterface(aNode));
- DebugOnly<nsresult> rv = SetPopupNode(node);
- MOZ_ASSERT(NS_SUCCEEDED(rv));
- }
- // Returns the rangeOffset element from the XUL Popup Manager. This is for
- // chrome callers only.
- NS_IMETHODIMP
- XULDocument::GetPopupRangeParent(nsIDOMNode** aRangeParent)
- {
- NS_ENSURE_ARG_POINTER(aRangeParent);
- *aRangeParent = nullptr;
- nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
- if (!pm)
- return NS_ERROR_FAILURE;
- int32_t offset;
- pm->GetMouseLocation(aRangeParent, &offset);
- if (*aRangeParent && !nsContentUtils::CanCallerAccess(*aRangeParent)) {
- NS_RELEASE(*aRangeParent);
- return NS_ERROR_DOM_SECURITY_ERR;
- }
- return NS_OK;
- }
- already_AddRefed<nsINode>
- XULDocument::GetPopupRangeParent(ErrorResult& aRv)
- {
- nsCOMPtr<nsIDOMNode> node;
- aRv = GetPopupRangeParent(getter_AddRefs(node));
- nsCOMPtr<nsINode> retval(do_QueryInterface(node));
- return retval.forget();
- }
- // Returns the rangeOffset element from the XUL Popup Manager. We check the
- // rangeParent to determine if the caller has rights to access to the data.
- NS_IMETHODIMP
- XULDocument::GetPopupRangeOffset(int32_t* aRangeOffset)
- {
- ErrorResult rv;
- *aRangeOffset = GetPopupRangeOffset(rv);
- return rv.StealNSResult();
- }
- int32_t
- XULDocument::GetPopupRangeOffset(ErrorResult& aRv)
- {
- nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
- if (!pm) {
- aRv.Throw(NS_ERROR_FAILURE);
- return 0;
- }
- int32_t offset;
- nsCOMPtr<nsIDOMNode> parent;
- pm->GetMouseLocation(getter_AddRefs(parent), &offset);
- if (parent && !nsContentUtils::CanCallerAccess(parent)) {
- aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
- return 0;
- }
- return offset;
- }
- NS_IMETHODIMP
- XULDocument::GetTooltipNode(nsIDOMNode** aNode)
- {
- *aNode = nullptr;
- nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
- if (pm) {
- nsCOMPtr<nsIDOMNode> node = pm->GetLastTriggerTooltipNode(this);
- if (node && nsContentUtils::CanCallerAccess(node))
- node.forget(aNode);
- }
- return NS_OK;
- }
- already_AddRefed<nsINode>
- XULDocument::GetTooltipNode()
- {
- nsCOMPtr<nsIDOMNode> node;
- DebugOnly<nsresult> rv = GetTooltipNode(getter_AddRefs(node));
- MOZ_ASSERT(NS_SUCCEEDED(rv));
- nsCOMPtr<nsINode> retval(do_QueryInterface(node));
- return retval.forget();
- }
- NS_IMETHODIMP
- XULDocument::SetTooltipNode(nsIDOMNode* aNode)
- {
- // do nothing
- return NS_OK;
- }
- NS_IMETHODIMP
- XULDocument::GetCommandDispatcher(nsIDOMXULCommandDispatcher** aTracker)
- {
- *aTracker = mCommandDispatcher;
- NS_IF_ADDREF(*aTracker);
- return NS_OK;
- }
- Element*
- XULDocument::GetRefById(const nsAString& aID)
- {
- if (nsRefMapEntry* refEntry = mRefMap.GetEntry(aID)) {
- MOZ_ASSERT(refEntry->GetFirstElement());
- return refEntry->GetFirstElement();
- }
- return nullptr;
- }
- nsresult
- XULDocument::AddElementToDocumentPre(Element* aElement)
- {
- // Do a bunch of work that's necessary when an element gets added
- // to the XUL Document.
- nsresult rv;
- // 1. Add the element to the resource-to-element map. Also add it to
- // the id map, since it seems this can be called when creating
- // elements from prototypes.
- nsIAtom* id = aElement->GetID();
- if (id) {
- // FIXME: Shouldn't BindToTree take care of this?
- nsAutoScriptBlocker scriptBlocker;
- AddToIdTable(aElement, id);
- }
- rv = AddElementToRefMap(aElement);
- if (NS_FAILED(rv)) return rv;
- // 2. If the element is a 'command updater' (i.e., has a
- // "commandupdater='true'" attribute), then add the element to the
- // document's command dispatcher
- if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::commandupdater,
- nsGkAtoms::_true, eCaseMatters)) {
- rv = nsXULContentUtils::SetCommandUpdater(this, aElement);
- if (NS_FAILED(rv)) return rv;
- }
- // 3. Check for a broadcaster hookup attribute, in which case
- // we'll hook the node up as a listener on a broadcaster.
- bool listener, resolved;
- rv = CheckBroadcasterHookup(aElement, &listener, &resolved);
- if (NS_FAILED(rv)) return rv;
- // If it's not there yet, we may be able to defer hookup until
- // later.
- if (listener && !resolved && (mResolutionPhase != nsForwardReference::eDone)) {
- BroadcasterHookup* hookup = new BroadcasterHookup(this, aElement);
- rv = AddForwardReference(hookup);
- if (NS_FAILED(rv)) return rv;
- }
- return NS_OK;
- }
- nsresult
- XULDocument::AddElementToDocumentPost(Element* aElement)
- {
- // We need to pay special attention to the keyset tag to set up a listener
- if (aElement->NodeInfo()->Equals(nsGkAtoms::keyset, kNameSpaceID_XUL)) {
- // Create our XUL key listener and hook it up.
- nsXBLService::AttachGlobalKeyHandler(aElement);
- }
- // See if we need to attach a XUL template to this node
- bool needsHookup;
- nsresult rv = CheckTemplateBuilderHookup(aElement, &needsHookup);
- if (NS_FAILED(rv))
- return rv;
- if (needsHookup) {
- if (mResolutionPhase == nsForwardReference::eDone) {
- rv = CreateTemplateBuilder(aElement);
- if (NS_FAILED(rv))
- return rv;
- }
- else {
- TemplateBuilderHookup* hookup = new TemplateBuilderHookup(aElement);
- rv = AddForwardReference(hookup);
- if (NS_FAILED(rv))
- return rv;
- }
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- XULDocument::AddSubtreeToDocument(nsIContent* aContent)
- {
- NS_ASSERTION(aContent->GetUncomposedDoc() == this, "Element not in doc!");
- // From here on we only care about elements.
- if (!aContent->IsElement()) {
- return NS_OK;
- }
- Element* aElement = aContent->AsElement();
- // Do pre-order addition magic
- nsresult rv = AddElementToDocumentPre(aElement);
- if (NS_FAILED(rv)) return rv;
- // Recurse to children
- for (nsIContent* child = aElement->GetLastChild();
- child;
- child = child->GetPreviousSibling()) {
- rv = AddSubtreeToDocument(child);
- if (NS_FAILED(rv))
- return rv;
- }
- // Do post-order addition magic
- return AddElementToDocumentPost(aElement);
- }
- NS_IMETHODIMP
- XULDocument::RemoveSubtreeFromDocument(nsIContent* aContent)
- {
- // From here on we only care about elements.
- if (!aContent->IsElement()) {
- return NS_OK;
- }
- Element* aElement = aContent->AsElement();
- // Do a bunch of cleanup to remove an element from the XUL
- // document.
- nsresult rv;
- if (aElement->NodeInfo()->Equals(nsGkAtoms::keyset, kNameSpaceID_XUL)) {
- nsXBLService::DetachGlobalKeyHandler(aElement);
- }
- // 1. Remove any children from the document.
- for (nsIContent* child = aElement->GetLastChild();
- child;
- child = child->GetPreviousSibling()) {
- rv = RemoveSubtreeFromDocument(child);
- if (NS_FAILED(rv))
- return rv;
- }
- // 2. Remove the element from the resource-to-element map.
- // Also remove it from the id map, since we added it in
- // AddElementToDocumentPre().
- RemoveElementFromRefMap(aElement);
- nsIAtom* id = aElement->GetID();
- if (id) {
- // FIXME: Shouldn't UnbindFromTree take care of this?
- nsAutoScriptBlocker scriptBlocker;
- RemoveFromIdTable(aElement, id);
- }
- // 3. If the element is a 'command updater', then remove the
- // element from the document's command dispatcher.
- if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::commandupdater,
- nsGkAtoms::_true, eCaseMatters)) {
- nsCOMPtr<nsIDOMElement> domelement = do_QueryInterface(aElement);
- NS_ASSERTION(domelement != nullptr, "not a DOM element");
- if (! domelement)
- return NS_ERROR_UNEXPECTED;
- rv = mCommandDispatcher->RemoveCommandUpdater(domelement);
- if (NS_FAILED(rv)) return rv;
- }
- // 4. Remove the element from our broadcaster map, since it is no longer
- // in the document.
- nsCOMPtr<Element> broadcaster, listener;
- nsAutoString attribute, broadcasterID;
- rv = FindBroadcaster(aElement, getter_AddRefs(listener),
- broadcasterID, attribute, getter_AddRefs(broadcaster));
- if (rv == NS_FINDBROADCASTER_FOUND) {
- RemoveBroadcastListenerFor(*broadcaster, *listener, attribute);
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- XULDocument::SetTemplateBuilderFor(nsIContent* aContent,
- nsIXULTemplateBuilder* aBuilder)
- {
- if (! mTemplateBuilderTable) {
- if (!aBuilder) {
- return NS_OK;
- }
- mTemplateBuilderTable = new BuilderTable;
- }
- if (aBuilder) {
- mTemplateBuilderTable->Put(aContent, aBuilder);
- }
- else {
- mTemplateBuilderTable->Remove(aContent);
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- XULDocument::GetTemplateBuilderFor(nsIContent* aContent,
- nsIXULTemplateBuilder** aResult)
- {
- if (mTemplateBuilderTable) {
- mTemplateBuilderTable->Get(aContent, aResult);
- }
- else
- *aResult = nullptr;
- return NS_OK;
- }
- static void
- GetRefMapAttribute(Element* aElement, nsAutoString* aValue)
- {
- aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::ref, *aValue);
- }
- nsresult
- XULDocument::AddElementToRefMap(Element* aElement)
- {
- // Look at the element's 'ref' attribute, and if set,
- // add an entry in the resource-to-element map to the element.
- nsAutoString value;
- GetRefMapAttribute(aElement, &value);
- if (!value.IsEmpty()) {
- nsRefMapEntry *entry = mRefMap.PutEntry(value);
- if (!entry)
- return NS_ERROR_OUT_OF_MEMORY;
- if (!entry->AddElement(aElement))
- return NS_ERROR_OUT_OF_MEMORY;
- }
- return NS_OK;
- }
- void
- XULDocument::RemoveElementFromRefMap(Element* aElement)
- {
- // Remove the element from the resource-to-element map.
- nsAutoString value;
- GetRefMapAttribute(aElement, &value);
- if (!value.IsEmpty()) {
- nsRefMapEntry *entry = mRefMap.GetEntry(value);
- if (!entry)
- return;
- if (entry->RemoveElement(aElement)) {
- mRefMap.RemoveEntry(entry);
- }
- }
- }
- //----------------------------------------------------------------------
- //
- // nsIDOMNode interface
- //
- nsresult
- XULDocument::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const
- {
- // We don't allow cloning of a XUL document
- *aResult = nullptr;
- return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
- }
- //----------------------------------------------------------------------
- //
- // Implementation methods
- //
- nsresult
- XULDocument::Init()
- {
- nsresult rv = XMLDocument::Init();
- NS_ENSURE_SUCCESS(rv, rv);
- // Create our command dispatcher and hook it up.
- mCommandDispatcher = new nsXULCommandDispatcher(this);
- if (gRefCnt++ == 0) {
- // ensure that the XUL prototype cache is instantiated successfully,
- // so that we can use nsXULPrototypeCache::GetInstance() without
- // null-checks in the rest of the class.
- nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
- if (!cache) {
- NS_ERROR("Could not instantiate nsXULPrototypeCache");
- return NS_ERROR_FAILURE;
- }
- }
- Preferences::RegisterCallback(XULDocument::DirectionChanged,
- "intl.uidirection.", this);
- return NS_OK;
- }
- nsresult
- XULDocument::StartLayout(void)
- {
- mMayStartLayout = true;
- nsCOMPtr<nsIPresShell> shell = GetShell();
- if (shell) {
- // Resize-reflow this time
- nsPresContext *cx = shell->GetPresContext();
- NS_ASSERTION(cx != nullptr, "no pres context");
- if (! cx)
- return NS_ERROR_UNEXPECTED;
- nsCOMPtr<nsIDocShell> docShell = cx->GetDocShell();
- NS_ASSERTION(docShell != nullptr, "container is not a docshell");
- if (! docShell)
- return NS_ERROR_UNEXPECTED;
- nsresult rv = NS_OK;
- nsRect r = cx->GetVisibleArea();
- rv = shell->Initialize(r.width, r.height);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- return NS_OK;
- }
- /* static */
- bool
- XULDocument::MatchAttribute(Element* aElement,
- int32_t aNamespaceID,
- nsIAtom* aAttrName,
- void* aData)
- {
- NS_PRECONDITION(aElement, "Must have content node to work with!");
- nsString* attrValue = static_cast<nsString*>(aData);
- if (aNamespaceID != kNameSpaceID_Unknown &&
- aNamespaceID != kNameSpaceID_Wildcard) {
- return attrValue->EqualsLiteral("*") ?
- aElement->HasAttr(aNamespaceID, aAttrName) :
- aElement->AttrValueIs(aNamespaceID, aAttrName, *attrValue,
- eCaseMatters);
- }
- // Qualified name match. This takes more work.
- uint32_t count = aElement->GetAttrCount();
- for (uint32_t i = 0; i < count; ++i) {
- const nsAttrName* name = aElement->GetAttrNameAt(i);
- bool nameMatch;
- if (name->IsAtom()) {
- nameMatch = name->Atom() == aAttrName;
- } else if (aNamespaceID == kNameSpaceID_Wildcard) {
- nameMatch = name->NodeInfo()->Equals(aAttrName);
- } else {
- nameMatch = name->NodeInfo()->QualifiedNameEquals(aAttrName);
- }
- if (nameMatch) {
- return attrValue->EqualsLiteral("*") ||
- aElement->AttrValueIs(name->NamespaceID(), name->LocalName(),
- *attrValue, eCaseMatters);
- }
- }
- return false;
- }
- nsresult
- XULDocument::PrepareToLoad(nsISupports* aContainer,
- const char* aCommand,
- nsIChannel* aChannel,
- nsILoadGroup* aLoadGroup,
- nsIParser** aResult)
- {
- // Get the document's principal
- nsCOMPtr<nsIPrincipal> principal;
- nsContentUtils::GetSecurityManager()->
- GetChannelResultPrincipal(aChannel, getter_AddRefs(principal));
- return PrepareToLoadPrototype(mDocumentURI, aCommand, principal, aResult);
- }
- nsresult
- XULDocument::PrepareToLoadPrototype(nsIURI* aURI, const char* aCommand,
- nsIPrincipal* aDocumentPrincipal,
- nsIParser** aResult)
- {
- nsresult rv;
- // Create a new prototype document.
- rv = NS_NewXULPrototypeDocument(getter_AddRefs(mCurrentPrototype));
- if (NS_FAILED(rv)) return rv;
- rv = mCurrentPrototype->InitPrincipal(aURI, aDocumentPrincipal);
- if (NS_FAILED(rv)) {
- mCurrentPrototype = nullptr;
- return rv;
- }
- // Bootstrap the master document prototype.
- if (! mMasterPrototype) {
- mMasterPrototype = mCurrentPrototype;
- // Set our principal based on the master proto.
- SetPrincipal(aDocumentPrincipal);
- }
- // Create a XUL content sink, a parser, and kick off a load for
- // the overlay.
- RefPtr<XULContentSinkImpl> sink = new XULContentSinkImpl();
- rv = sink->Init(this, mCurrentPrototype);
- NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to initialize datasource sink");
- if (NS_FAILED(rv)) return rv;
- nsCOMPtr<nsIParser> parser = do_CreateInstance(kParserCID, &rv);
- NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create parser");
- if (NS_FAILED(rv)) return rv;
- parser->SetCommand(nsCRT::strcmp(aCommand, "view-source") ? eViewNormal :
- eViewSource);
- parser->SetDocumentCharset(NS_LITERAL_CSTRING("UTF-8"),
- kCharsetFromDocTypeDefault);
- parser->SetContentSink(sink); // grabs a reference to the parser
- parser.forget(aResult);
- return NS_OK;
- }
- nsresult
- XULDocument::ApplyPersistentAttributes()
- {
- // For non-chrome documents, persistance is simply broken
- if (!nsContentUtils::IsSystemPrincipal(NodePrincipal()))
- return NS_ERROR_NOT_AVAILABLE;
- // Add all of the 'persisted' attributes into the content
- // model.
- if (!mLocalStore) {
- mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
- if (NS_WARN_IF(!mLocalStore)) {
- return NS_ERROR_NOT_INITIALIZED;
- }
- }
- mApplyingPersistedAttrs = true;
- ApplyPersistentAttributesInternal();
- mApplyingPersistedAttrs = false;
- // After we've applied persistence once, we should only reapply
- // it to nodes created by overlays
- mRestrictPersistence = true;
- mPersistenceIds.Clear();
- return NS_OK;
- }
- nsresult
- XULDocument::ApplyPersistentAttributesInternal()
- {
- nsCOMArray<nsIContent> elements;
- nsAutoCString utf8uri;
- nsresult rv = mDocumentURI->GetSpec(utf8uri);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- NS_ConvertUTF8toUTF16 uri(utf8uri);
- // Get a list of element IDs for which persisted values are available
- nsCOMPtr<nsIStringEnumerator> ids;
- rv = mLocalStore->GetIDsEnumerator(uri, getter_AddRefs(ids));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- while (1) {
- bool hasmore = false;
- ids->HasMore(&hasmore);
- if (!hasmore) {
- break;
- }
- nsAutoString id;
- ids->GetNext(id);
- if (mRestrictPersistence && !mPersistenceIds.Contains(id)) {
- continue;
- }
- // This will clear the array if there are no elements.
- GetElementsForID(id, elements);
- if (!elements.Count()) {
- continue;
- }
- rv = ApplyPersistentAttributesToElements(id, elements);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- return NS_OK;
- }
- nsresult
- XULDocument::ApplyPersistentAttributesToElements(const nsAString &aID,
- nsCOMArray<nsIContent>& aElements)
- {
- nsAutoCString utf8uri;
- nsresult rv = mDocumentURI->GetSpec(utf8uri);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- NS_ConvertUTF8toUTF16 uri(utf8uri);
- // Get a list of attributes for which persisted values are available
- nsCOMPtr<nsIStringEnumerator> attrs;
- rv = mLocalStore->GetAttributeEnumerator(uri, aID, getter_AddRefs(attrs));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- while (1) {
- bool hasmore = PR_FALSE;
- attrs->HasMore(&hasmore);
- if (!hasmore) {
- break;
- }
- nsAutoString attrstr;
- attrs->GetNext(attrstr);
- nsAutoString value;
- rv = mLocalStore->GetValue(uri, aID, attrstr, value);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsCOMPtr<nsIAtom> attr = NS_Atomize(attrstr);
- if (NS_WARN_IF(!attr)) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
- uint32_t cnt = aElements.Count();
- for (int32_t i = int32_t(cnt) - 1; i >= 0; --i) {
- nsCOMPtr<nsIContent> element = aElements.SafeObjectAt(i);
- if (!element) {
- continue;
- }
- rv = element->SetAttr(kNameSpaceID_None, attr, value, PR_TRUE);
- }
- }
- return NS_OK;
- }
- void
- XULDocument::TraceProtos(JSTracer* aTrc, uint32_t aGCNumber)
- {
- uint32_t i, count = mPrototypes.Length();
- for (i = 0; i < count; ++i) {
- mPrototypes[i]->TraceProtos(aTrc, aGCNumber);
- }
- if (mCurrentPrototype) {
- mCurrentPrototype->TraceProtos(aTrc, aGCNumber);
- }
- }
- //----------------------------------------------------------------------
- //
- // XULDocument::ContextStack
- //
- XULDocument::ContextStack::ContextStack()
- : mTop(nullptr), mDepth(0)
- {
- }
- XULDocument::ContextStack::~ContextStack()
- {
- while (mTop) {
- Entry* doomed = mTop;
- mTop = mTop->mNext;
- NS_IF_RELEASE(doomed->mElement);
- delete doomed;
- }
- }
- nsresult
- XULDocument::ContextStack::Push(nsXULPrototypeElement* aPrototype,
- nsIContent* aElement)
- {
- Entry* entry = new Entry;
- entry->mPrototype = aPrototype;
- entry->mElement = aElement;
- NS_IF_ADDREF(entry->mElement);
- entry->mIndex = 0;
- entry->mNext = mTop;
- mTop = entry;
- ++mDepth;
- return NS_OK;
- }
- nsresult
- XULDocument::ContextStack::Pop()
- {
- if (mDepth == 0)
- return NS_ERROR_UNEXPECTED;
- Entry* doomed = mTop;
- mTop = mTop->mNext;
- --mDepth;
- NS_IF_RELEASE(doomed->mElement);
- delete doomed;
- return NS_OK;
- }
- nsresult
- XULDocument::ContextStack::Peek(nsXULPrototypeElement** aPrototype,
- nsIContent** aElement,
- int32_t* aIndex)
- {
- if (mDepth == 0)
- return NS_ERROR_UNEXPECTED;
- *aPrototype = mTop->mPrototype;
- *aElement = mTop->mElement;
- NS_IF_ADDREF(*aElement);
- *aIndex = mTop->mIndex;
- return NS_OK;
- }
- nsresult
- XULDocument::ContextStack::SetTopIndex(int32_t aIndex)
- {
- if (mDepth == 0)
- return NS_ERROR_UNEXPECTED;
- mTop->mIndex = aIndex;
- return NS_OK;
- }
- //----------------------------------------------------------------------
- //
- // Content model walking routines
- //
- nsresult
- XULDocument::PrepareToWalk()
- {
- // Prepare to walk the mCurrentPrototype
- nsresult rv;
- // Keep an owning reference to the prototype document so that its
- // elements aren't yanked from beneath us.
- mPrototypes.AppendElement(mCurrentPrototype);
- // Get the prototype's root element and initialize the context
- // stack for the prototype walk.
- nsXULPrototypeElement* proto = mCurrentPrototype->GetRootElement();
- if (! proto) {
- if (MOZ_LOG_TEST(gXULLog, LogLevel::Error)) {
- nsCOMPtr<nsIURI> url = mCurrentPrototype->GetURI();
- nsAutoCString urlspec;
- rv = url->GetSpec(urlspec);
- if (NS_FAILED(rv)) return rv;
- MOZ_LOG(gXULLog, LogLevel::Error,
- ("xul: error parsing '%s'", urlspec.get()));
- }
- return NS_OK;
- }
- uint32_t piInsertionPoint = 0;
- if (mState != eState_Master) {
- int32_t indexOfRoot = IndexOf(GetRootElement());
- NS_ASSERTION(indexOfRoot >= 0,
- "No root content when preparing to walk overlay!");
- piInsertionPoint = indexOfRoot;
- }
- const nsTArray<RefPtr<nsXULPrototypePI> >& processingInstructions =
- mCurrentPrototype->GetProcessingInstructions();
- uint32_t total = processingInstructions.Length();
- for (uint32_t i = 0; i < total; ++i) {
- rv = CreateAndInsertPI(processingInstructions[i],
- this, piInsertionPoint + i);
- if (NS_FAILED(rv)) return rv;
- }
- // Now check the chrome registry for any additional overlays.
- rv = AddChromeOverlays();
- if (NS_FAILED(rv)) return rv;
- // Do one-time initialization if we're preparing to walk the
- // master document's prototype.
- RefPtr<Element> root;
- if (mState == eState_Master) {
- // Add the root element
- rv = CreateElementFromPrototype(proto, getter_AddRefs(root), true);
- if (NS_FAILED(rv)) return rv;
- rv = AppendChildTo(root, false);
- if (NS_FAILED(rv)) return rv;
-
- rv = AddElementToRefMap(root);
- if (NS_FAILED(rv)) return rv;
- // Block onload until we've finished building the complete
- // document content model.
- BlockOnload();
- }
- // There'd better not be anything on the context stack at this
- // point! This is the basis case for our "induction" in
- // ResumeWalk(), below, which'll assume that there's always a
- // content element on the context stack if either 1) we're in the
- // "master" document, or 2) we're in an overlay, and we've got
- // more than one prototype element (the single, root "overlay"
- // element) on the stack.
- NS_ASSERTION(mContextStack.Depth() == 0, "something's on the context stack already");
- if (mContextStack.Depth() != 0)
- return NS_ERROR_UNEXPECTED;
- rv = mContextStack.Push(proto, root);
- if (NS_FAILED(rv)) return rv;
- return NS_OK;
- }
- nsresult
- XULDocument::CreateAndInsertPI(const nsXULPrototypePI* aProtoPI,
- nsINode* aParent, uint32_t aIndex)
- {
- NS_PRECONDITION(aProtoPI, "null ptr");
- NS_PRECONDITION(aParent, "null ptr");
- RefPtr<ProcessingInstruction> node =
- NS_NewXMLProcessingInstruction(mNodeInfoManager, aProtoPI->mTarget,
- aProtoPI->mData);
- nsresult rv;
- if (aProtoPI->mTarget.EqualsLiteral("xml-stylesheet")) {
- rv = InsertXMLStylesheetPI(aProtoPI, aParent, aIndex, node);
- } else if (aProtoPI->mTarget.EqualsLiteral("xul-overlay")) {
- rv = InsertXULOverlayPI(aProtoPI, aParent, aIndex, node);
- } else {
- // No special processing, just add the PI to the document.
- rv = aParent->InsertChildAt(node, aIndex, false);
- }
- return rv;
- }
- nsresult
- XULDocument::InsertXMLStylesheetPI(const nsXULPrototypePI* aProtoPI,
- nsINode* aParent,
- uint32_t aIndex,
- nsIContent* aPINode)
- {
- nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(aPINode));
- NS_ASSERTION(ssle, "passed XML Stylesheet node does not "
- "implement nsIStyleSheetLinkingElement!");
- nsresult rv;
- ssle->InitStyleLinkElement(false);
- // We want to be notified when the style sheet finishes loading, so
- // disable style sheet loading for now.
- ssle->SetEnableUpdates(false);
- ssle->OverrideBaseURI(mCurrentPrototype->GetURI());
- rv = aParent->InsertChildAt(aPINode, aIndex, false);
- if (NS_FAILED(rv)) return rv;
- ssle->SetEnableUpdates(true);
- // load the stylesheet if necessary, passing ourselves as
- // nsICSSObserver
- bool willNotify;
- bool isAlternate;
- rv = ssle->UpdateStyleSheet(this, &willNotify, &isAlternate);
- if (NS_SUCCEEDED(rv) && willNotify && !isAlternate) {
- ++mPendingSheets;
- }
- // Ignore errors from UpdateStyleSheet; we don't want failure to
- // do that to break the XUL document load. But do propagate out
- // NS_ERROR_OUT_OF_MEMORY.
- if (rv == NS_ERROR_OUT_OF_MEMORY) {
- return rv;
- }
-
- return NS_OK;
- }
- nsresult
- XULDocument::InsertXULOverlayPI(const nsXULPrototypePI* aProtoPI,
- nsINode* aParent,
- uint32_t aIndex,
- nsIContent* aPINode)
- {
- nsresult rv;
- rv = aParent->InsertChildAt(aPINode, aIndex, false);
- if (NS_FAILED(rv)) return rv;
- // xul-overlay PI is special only in prolog
- if (!nsContentUtils::InProlog(aPINode)) {
- return NS_OK;
- }
- nsAutoString href;
- nsContentUtils::GetPseudoAttributeValue(aProtoPI->mData,
- nsGkAtoms::href,
- href);
- // If there was no href, we can't do anything with this PI
- if (href.IsEmpty()) {
- return NS_OK;
- }
- // Add the overlay to our list of overlays that need to be processed.
- nsCOMPtr<nsIURI> uri;
- rv = NS_NewURI(getter_AddRefs(uri), href, nullptr,
- mCurrentPrototype->GetURI());
- if (NS_SUCCEEDED(rv)) {
- // We insert overlays into mUnloadedOverlays at the same index in
- // document order, so they end up in the reverse of the document
- // order in mUnloadedOverlays.
- // This is needed because the code in ResumeWalk loads the overlays
- // by processing the last item of mUnloadedOverlays and removing it
- // from the array.
- mUnloadedOverlays.InsertElementAt(0, uri);
- rv = NS_OK;
- } else if (rv == NS_ERROR_MALFORMED_URI) {
- // The URL is bad, move along. Don't propagate for now.
- // XXX report this to the Error Console (bug 359846)
- rv = NS_OK;
- }
- return rv;
- }
- nsresult
- XULDocument::AddChromeOverlays()
- {
- nsresult rv;
- nsCOMPtr<nsIURI> docUri = mCurrentPrototype->GetURI();
- /* overlays only apply to chrome or about URIs */
- if (!IsOverlayAllowed(docUri)) return NS_OK;
- nsCOMPtr<nsIXULOverlayProvider> chromeReg =
- mozilla::services::GetXULOverlayProviderService();
- // In embedding situations, the chrome registry may not provide overlays,
- // or even exist at all; that's OK.
- NS_ENSURE_TRUE(chromeReg, NS_OK);
- nsCOMPtr<nsISimpleEnumerator> overlays;
- rv = chromeReg->GetXULOverlays(docUri, getter_AddRefs(overlays));
- NS_ENSURE_SUCCESS(rv, rv);
- bool moreOverlays;
- nsCOMPtr<nsISupports> next;
- nsCOMPtr<nsIURI> uri;
- while (NS_SUCCEEDED(rv = overlays->HasMoreElements(&moreOverlays)) &&
- moreOverlays) {
- rv = overlays->GetNext(getter_AddRefs(next));
- if (NS_FAILED(rv) || !next) break;
- uri = do_QueryInterface(next);
- if (!uri) {
- NS_ERROR("Chrome registry handed me a non-nsIURI object!");
- continue;
- }
- // Same comment as in XULDocument::InsertXULOverlayPI
- mUnloadedOverlays.InsertElementAt(0, uri);
- }
- return rv;
- }
- NS_IMETHODIMP
- XULDocument::LoadOverlay(const nsAString& aURL, nsIObserver* aObserver)
- {
- nsresult rv;
- nsCOMPtr<nsIURI> uri;
- rv = NS_NewURI(getter_AddRefs(uri), aURL, nullptr);
- if (NS_FAILED(rv)) return rv;
- if (aObserver) {
- nsIObserver* obs = nullptr;
- if (!mOverlayLoadObservers) {
- mOverlayLoadObservers = new nsInterfaceHashtable<nsURIHashKey,nsIObserver>;
- }
- obs = mOverlayLoadObservers->GetWeak(uri);
- if (obs) {
- // We don't support loading the same overlay twice into the same
- // document - that doesn't make sense anyway.
- return NS_ERROR_FAILURE;
- }
- mOverlayLoadObservers->Put(uri, aObserver);
- }
- bool shouldReturn, failureFromContent;
- rv = LoadOverlayInternal(uri, true, &shouldReturn, &failureFromContent);
- if (NS_FAILED(rv) && mOverlayLoadObservers)
- mOverlayLoadObservers->Remove(uri); // remove the observer if LoadOverlayInternal generated an error
- return rv;
- }
- nsresult
- XULDocument::LoadOverlayInternal(nsIURI* aURI, bool aIsDynamic,
- bool* aShouldReturn,
- bool* aFailureFromContent)
- {
- nsresult rv;
- *aShouldReturn = false;
- *aFailureFromContent = false;
- if (MOZ_LOG_TEST(gXULLog, LogLevel::Debug)) {
- nsCOMPtr<nsIURI> uri;
- mChannel->GetOriginalURI(getter_AddRefs(uri));
- MOZ_LOG(gXULLog, LogLevel::Debug,
- ("xul: %s loading overlay %s",
- uri ? uri->GetSpecOrDefault().get() : "",
- aURI->GetSpecOrDefault().get()));
- }
- if (aIsDynamic)
- mResolutionPhase = nsForwardReference::eStart;
- // Look in the prototype cache for the prototype document with
- // the specified overlay URI. Only use the cache if the containing
- // document is chrome otherwise it may not have a system principal and
- // the cached document will, see bug 565610.
- bool overlayIsChrome = IsChromeURI(aURI);
- bool documentIsChrome = IsChromeURI(mDocumentURI);
- mCurrentPrototype = overlayIsChrome && documentIsChrome ?
- nsXULPrototypeCache::GetInstance()->GetPrototype(aURI) : nullptr;
- // Same comment as nsChromeProtocolHandler::NewChannel and
- // XULDocument::StartDocumentLoad
- // - Ben Goodger
- //
- // We don't abort on failure here because there are too many valid
- // cases that can return failure, and the null-ness of |proto| is
- // enough to trigger the fail-safe parse-from-disk solution.
- // Example failure cases (for reference) include:
- //
- // NS_ERROR_NOT_AVAILABLE: the URI was not found in the FastLoad file,
- // parse from disk
- // other: the FastLoad file, XUL.mfl, could not be found, probably
- // due to being accessed before a profile has been selected
- // (e.g. loading chrome for the profile manager itself).
- // The .xul file must be parsed from disk.
- bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
- if (useXULCache && mCurrentPrototype) {
- bool loaded;
- rv = mCurrentPrototype->AwaitLoadDone(this, &loaded);
- if (NS_FAILED(rv)) return rv;
- if (! loaded) {
- // Return to the main event loop and eagerly await the
- // prototype overlay load's completion. When the content
- // sink completes, it will trigger an EndLoad(), which'll
- // wind us back up here, in ResumeWalk().
- *aShouldReturn = true;
- return NS_OK;
- }
- MOZ_LOG(gXULLog, LogLevel::Debug, ("xul: overlay was cached"));
- // Found the overlay's prototype in the cache, fully loaded. If
- // this is a dynamic overlay, this will call ResumeWalk.
- // Otherwise, we'll return to ResumeWalk, which called us.
- return OnPrototypeLoadDone(aIsDynamic);
- }
- else {
- // Not there. Initiate a load.
- MOZ_LOG(gXULLog, LogLevel::Debug, ("xul: overlay was not cached"));
- if (mIsGoingAway) {
- MOZ_LOG(gXULLog, LogLevel::Debug, ("xul: ...and document already destroyed"));
- return NS_ERROR_NOT_AVAILABLE;
- }
- // We'll set the right principal on the proto doc when we get
- // OnStartRequest from the parser, so just pass in a null principal for
- // now.
- nsCOMPtr<nsIParser> parser;
- rv = PrepareToLoadPrototype(aURI, "view", nullptr, getter_AddRefs(parser));
- if (NS_FAILED(rv)) return rv;
- // Predicate mIsWritingFastLoad on the XUL cache being enabled,
- // so we don't have to re-check whether the cache is enabled all
- // the time.
- mIsWritingFastLoad = useXULCache;
- nsCOMPtr<nsIStreamListener> listener = do_QueryInterface(parser);
- if (! listener)
- return NS_ERROR_UNEXPECTED;
- // Add an observer to the parser; this'll get called when
- // Necko fires its On[Start|Stop]Request() notifications,
- // and will let us recover from a missing overlay.
- RefPtr<ParserObserver> parserObserver =
- new ParserObserver(this, mCurrentPrototype);
- parser->Parse(aURI, parserObserver);
- parserObserver = nullptr;
- nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
- nsCOMPtr<nsIChannel> channel;
- // Set the owner of the channel to be our principal so
- // that the overlay's JSObjects etc end up being created
- // with the right principal and in the correct
- // compartment.
- rv = NS_NewChannel(getter_AddRefs(channel),
- aURI,
- NodePrincipal(),
- nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS |
- nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
- nsIContentPolicy::TYPE_OTHER,
- group);
- if (NS_SUCCEEDED(rv)) {
- rv = channel->AsyncOpen2(listener);
- }
- if (NS_FAILED(rv)) {
- // Abandon this prototype
- mCurrentPrototype = nullptr;
- // The parser won't get an OnStartRequest and
- // OnStopRequest, so it needs a Terminate.
- parser->Terminate();
- // Just move on to the next overlay.
- ReportMissingOverlay(aURI);
- // XXX the error could indicate an internal error as well...
- *aFailureFromContent = true;
- return rv;
- }
- // If it's a 'chrome:' prototype document, then put it into
- // the prototype cache; other XUL documents will be reloaded
- // each time. We must do this after AsyncOpen,
- // or chrome code will wrongly create a cached chrome channel
- // instead of a real one. Prototypes are only cached when the
- // document to be overlayed is chrome to avoid caching overlay
- // scripts with incorrect principals, see bug 565610.
- if (useXULCache && overlayIsChrome && documentIsChrome) {
- nsXULPrototypeCache::GetInstance()->PutPrototype(mCurrentPrototype);
- }
- // Return to the main event loop and eagerly await the
- // overlay load's completion. When the content sink
- // completes, it will trigger an EndLoad(), which'll wind
- // us back in ResumeWalk().
- if (!aIsDynamic)
- *aShouldReturn = true;
- }
- return NS_OK;
- }
- nsresult
- XULDocument::ResumeWalk()
- {
- // Walk the prototype and build the delegate content model. The
- // walk is performed in a top-down, left-to-right fashion. That
- // is, a parent is built before any of its children; a node is
- // only built after all of its siblings to the left are fully
- // constructed.
- //
- // It is interruptable so that transcluded documents (e.g.,
- // <html:script src="..." />) can be properly re-loaded if the
- // cached copy of the document becomes stale.
- nsresult rv;
- nsCOMPtr<nsIURI> overlayURI =
- mCurrentPrototype ? mCurrentPrototype->GetURI() : nullptr;
- while (1) {
- // Begin (or resume) walking the current prototype.
- while (mContextStack.Depth() > 0) {
- // Look at the top of the stack to determine what we're
- // currently working on.
- // This will always be a node already constructed and
- // inserted to the actual document.
- nsXULPrototypeElement* proto;
- nsCOMPtr<nsIContent> element;
- int32_t indx; // all children of proto before indx (not
- // inclusive) have already been constructed
- rv = mContextStack.Peek(&proto, getter_AddRefs(element), &indx);
- if (NS_FAILED(rv)) return rv;
- if (indx >= (int32_t)proto->mChildren.Length()) {
- if (element) {
- // We've processed all of the prototype's children. If
- // we're in the master prototype, do post-order
- // document-level hookup. (An overlay will get its
- // document hookup done when it's successfully
- // resolved.)
- if (mState == eState_Master) {
- AddElementToDocumentPost(element->AsElement());
- if (element->NodeInfo()->Equals(nsGkAtoms::style,
- kNameSpaceID_XHTML) ||
- element->NodeInfo()->Equals(nsGkAtoms::style,
- kNameSpaceID_SVG)) {
- // XXX sucks that we have to do this -
- // see bug 370111
- nsCOMPtr<nsIStyleSheetLinkingElement> ssle =
- do_QueryInterface(element);
- NS_ASSERTION(ssle, "<html:style> doesn't implement "
- "nsIStyleSheetLinkingElement?");
- bool willNotify;
- bool isAlternate;
- ssle->UpdateStyleSheet(nullptr, &willNotify,
- &isAlternate);
- }
- }
- }
- // Now pop the context stack back up to the parent
- // element and continue the prototype walk.
- mContextStack.Pop();
- continue;
- }
- // Grab the next child, and advance the current context stack
- // to the next sibling to our right.
- nsXULPrototypeNode* childproto = proto->mChildren[indx];
- mContextStack.SetTopIndex(++indx);
- // Whether we're in the "first ply" of an overlay:
- // the "hookup" nodes. In the case !processingOverlayHookupNodes,
- // we're in the master document -or- we're in an overlay, and far
- // enough down into the overlay's content that we can simply build
- // the delegates and attach them to the parent node.
- bool processingOverlayHookupNodes = (mState == eState_Overlay) &&
- (mContextStack.Depth() == 1);
- NS_ASSERTION(element || processingOverlayHookupNodes,
- "no element on context stack");
- switch (childproto->mType) {
- case nsXULPrototypeNode::eType_Element: {
- // An 'element', which may contain more content.
- nsXULPrototypeElement* protoele =
- static_cast<nsXULPrototypeElement*>(childproto);
- RefPtr<Element> child;
- if (!processingOverlayHookupNodes) {
- rv = CreateElementFromPrototype(protoele,
- getter_AddRefs(child),
- false);
- if (NS_FAILED(rv)) return rv;
- // ...and append it to the content model.
- rv = element->AppendChildTo(child, false);
- if (NS_FAILED(rv)) return rv;
- // If we're only restoring persisted things on
- // some elements, store the ID here to do that.
- if (mRestrictPersistence) {
- nsIAtom* id = child->GetID();
- if (id) {
- mPersistenceIds.PutEntry(nsDependentAtomString(id));
- }
- }
- // do pre-order document-level hookup, but only if
- // we're in the master document. For an overlay,
- // this will happen when the overlay is
- // successfully resolved.
- if (mState == eState_Master)
- AddElementToDocumentPre(child);
- }
- else {
- // We're in the "first ply" of an overlay: the
- // "hookup" nodes. Create an 'overlay' element so
- // that we can continue to build content, and
- // enter a forward reference so we can hook it up
- // later.
- rv = CreateOverlayElement(protoele, getter_AddRefs(child));
- if (NS_FAILED(rv)) return rv;
- }
- // If it has children, push the element onto the context
- // stack and begin to process them.
- if (protoele->mChildren.Length() > 0) {
- rv = mContextStack.Push(protoele, child);
- if (NS_FAILED(rv)) return rv;
- }
- else {
- if (mState == eState_Master) {
- // If there are no children, and we're in the
- // master document, do post-order document hookup
- // immediately.
- AddElementToDocumentPost(child);
- }
- }
- }
- break;
- case nsXULPrototypeNode::eType_Script: {
- // A script reference. Execute the script immediately;
- // this may have side effects in the content model.
- nsXULPrototypeScript* scriptproto =
- static_cast<nsXULPrototypeScript*>(childproto);
- if (scriptproto->mSrcURI) {
- // A transcluded script reference; this may
- // "block" our prototype walk if the script isn't
- // cached, or the cached copy of the script is
- // stale and must be reloaded.
- bool blocked;
- rv = LoadScript(scriptproto, &blocked);
- // If the script cannot be loaded, just keep going!
- if (NS_SUCCEEDED(rv) && blocked)
- return NS_OK;
- }
- else if (scriptproto->HasScriptObject()) {
- // An inline script
- rv = ExecuteScript(scriptproto);
- if (NS_FAILED(rv)) return rv;
- }
- }
- break;
- case nsXULPrototypeNode::eType_Text: {
- // A simple text node.
- if (!processingOverlayHookupNodes) {
- // This does mean that text nodes that are direct children
- // of <overlay> get ignored.
- RefPtr<nsTextNode> text =
- new nsTextNode(mNodeInfoManager);
- nsXULPrototypeText* textproto =
- static_cast<nsXULPrototypeText*>(childproto);
- text->SetText(textproto->mValue, false);
- rv = element->AppendChildTo(text, false);
- NS_ENSURE_SUCCESS(rv, rv);
- }
- }
- break;
- case nsXULPrototypeNode::eType_PI: {
- nsXULPrototypePI* piProto =
- static_cast<nsXULPrototypePI*>(childproto);
- // <?xul-overlay?> and <?xml-stylesheet?> don't have effect
- // outside the prolog, like they used to. Issue a warning.
- if (piProto->mTarget.EqualsLiteral("xml-stylesheet") ||
- piProto->mTarget.EqualsLiteral("xul-overlay")) {
- const char16_t* params[] = { piProto->mTarget.get() };
- nsContentUtils::ReportToConsole(
- nsIScriptError::warningFlag,
- NS_LITERAL_CSTRING("XUL Document"), nullptr,
- nsContentUtils::eXUL_PROPERTIES,
- "PINotInProlog",
- params, ArrayLength(params),
- overlayURI);
- }
- nsIContent* parent = processingOverlayHookupNodes ?
- GetRootElement() : element.get();
- if (parent) {
- // an inline script could have removed the root element
- rv = CreateAndInsertPI(piProto, parent,
- parent->GetChildCount());
- NS_ENSURE_SUCCESS(rv, rv);
- }
- }
- break;
- default:
- NS_NOTREACHED("Unexpected nsXULPrototypeNode::Type value");
- }
- }
- // Once we get here, the context stack will have been
- // depleted. That means that the entire prototype has been
- // walked and content has been constructed.
- // If we're not already, mark us as now processing overlays.
- mState = eState_Overlay;
- // If there are no overlay URIs, then we're done.
- uint32_t count = mUnloadedOverlays.Length();
- if (! count)
- break;
- nsCOMPtr<nsIURI> uri = mUnloadedOverlays[count-1];
- mUnloadedOverlays.RemoveElementAt(count - 1);
- bool shouldReturn, failureFromContent;
- rv = LoadOverlayInternal(uri, false, &shouldReturn,
- &failureFromContent);
- if (failureFromContent)
- // The failure |rv| was the result of a problem in the content
- // rather than an unexpected problem in our implementation, so
- // just continue with the next overlay.
- continue;
- if (NS_FAILED(rv))
- return rv;
- if (mOverlayLoadObservers) {
- nsIObserver *obs = mOverlayLoadObservers->GetWeak(overlayURI);
- if (obs) {
- // This overlay has an unloaded overlay, so it will never
- // notify. The best we can do is to notify for the unloaded
- // overlay instead, assuming nobody is already notifiable
- // for it. Note that this will confuse the observer.
- if (!mOverlayLoadObservers->GetWeak(uri))
- mOverlayLoadObservers->Put(uri, obs);
- mOverlayLoadObservers->Remove(overlayURI);
- }
- }
- if (shouldReturn)
- return NS_OK;
- overlayURI.swap(uri);
- }
- // If we get here, there is nothing left for us to walk. The content
- // model is built and ready for layout.
- rv = ResolveForwardReferences();
- if (NS_FAILED(rv)) return rv;
- ApplyPersistentAttributes();
- mStillWalking = false;
- if (mPendingSheets == 0) {
- rv = DoneWalking();
- }
- return rv;
- }
- nsresult
- XULDocument::DoneWalking()
- {
- NS_PRECONDITION(mPendingSheets == 0, "there are sheets to be loaded");
- NS_PRECONDITION(!mStillWalking, "walk not done");
- // XXXldb This is where we should really be setting the chromehidden
- // attribute.
- {
- mozAutoDocUpdate updateBatch(this, UPDATE_STYLE, true);
- uint32_t count = mOverlaySheets.Length();
- for (uint32_t i = 0; i < count; ++i) {
- AddStyleSheet(mOverlaySheets[i]);
- }
- }
- mOverlaySheets.Clear();
- if (!mDocumentLoaded) {
- // Make sure we don't reenter here from StartLayout(). Note that
- // setting mDocumentLoaded to true here means that if StartLayout()
- // causes ResumeWalk() to be reentered, we'll take the other branch of
- // the |if (!mDocumentLoaded)| check above and since
- // mInitialLayoutComplete will be false will follow the else branch
- // there too. See the big comment there for how such reentry can
- // happen.
- mDocumentLoaded = true;
- NotifyPossibleTitleChange(false);
- // Before starting layout, check whether we're a toplevel chrome
- // window. If we are, set our chrome flags now, so that we don't have
- // to restyle the whole frame tree after StartLayout.
- nsCOMPtr<nsIDocShellTreeItem> item = GetDocShell();
- if (item) {
- nsCOMPtr<nsIDocShellTreeOwner> owner;
- item->GetTreeOwner(getter_AddRefs(owner));
- nsCOMPtr<nsIXULWindow> xulWin = do_GetInterface(owner);
- if (xulWin) {
- nsCOMPtr<nsIDocShell> xulWinShell;
- xulWin->GetDocShell(getter_AddRefs(xulWinShell));
- if (SameCOMIdentity(xulWinShell, item)) {
- // We're the chrome document! Apply our chrome flags now.
- xulWin->ApplyChromeFlags();
- }
- }
- }
- StartLayout();
- if (mIsWritingFastLoad && IsChromeURI(mDocumentURI))
- nsXULPrototypeCache::GetInstance()->WritePrototype(mMasterPrototype);
- NS_ASSERTION(mDelayFrameLoaderInitialization,
- "mDelayFrameLoaderInitialization should be true!");
- mDelayFrameLoaderInitialization = false;
- NS_WARNING_ASSERTION(
- mUpdateNestLevel == 0,
- "Constructing XUL document in middle of an update?");
- if (mUpdateNestLevel == 0) {
- MaybeInitializeFinalizeFrameLoaders();
- }
- NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad, (this));
- // DispatchContentLoadedEvents undoes the onload-blocking we
- // did in PrepareToWalk().
- DispatchContentLoadedEvents();
- mInitialLayoutComplete = true;
- // Walk the set of pending load notifications and notify any observers.
- // See below for detail.
- if (mPendingOverlayLoadNotifications) {
- nsInterfaceHashtable<nsURIHashKey,nsIObserver>* observers =
- mOverlayLoadObservers.get();
- for (auto iter = mPendingOverlayLoadNotifications->Iter();
- !iter.Done();
- iter.Next()) {
- nsIURI* aKey = iter.Key();
- iter.Data()->Observe(aKey, "xul-overlay-merged",
- EmptyString().get());
- if (observers) {
- observers->Remove(aKey);
- }
- iter.Remove();
- }
- }
- }
- else {
- if (mOverlayLoadObservers) {
- nsCOMPtr<nsIURI> overlayURI = mCurrentPrototype->GetURI();
- nsCOMPtr<nsIObserver> obs;
- if (mInitialLayoutComplete) {
- // We have completed initial layout, so just send the notification.
- mOverlayLoadObservers->Get(overlayURI, getter_AddRefs(obs));
- if (obs)
- obs->Observe(overlayURI, "xul-overlay-merged", EmptyString().get());
- mOverlayLoadObservers->Remove(overlayURI);
- }
- else {
- // If we have not yet displayed the document for the first time
- // (i.e. we came in here as the result of a dynamic overlay load
- // which was spawned by a binding-attached event caused by
- // StartLayout() on the master prototype - we must remember that
- // this overlay has been merged and tell the listeners after
- // StartLayout() is completely finished rather than doing so
- // immediately - otherwise we may be executing code that needs to
- // access XBL Binding implementations on nodes for which frames
- // have not yet been constructed because their bindings have not
- // yet been attached. This can be a race condition because dynamic
- // overlay loading can take varying amounts of time depending on
- // whether or not the overlay prototype is in the XUL cache. The
- // most likely effect of this bug is odd UI initialization due to
- // methods and properties that do not work.
- // XXXbz really, we shouldn't be firing binding constructors
- // until after StartLayout returns!
- if (!mPendingOverlayLoadNotifications) {
- mPendingOverlayLoadNotifications =
- new nsInterfaceHashtable<nsURIHashKey,nsIObserver>;
- }
-
- mPendingOverlayLoadNotifications->Get(overlayURI, getter_AddRefs(obs));
- if (!obs) {
- mOverlayLoadObservers->Get(overlayURI, getter_AddRefs(obs));
- NS_ASSERTION(obs, "null overlay load observer?");
- mPendingOverlayLoadNotifications->Put(overlayURI, obs);
- }
- }
- }
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- XULDocument::StyleSheetLoaded(StyleSheet* aSheet,
- bool aWasAlternate,
- nsresult aStatus)
- {
- if (!aWasAlternate) {
- // Don't care about when alternate sheets finish loading
- NS_ASSERTION(mPendingSheets > 0,
- "Unexpected StyleSheetLoaded notification");
- --mPendingSheets;
- if (!mStillWalking && mPendingSheets == 0) {
- return DoneWalking();
- }
- }
- return NS_OK;
- }
- void
- XULDocument::MaybeBroadcast()
- {
- // Only broadcast when not in an update and when safe to run scripts.
- if (mUpdateNestLevel == 0 &&
- (mDelayedAttrChangeBroadcasts.Length() ||
- mDelayedBroadcasters.Length())) {
- if (!nsContentUtils::IsSafeToRunScript()) {
- if (!mInDestructor) {
- nsContentUtils::AddScriptRunner(
- NewRunnableMethod(this, &XULDocument::MaybeBroadcast));
- }
- return;
- }
- if (!mHandlingDelayedAttrChange) {
- mHandlingDelayedAttrChange = true;
- for (uint32_t i = 0; i < mDelayedAttrChangeBroadcasts.Length(); ++i) {
- nsIAtom* attrName = mDelayedAttrChangeBroadcasts[i].mAttrName;
- if (mDelayedAttrChangeBroadcasts[i].mNeedsAttrChange) {
- nsCOMPtr<nsIContent> listener =
- do_QueryInterface(mDelayedAttrChangeBroadcasts[i].mListener);
- const nsString& value = mDelayedAttrChangeBroadcasts[i].mAttr;
- if (mDelayedAttrChangeBroadcasts[i].mSetAttr) {
- listener->SetAttr(kNameSpaceID_None, attrName, value,
- true);
- } else {
- listener->UnsetAttr(kNameSpaceID_None, attrName,
- true);
- }
- }
- ExecuteOnBroadcastHandlerFor(mDelayedAttrChangeBroadcasts[i].mBroadcaster,
- mDelayedAttrChangeBroadcasts[i].mListener,
- attrName);
- }
- mDelayedAttrChangeBroadcasts.Clear();
- mHandlingDelayedAttrChange = false;
- }
- uint32_t length = mDelayedBroadcasters.Length();
- if (length) {
- bool oldValue = mHandlingDelayedBroadcasters;
- mHandlingDelayedBroadcasters = true;
- nsTArray<nsDelayedBroadcastUpdate> delayedBroadcasters;
- mDelayedBroadcasters.SwapElements(delayedBroadcasters);
- for (uint32_t i = 0; i < length; ++i) {
- SynchronizeBroadcastListener(delayedBroadcasters[i].mBroadcaster,
- delayedBroadcasters[i].mListener,
- delayedBroadcasters[i].mAttr);
- }
- mHandlingDelayedBroadcasters = oldValue;
- }
- }
- }
- void
- XULDocument::EndUpdate(nsUpdateType aUpdateType)
- {
- XMLDocument::EndUpdate(aUpdateType);
- MaybeBroadcast();
- }
- void
- XULDocument::ReportMissingOverlay(nsIURI* aURI)
- {
- NS_PRECONDITION(aURI, "Must have a URI");
- NS_ConvertUTF8toUTF16 utfSpec(aURI->GetSpecOrDefault());
- const char16_t* params[] = { utfSpec.get() };
- nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
- NS_LITERAL_CSTRING("XUL Document"), this,
- nsContentUtils::eXUL_PROPERTIES,
- "MissingOverlay",
- params, ArrayLength(params));
- }
- nsresult
- XULDocument::LoadScript(nsXULPrototypeScript* aScriptProto, bool* aBlock)
- {
- // Load a transcluded script
- nsresult rv;
- bool isChromeDoc = IsChromeURI(mDocumentURI);
- if (isChromeDoc && aScriptProto->HasScriptObject()) {
- rv = ExecuteScript(aScriptProto);
- // Ignore return value from execution, and don't block
- *aBlock = false;
- return NS_OK;
- }
- // Try the XUL script cache, in case two XUL documents source the same
- // .js file (e.g., strres.js from navigator.xul and utilityOverlay.xul).
- // XXXbe the cache relies on aScriptProto's GC root!
- bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
- if (isChromeDoc && useXULCache) {
- JSScript* newScriptObject =
- nsXULPrototypeCache::GetInstance()->GetScript(
- aScriptProto->mSrcURI);
- if (newScriptObject) {
- // The script language for a proto must remain constant - we
- // can't just change it for this unexpected language.
- aScriptProto->Set(newScriptObject);
- }
- if (aScriptProto->HasScriptObject()) {
- rv = ExecuteScript(aScriptProto);
- // Ignore return value from execution, and don't block
- *aBlock = false;
- return NS_OK;
- }
- }
- // Release script objects from FastLoad since we decided against using them
- aScriptProto->UnlinkJSObjects();
- // Set the current script prototype so that OnStreamComplete can report
- // the right file if there are errors in the script.
- NS_ASSERTION(!mCurrentScriptProto,
- "still loading a script when starting another load?");
- mCurrentScriptProto = aScriptProto;
- if (isChromeDoc && aScriptProto->mSrcLoading) {
- // Another XULDocument load has started, which is still in progress.
- // Remember to ResumeWalk this document when the load completes.
- mNextSrcLoadWaiter = aScriptProto->mSrcLoadWaiters;
- aScriptProto->mSrcLoadWaiters = this;
- NS_ADDREF_THIS();
- }
- else {
- nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
- // Note: the loader will keep itself alive while it's loading.
- nsCOMPtr<nsIStreamLoader> loader;
- rv = NS_NewStreamLoader(getter_AddRefs(loader),
- aScriptProto->mSrcURI,
- this, // aObserver
- this, // aRequestingContext
- nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS,
- nsIContentPolicy::TYPE_INTERNAL_SCRIPT,
- group);
- if (NS_FAILED(rv)) {
- mCurrentScriptProto = nullptr;
- return rv;
- }
- aScriptProto->mSrcLoading = true;
- }
- // Block until OnStreamComplete resumes us.
- *aBlock = true;
- return NS_OK;
- }
- NS_IMETHODIMP
- XULDocument::OnStreamComplete(nsIStreamLoader* aLoader,
- nsISupports* context,
- nsresult aStatus,
- uint32_t stringLen,
- const uint8_t* string)
- {
- nsCOMPtr<nsIRequest> request;
- aLoader->GetRequest(getter_AddRefs(request));
- nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
- #ifdef DEBUG
- // print a load error on bad status
- if (NS_FAILED(aStatus)) {
- if (channel) {
- nsCOMPtr<nsIURI> uri;
- channel->GetURI(getter_AddRefs(uri));
- if (uri) {
- printf("Failed to load %s\n", uri->GetSpecOrDefault().get());
- }
- }
- }
- #endif
- // This is the completion routine that will be called when a
- // transcluded script completes. Compile and execute the script
- // if the load was successful, then continue building content
- // from the prototype.
- nsresult rv = aStatus;
- NS_ASSERTION(mCurrentScriptProto && mCurrentScriptProto->mSrcLoading,
- "script source not loading on unichar stream complete?");
- if (!mCurrentScriptProto) {
- // XXX Wallpaper for bug 270042
- return NS_OK;
- }
- if (NS_SUCCEEDED(aStatus)) {
- // If the including XUL document is a FastLoad document, and we're
- // compiling an out-of-line script (one with src=...), then we must
- // be writing a new FastLoad file. If we were reading this script
- // from the FastLoad file, XULContentSinkImpl::OpenScript (over in
- // nsXULContentSink.cpp) would have already deserialized a non-null
- // script->mScriptObject, causing control flow at the top of LoadScript
- // not to reach here.
- nsCOMPtr<nsIURI> uri = mCurrentScriptProto->mSrcURI;
- // XXX should also check nsIHttpChannel::requestSucceeded
- MOZ_ASSERT(!mOffThreadCompiling && (mOffThreadCompileStringLength == 0 &&
- !mOffThreadCompileStringBuf),
- "XULDocument can't load multiple scripts at once");
- rv = ScriptLoader::ConvertToUTF16(channel, string, stringLen,
- EmptyString(), this,
- mOffThreadCompileStringBuf,
- mOffThreadCompileStringLength);
- if (NS_SUCCEEDED(rv)) {
- // Attempt to give ownership of the buffer to the JS engine. If
- // we hit offthread compilation, however, we will have to take it
- // back below in order to keep the memory alive until compilation
- // completes.
- JS::SourceBufferHolder srcBuf(mOffThreadCompileStringBuf,
- mOffThreadCompileStringLength,
- JS::SourceBufferHolder::GiveOwnership);
- mOffThreadCompileStringBuf = nullptr;
- mOffThreadCompileStringLength = 0;
- rv = mCurrentScriptProto->Compile(srcBuf, uri, 1, this, this);
- if (NS_SUCCEEDED(rv) && !mCurrentScriptProto->HasScriptObject()) {
- // We will be notified via OnOffThreadCompileComplete when the
- // compile finishes. Keep the contents of the compiled script
- // alive until the compilation finishes.
- mOffThreadCompiling = true;
- // If the JS engine did not take the source buffer, then take
- // it back here to ensure it remains alive.
- mOffThreadCompileStringBuf = srcBuf.take();
- if (mOffThreadCompileStringBuf) {
- mOffThreadCompileStringLength = srcBuf.length();
- }
- BlockOnload();
- return NS_OK;
- }
- }
- }
- return OnScriptCompileComplete(mCurrentScriptProto->GetScriptObject(), rv);
- }
- NS_IMETHODIMP
- XULDocument::OnScriptCompileComplete(JSScript* aScript, nsresult aStatus)
- {
- // When compiling off thread the script will not have been attached to the
- // script proto yet.
- if (aScript && !mCurrentScriptProto->HasScriptObject())
- mCurrentScriptProto->Set(aScript);
- // Allow load events to be fired once off thread compilation finishes.
- if (mOffThreadCompiling) {
- mOffThreadCompiling = false;
- UnblockOnload(false);
- }
- // After compilation finishes the script's characters are no longer needed.
- if (mOffThreadCompileStringBuf) {
- js_free(mOffThreadCompileStringBuf);
- mOffThreadCompileStringBuf = nullptr;
- mOffThreadCompileStringLength = 0;
- }
- // Clear mCurrentScriptProto now, but save it first for use below in
- // the execute code, and in the while loop that resumes walks of other
- // documents that raced to load this script.
- nsXULPrototypeScript* scriptProto = mCurrentScriptProto;
- mCurrentScriptProto = nullptr;
- // Clear the prototype's loading flag before executing the script or
- // resuming document walks, in case any of those control flows starts a
- // new script load.
- scriptProto->mSrcLoading = false;
- nsresult rv = aStatus;
- if (NS_SUCCEEDED(rv)) {
- rv = ExecuteScript(scriptProto);
- // If the XUL cache is enabled, save the script object there in
- // case different XUL documents source the same script.
- //
- // But don't save the script in the cache unless the master XUL
- // document URL is a chrome: URL. It is valid for a URL such as
- // about:config to translate into a master document URL, whose
- // prototype document nodes -- including prototype scripts that
- // hold GC roots protecting their mJSObject pointers -- are not
- // cached in the XUL prototype cache. See StartDocumentLoad,
- // the fillXULCache logic.
- //
- // A document such as about:config is free to load a script via
- // a URL such as chrome://global/content/config.js, and we must
- // not cache that script object without a prototype cache entry
- // containing a companion nsXULPrototypeScript node that owns a
- // GC root protecting the script object. Otherwise, the script
- // cache entry will dangle once the uncached prototype document
- // is released when its owning XULDocument is unloaded.
- //
- // (See http://bugzilla.mozilla.org/show_bug.cgi?id=98207 for
- // the true crime story.)
- bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
- if (useXULCache && IsChromeURI(mDocumentURI) && scriptProto->HasScriptObject()) {
- JS::Rooted<JSScript*> script(RootingCx(), scriptProto->GetScriptObject());
- nsXULPrototypeCache::GetInstance()->PutScript(
- scriptProto->mSrcURI, script);
- }
- if (mIsWritingFastLoad && mCurrentPrototype != mMasterPrototype) {
- // If we are loading an overlay script, try to serialize
- // it to the FastLoad file here. Master scripts will be
- // serialized when the master prototype document gets
- // written, at the bottom of ResumeWalk. That way, master
- // out-of-line scripts are serialized in the same order that
- // they'll be read, in the FastLoad file, which reduces the
- // number of seeks that dump the underlying stream's buffer.
- //
- // Ignore the return value, as we don't need to propagate
- // a failure to write to the FastLoad file, because this
- // method aborts that whole process on error.
- scriptProto->SerializeOutOfLine(nullptr, mCurrentPrototype);
- }
- // ignore any evaluation errors
- }
- rv = ResumeWalk();
- // Load a pointer to the prototype-script's list of XULDocuments who
- // raced to load the same script
- XULDocument** docp = &scriptProto->mSrcLoadWaiters;
- // Resume walking other documents that waited for this one's load, first
- // executing the script we just compiled, in each doc's script context
- XULDocument* doc;
- while ((doc = *docp) != nullptr) {
- NS_ASSERTION(doc->mCurrentScriptProto == scriptProto,
- "waiting for wrong script to load?");
- doc->mCurrentScriptProto = nullptr;
- // Unlink doc from scriptProto's list before executing and resuming
- *docp = doc->mNextSrcLoadWaiter;
- doc->mNextSrcLoadWaiter = nullptr;
- // Execute only if we loaded and compiled successfully, then resume
- if (NS_SUCCEEDED(aStatus) && scriptProto->HasScriptObject()) {
- doc->ExecuteScript(scriptProto);
- }
- doc->ResumeWalk();
- NS_RELEASE(doc);
- }
- return rv;
- }
- nsresult
- XULDocument::ExecuteScript(nsXULPrototypeScript *aScript)
- {
- NS_PRECONDITION(aScript != nullptr, "null ptr");
- NS_ENSURE_TRUE(aScript, NS_ERROR_NULL_POINTER);
- NS_ENSURE_TRUE(mScriptGlobalObject, NS_ERROR_NOT_INITIALIZED);
- nsresult rv;
- rv = mScriptGlobalObject->EnsureScriptEnvironment();
- NS_ENSURE_SUCCESS(rv, rv);
- // Execute the precompiled script with the given version
- nsAutoMicroTask mt;
- // We're about to run script via JS::CloneAndExecuteScript, so we need an
- // AutoEntryScript. This is Gecko specific and not in any spec.
- AutoEntryScript aes(mScriptGlobalObject, "precompiled XUL <script> element");
- JSContext* cx = aes.cx();
- JS::Rooted<JSScript*> scriptObject(cx, aScript->GetScriptObject());
- NS_ENSURE_TRUE(scriptObject, NS_ERROR_UNEXPECTED);
- JS::Rooted<JSObject*> baseGlobal(cx, JS::CurrentGlobalOrNull(cx));
- NS_ENSURE_TRUE(xpc::Scriptability::Get(baseGlobal).Allowed(), NS_OK);
- JSAddonId* addonId = mCurrentPrototype ? MapURIToAddonID(mCurrentPrototype->GetURI()) : nullptr;
- JS::Rooted<JSObject*> global(cx, xpc::GetAddonScope(cx, baseGlobal, addonId));
- NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
- JS::ExposeObjectToActiveJS(global);
- JSAutoCompartment ac(cx, global);
- // The script is in the compilation scope. Clone it into the target scope
- // and execute it. On failure, ~AutoScriptEntry will handle exceptions, so
- // there is no need to manually check the return value.
- JS::RootedValue rval(cx);
- JS::CloneAndExecuteScript(cx, scriptObject, &rval);
- return NS_OK;
- }
- nsresult
- XULDocument::CreateElementFromPrototype(nsXULPrototypeElement* aPrototype,
- Element** aResult,
- bool aIsRoot)
- {
- // Create a content model element from a prototype element.
- NS_PRECONDITION(aPrototype != nullptr, "null ptr");
- if (! aPrototype)
- return NS_ERROR_NULL_POINTER;
- *aResult = nullptr;
- nsresult rv = NS_OK;
- if (MOZ_LOG_TEST(gXULLog, LogLevel::Debug)) {
- MOZ_LOG(gXULLog, LogLevel::Debug,
- ("xul: creating <%s> from prototype",
- NS_ConvertUTF16toUTF8(aPrototype->mNodeInfo->QualifiedName()).get()));
- }
- RefPtr<Element> result;
- if (aPrototype->mNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) {
- // If it's a XUL element, it'll be lightweight until somebody
- // monkeys with it.
- rv = nsXULElement::Create(aPrototype, this, true, aIsRoot, getter_AddRefs(result));
- if (NS_FAILED(rv)) return rv;
- }
- else {
- // If it's not a XUL element, it's gonna be heavyweight no matter
- // what. So we need to copy everything out of the prototype
- // into the element. Get a nodeinfo from our nodeinfo manager
- // for this node.
- RefPtr<mozilla::dom::NodeInfo> newNodeInfo;
- newNodeInfo = mNodeInfoManager->GetNodeInfo(aPrototype->mNodeInfo->NameAtom(),
- aPrototype->mNodeInfo->GetPrefixAtom(),
- aPrototype->mNodeInfo->NamespaceID(),
- nsIDOMNode::ELEMENT_NODE);
- if (!newNodeInfo) return NS_ERROR_OUT_OF_MEMORY;
- RefPtr<mozilla::dom::NodeInfo> xtfNi = newNodeInfo;
- rv = NS_NewElement(getter_AddRefs(result), newNodeInfo.forget(),
- NOT_FROM_PARSER);
- if (NS_FAILED(rv))
- return rv;
- rv = AddAttributes(aPrototype, result);
- if (NS_FAILED(rv)) return rv;
- }
- result.forget(aResult);
- return NS_OK;
- }
- nsresult
- XULDocument::CreateOverlayElement(nsXULPrototypeElement* aPrototype,
- Element** aResult)
- {
- nsresult rv;
- RefPtr<Element> element;
- rv = CreateElementFromPrototype(aPrototype, getter_AddRefs(element), false);
- if (NS_FAILED(rv)) return rv;
- OverlayForwardReference* fwdref =
- new OverlayForwardReference(this, element);
- // transferring ownership to ya...
- rv = AddForwardReference(fwdref);
- if (NS_FAILED(rv)) return rv;
- element.forget(aResult);
- return NS_OK;
- }
- nsresult
- XULDocument::AddAttributes(nsXULPrototypeElement* aPrototype,
- nsIContent* aElement)
- {
- nsresult rv;
- for (uint32_t i = 0; i < aPrototype->mNumAttributes; ++i) {
- nsXULPrototypeAttribute* protoattr = &(aPrototype->mAttributes[i]);
- nsAutoString valueStr;
- protoattr->mValue.ToString(valueStr);
- rv = aElement->SetAttr(protoattr->mName.NamespaceID(),
- protoattr->mName.LocalName(),
- protoattr->mName.GetPrefix(),
- valueStr,
- false);
- if (NS_FAILED(rv)) return rv;
- }
- return NS_OK;
- }
- nsresult
- XULDocument::CheckTemplateBuilderHookup(nsIContent* aElement,
- bool* aNeedsHookup)
- {
- // See if the element already has a `database' attribute. If it
- // does, then the template builder has already been created.
- //
- // XXX This approach will crash and burn (well, maybe not _that_
- // bad) if aElement is not a XUL element.
- //
- // XXXvarga Do we still want to support non XUL content?
- nsCOMPtr<nsIDOMXULElement> xulElement = do_QueryInterface(aElement);
- if (xulElement) {
- nsCOMPtr<nsIRDFCompositeDataSource> ds;
- xulElement->GetDatabase(getter_AddRefs(ds));
- if (ds) {
- *aNeedsHookup = false;
- return NS_OK;
- }
- }
- // Check aElement for a 'datasources' attribute, if it has
- // one a XUL template builder needs to be hooked up.
- *aNeedsHookup = aElement->HasAttr(kNameSpaceID_None,
- nsGkAtoms::datasources);
- return NS_OK;
- }
- /* static */ nsresult
- XULDocument::CreateTemplateBuilder(nsIContent* aElement)
- {
- // Check if need to construct a tree builder or content builder.
- bool isTreeBuilder = false;
- // return successful if the element is not is a document, as an inline
- // script could have removed it
- nsIDocument* document = aElement->GetUncomposedDoc();
- NS_ENSURE_TRUE(document, NS_OK);
- int32_t nameSpaceID;
- nsIAtom* baseTag = document->BindingManager()->
- ResolveTag(aElement, &nameSpaceID);
- if ((nameSpaceID == kNameSpaceID_XUL) && (baseTag == nsGkAtoms::tree)) {
- // By default, we build content for a tree and then we attach
- // the tree content view. However, if the `dont-build-content'
- // flag is set, then we we'll attach a tree builder which
- // directly implements the tree view.
- nsAutoString flags;
- aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::flags, flags);
- if (flags.Find(NS_LITERAL_STRING("dont-build-content")) >= 0) {
- isTreeBuilder = true;
- }
- }
- if (isTreeBuilder) {
- // Create and initialize a tree builder.
- nsCOMPtr<nsIXULTemplateBuilder> builder =
- do_CreateInstance("@mozilla.org/xul/xul-tree-builder;1");
- if (! builder)
- return NS_ERROR_FAILURE;
- builder->Init(aElement);
- // Create a <treechildren> if one isn't there already.
- // XXXvarga what about attributes?
- nsCOMPtr<nsIContent> bodyContent;
- nsXULContentUtils::FindChildByTag(aElement, kNameSpaceID_XUL,
- nsGkAtoms::treechildren,
- getter_AddRefs(bodyContent));
- if (! bodyContent) {
- bodyContent =
- document->CreateElem(nsDependentAtomString(nsGkAtoms::treechildren),
- nullptr, kNameSpaceID_XUL);
- aElement->AppendChildTo(bodyContent, false);
- }
- }
- else {
- // Create and initialize a content builder.
- nsCOMPtr<nsIXULTemplateBuilder> builder
- = do_CreateInstance("@mozilla.org/xul/xul-template-builder;1");
- if (! builder)
- return NS_ERROR_FAILURE;
- builder->Init(aElement);
- builder->CreateContents(aElement, false);
- }
- return NS_OK;
- }
- nsresult
- XULDocument::AddPrototypeSheets()
- {
- nsresult rv;
- const nsCOMArray<nsIURI>& sheets = mCurrentPrototype->GetStyleSheetReferences();
- for (int32_t i = 0; i < sheets.Count(); i++) {
- nsCOMPtr<nsIURI> uri = sheets[i];
- RefPtr<StyleSheet> incompleteSheet;
- rv = CSSLoader()->LoadSheet(uri,
- mCurrentPrototype->DocumentPrincipal(),
- EmptyCString(), this,
- &incompleteSheet);
- // XXXldb We need to prevent bogus sheets from being held in the
- // prototype's list, but until then, don't propagate the failure
- // from LoadSheet (and thus exit the loop).
- if (NS_SUCCEEDED(rv)) {
- ++mPendingSheets;
- if (!mOverlaySheets.AppendElement(incompleteSheet)) {
- return NS_ERROR_OUT_OF_MEMORY;
- }
- }
- }
- return NS_OK;
- }
- //----------------------------------------------------------------------
- //
- // XULDocument::OverlayForwardReference
- //
- nsForwardReference::Result
- XULDocument::OverlayForwardReference::Resolve()
- {
- // Resolve a forward reference from an overlay element; attempt to
- // hook it up into the main document.
- nsresult rv;
- nsCOMPtr<nsIContent> target;
- nsIPresShell *shell = mDocument->GetShell();
- bool notify = shell && shell->DidInitialize();
- nsAutoString id;
- mOverlay->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id);
- if (id.IsEmpty()) {
- // mOverlay is a direct child of <overlay> and has no id.
- // Insert it under the root element in the base document.
- Element* root = mDocument->GetRootElement();
- if (!root) {
- return eResolve_Error;
- }
- rv = mDocument->InsertElement(root, mOverlay, notify);
- if (NS_FAILED(rv)) return eResolve_Error;
- target = mOverlay;
- }
- else {
- // The hook-up element has an id, try to match it with an element
- // with the same id in the base document.
- target = mDocument->GetElementById(id);
- // If we can't find the element in the document, defer the hookup
- // until later.
- if (!target)
- return eResolve_Later;
- rv = Merge(target, mOverlay, notify);
- if (NS_FAILED(rv)) return eResolve_Error;
- }
- // Check if 'target' is still in our document --- it might not be!
- if (!notify && target->GetUncomposedDoc() == mDocument) {
- // Add child and any descendants to the element map
- // XXX this is bogus, the content in 'target' might already be
- // in the document
- rv = mDocument->AddSubtreeToDocument(target);
- if (NS_FAILED(rv)) return eResolve_Error;
- }
- if (MOZ_LOG_TEST(gXULLog, LogLevel::Debug)) {
- nsAutoCString idC;
- idC.AssignWithConversion(id);
- MOZ_LOG(gXULLog, LogLevel::Debug,
- ("xul: overlay resolved '%s'",
- idC.get()));
- }
- mResolved = true;
- return eResolve_Succeeded;
- }
- nsresult
- XULDocument::OverlayForwardReference::Merge(nsIContent* aTargetNode,
- nsIContent* aOverlayNode,
- bool aNotify)
- {
- // This function is given:
- // aTargetNode: the node in the document whose 'id' attribute
- // matches a toplevel node in our overlay.
- // aOverlayNode: the node in the overlay document that matches
- // a node in the actual document.
- // aNotify: whether or not content manipulation methods should
- // use the aNotify parameter. After the initial
- // reflow (i.e. in the dynamic overlay merge case),
- // we want all the content manipulation methods we
- // call to notify so that frames are constructed
- // etc. Otherwise do not, since that's during initial
- // document construction before StartLayout has been
- // called which will do everything for us.
- //
- // This function merges the tree from the overlay into the tree in
- // the document, overwriting attributes and appending child content
- // nodes appropriately. (See XUL overlay reference for details)
- nsresult rv;
- // Merge attributes from the overlay content node to that of the
- // actual document.
- uint32_t i;
- const nsAttrName* name;
- for (i = 0; (name = aOverlayNode->GetAttrNameAt(i)); ++i) {
- // We don't want to swap IDs, they should be the same.
- if (name->Equals(nsGkAtoms::id))
- continue;
- // In certain cases merging command or observes is unsafe, so don't.
- if (!aNotify) {
- if (aTargetNode->NodeInfo()->Equals(nsGkAtoms::observes,
- kNameSpaceID_XUL))
- continue;
- if (name->Equals(nsGkAtoms::observes) &&
- aTargetNode->HasAttr(kNameSpaceID_None, nsGkAtoms::observes))
- continue;
- if (name->Equals(nsGkAtoms::command) &&
- aTargetNode->HasAttr(kNameSpaceID_None, nsGkAtoms::command) &&
- !aTargetNode->NodeInfo()->Equals(nsGkAtoms::key,
- kNameSpaceID_XUL) &&
- !aTargetNode->NodeInfo()->Equals(nsGkAtoms::menuitem,
- kNameSpaceID_XUL))
- continue;
- }
- int32_t nameSpaceID = name->NamespaceID();
- nsIAtom* attr = name->LocalName();
- nsIAtom* prefix = name->GetPrefix();
- nsAutoString value;
- aOverlayNode->GetAttr(nameSpaceID, attr, value);
- // Element in the overlay has the 'removeelement' attribute set
- // so remove it from the actual document.
- if (attr == nsGkAtoms::removeelement &&
- value.EqualsLiteral("true")) {
- nsCOMPtr<nsINode> parent = aTargetNode->GetParentNode();
- if (!parent) return NS_ERROR_FAILURE;
- rv = RemoveElement(parent, aTargetNode);
- if (NS_FAILED(rv)) return rv;
- return NS_OK;
- }
- rv = aTargetNode->SetAttr(nameSpaceID, attr, prefix, value, aNotify);
- if (!NS_FAILED(rv) && !aNotify)
- rv = mDocument->BroadcastAttributeChangeFromOverlay(aTargetNode,
- nameSpaceID,
- attr, prefix,
- value);
- if (NS_FAILED(rv)) return rv;
- }
- // Walk our child nodes, looking for elements that have the 'id'
- // attribute set. If we find any, we must do a parent check in the
- // actual document to ensure that the structure matches that of
- // the actual document. If it does, we can call ourselves and attempt
- // to merge inside that subtree. If not, we just append the tree to
- // the parent like any other.
- uint32_t childCount = aOverlayNode->GetChildCount();
- // This must be a strong reference since it will be the only
- // reference to a content object during part of this loop.
- nsCOMPtr<nsIContent> currContent;
- for (i = 0; i < childCount; ++i) {
- currContent = aOverlayNode->GetFirstChild();
- nsIAtom *idAtom = currContent->GetID();
- nsIContent *elementInDocument = nullptr;
- if (idAtom) {
- nsDependentAtomString id(idAtom);
- if (!id.IsEmpty()) {
- nsIDocument *doc = aTargetNode->GetUncomposedDoc();
- //XXXsmaug should we use ShadowRoot::GetElementById()
- // if doc is null?
- if (!doc) return NS_ERROR_FAILURE;
- elementInDocument = doc->GetElementById(id);
- }
- }
- // The item has an 'id' attribute set, and we need to check with
- // the actual document to see if an item with this id exists at
- // this locale. If so, we want to merge the subtree under that
- // node. Otherwise, we just do an append as if the element had
- // no id attribute.
- if (elementInDocument) {
- // Given two parents, aTargetNode and aOverlayNode, we want
- // to call merge on currContent if we find an associated
- // node in the document with the same id as currContent that
- // also has aTargetNode as its parent.
- nsIContent *elementParent = elementInDocument->GetParent();
- nsIAtom *parentID = elementParent->GetID();
- if (parentID &&
- aTargetNode->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id,
- nsDependentAtomString(parentID),
- eCaseMatters)) {
- // The element matches. "Go Deep!"
- rv = Merge(elementInDocument, currContent, aNotify);
- if (NS_FAILED(rv)) return rv;
- aOverlayNode->RemoveChildAt(0, false);
- continue;
- }
- }
- aOverlayNode->RemoveChildAt(0, false);
- rv = InsertElement(aTargetNode, currContent, aNotify);
- if (NS_FAILED(rv)) return rv;
- }
- return NS_OK;
- }
- XULDocument::OverlayForwardReference::~OverlayForwardReference()
- {
- if (MOZ_LOG_TEST(gXULLog, LogLevel::Warning) && !mResolved) {
- nsAutoString id;
- mOverlay->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id);
- nsAutoCString idC;
- idC.AssignWithConversion(id);
- nsIURI *protoURI = mDocument->mCurrentPrototype->GetURI();
- nsCOMPtr<nsIURI> docURI;
- mDocument->mChannel->GetOriginalURI(getter_AddRefs(docURI));
- MOZ_LOG(gXULLog, LogLevel::Warning,
- ("xul: %s overlay failed to resolve '%s' in %s",
- protoURI->GetSpecOrDefault().get(), idC.get(),
- docURI ? docURI->GetSpecOrDefault().get() : ""));
- }
- }
- //----------------------------------------------------------------------
- //
- // XULDocument::BroadcasterHookup
- //
- nsForwardReference::Result
- XULDocument::BroadcasterHookup::Resolve()
- {
- nsresult rv;
- bool listener;
- rv = mDocument->CheckBroadcasterHookup(mObservesElement, &listener, &mResolved);
- if (NS_FAILED(rv)) return eResolve_Error;
- return mResolved ? eResolve_Succeeded : eResolve_Later;
- }
- XULDocument::BroadcasterHookup::~BroadcasterHookup()
- {
- if (MOZ_LOG_TEST(gXULLog, LogLevel::Warning) && !mResolved) {
- // Tell the world we failed
- nsAutoString broadcasterID;
- nsAutoString attribute;
- if (mObservesElement->IsXULElement(nsGkAtoms::observes)) {
- mObservesElement->GetAttr(kNameSpaceID_None, nsGkAtoms::element, broadcasterID);
- mObservesElement->GetAttr(kNameSpaceID_None, nsGkAtoms::attribute, attribute);
- }
- else {
- mObservesElement->GetAttr(kNameSpaceID_None, nsGkAtoms::observes, broadcasterID);
- attribute.Assign('*');
- }
- nsAutoCString attributeC,broadcasteridC;
- attributeC.AssignWithConversion(attribute);
- broadcasteridC.AssignWithConversion(broadcasterID);
- MOZ_LOG(gXULLog, LogLevel::Warning,
- ("xul: broadcaster hookup failed <%s attribute='%s'> to %s",
- nsAtomCString(mObservesElement->NodeInfo()->NameAtom()).get(),
- attributeC.get(),
- broadcasteridC.get()));
- }
- }
- //----------------------------------------------------------------------
- //
- // XULDocument::TemplateBuilderHookup
- //
- nsForwardReference::Result
- XULDocument::TemplateBuilderHookup::Resolve()
- {
- bool needsHookup;
- nsresult rv = CheckTemplateBuilderHookup(mElement, &needsHookup);
- if (NS_FAILED(rv))
- return eResolve_Error;
- if (needsHookup) {
- rv = CreateTemplateBuilder(mElement);
- if (NS_FAILED(rv))
- return eResolve_Error;
- }
- return eResolve_Succeeded;
- }
- //----------------------------------------------------------------------
- nsresult
- XULDocument::BroadcastAttributeChangeFromOverlay(nsIContent* aNode,
- int32_t aNameSpaceID,
- nsIAtom* aAttribute,
- nsIAtom* aPrefix,
- const nsAString& aValue)
- {
- nsresult rv = NS_OK;
- if (!mBroadcasterMap || !CanBroadcast(aNameSpaceID, aAttribute))
- return rv;
- if (!aNode->IsElement())
- return rv;
- auto entry = static_cast<BroadcasterMapEntry*>
- (mBroadcasterMap->Search(aNode->AsElement()));
- if (!entry)
- return rv;
- // We've got listeners: push the value.
- for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
- BroadcastListener* bl = entry->mListeners[i];
- if ((bl->mAttribute != aAttribute) &&
- (bl->mAttribute != nsGkAtoms::_asterisk))
- continue;
- nsCOMPtr<nsIContent> l = do_QueryReferent(bl->mListener);
- if (l) {
- rv = l->SetAttr(aNameSpaceID, aAttribute,
- aPrefix, aValue, false);
- if (NS_FAILED(rv)) return rv;
- }
- }
- return rv;
- }
- nsresult
- XULDocument::FindBroadcaster(Element* aElement,
- Element** aListener,
- nsString& aBroadcasterID,
- nsString& aAttribute,
- Element** aBroadcaster)
- {
- mozilla::dom::NodeInfo *ni = aElement->NodeInfo();
- *aListener = nullptr;
- *aBroadcaster = nullptr;
- if (ni->Equals(nsGkAtoms::observes, kNameSpaceID_XUL)) {
- // It's an <observes> element, which means that the actual
- // listener is the _parent_ node. This element should have an
- // 'element' attribute that specifies the ID of the
- // broadcaster element, and an 'attribute' element, which
- // specifies the name of the attribute to observe.
- nsIContent* parent = aElement->GetParent();
- if (!parent) {
- // <observes> is the root element
- return NS_FINDBROADCASTER_NOT_FOUND;
- }
- // If we're still parented by an 'overlay' tag, then we haven't
- // made it into the real document yet. Defer hookup.
- if (parent->NodeInfo()->Equals(nsGkAtoms::overlay,
- kNameSpaceID_XUL)) {
- return NS_FINDBROADCASTER_AWAIT_OVERLAYS;
- }
- *aListener = parent->IsElement() ? parent->AsElement() : nullptr;
- NS_IF_ADDREF(*aListener);
- aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::element, aBroadcasterID);
- if (aBroadcasterID.IsEmpty()) {
- return NS_FINDBROADCASTER_NOT_FOUND;
- }
- aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::attribute, aAttribute);
- }
- else {
- // It's a generic element, which means that we'll use the
- // value of the 'observes' attribute to determine the ID of
- // the broadcaster element, and we'll watch _all_ of its
- // values.
- aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::observes, aBroadcasterID);
- // Bail if there's no aBroadcasterID
- if (aBroadcasterID.IsEmpty()) {
- // Try the command attribute next.
- aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::command, aBroadcasterID);
- if (!aBroadcasterID.IsEmpty()) {
- // We've got something in the command attribute. We
- // only treat this as a normal broadcaster if we are
- // not a menuitem or a key.
- if (ni->Equals(nsGkAtoms::menuitem, kNameSpaceID_XUL) ||
- ni->Equals(nsGkAtoms::key, kNameSpaceID_XUL)) {
- return NS_FINDBROADCASTER_NOT_FOUND;
- }
- }
- else {
- return NS_FINDBROADCASTER_NOT_FOUND;
- }
- }
- *aListener = aElement;
- NS_ADDREF(*aListener);
- aAttribute.Assign('*');
- }
- // Make sure we got a valid listener.
- NS_ENSURE_TRUE(*aListener, NS_ERROR_UNEXPECTED);
- // Try to find the broadcaster element in the document.
- *aBroadcaster = GetElementById(aBroadcasterID);
- // If we can't find the broadcaster, then we'll need to defer the
- // hookup. We may need to resolve some of the other overlays
- // first.
- if (! *aBroadcaster) {
- return NS_FINDBROADCASTER_AWAIT_OVERLAYS;
- }
- NS_ADDREF(*aBroadcaster);
- return NS_FINDBROADCASTER_FOUND;
- }
- nsresult
- XULDocument::CheckBroadcasterHookup(Element* aElement,
- bool* aNeedsHookup,
- bool* aDidResolve)
- {
- // Resolve a broadcaster hookup. Look at the element that we're
- // trying to resolve: it could be an '<observes>' element, or just
- // a vanilla element with an 'observes' attribute on it.
- nsresult rv;
- *aDidResolve = false;
- nsCOMPtr<Element> listener;
- nsAutoString broadcasterID;
- nsAutoString attribute;
- nsCOMPtr<Element> broadcaster;
- rv = FindBroadcaster(aElement, getter_AddRefs(listener),
- broadcasterID, attribute, getter_AddRefs(broadcaster));
- switch (rv) {
- case NS_FINDBROADCASTER_NOT_FOUND:
- *aNeedsHookup = false;
- return NS_OK;
- case NS_FINDBROADCASTER_AWAIT_OVERLAYS:
- *aNeedsHookup = true;
- return NS_OK;
- case NS_FINDBROADCASTER_FOUND:
- break;
- default:
- return rv;
- }
- NS_ENSURE_ARG(broadcaster && listener);
- ErrorResult domRv;
- AddBroadcastListenerFor(*broadcaster, *listener, attribute, domRv);
- if (domRv.Failed()) {
- return domRv.StealNSResult();
- }
- // Tell the world we succeeded
- if (MOZ_LOG_TEST(gXULLog, LogLevel::Debug)) {
- nsCOMPtr<nsIContent> content =
- do_QueryInterface(listener);
- NS_ASSERTION(content != nullptr, "not an nsIContent");
- if (! content)
- return rv;
- nsAutoCString attributeC,broadcasteridC;
- attributeC.AssignWithConversion(attribute);
- broadcasteridC.AssignWithConversion(broadcasterID);
- MOZ_LOG(gXULLog, LogLevel::Debug,
- ("xul: broadcaster hookup <%s attribute='%s'> to %s",
- nsAtomCString(content->NodeInfo()->NameAtom()).get(),
- attributeC.get(),
- broadcasteridC.get()));
- }
- *aNeedsHookup = false;
- *aDidResolve = true;
- return NS_OK;
- }
- nsresult
- XULDocument::InsertElement(nsINode* aParent, nsIContent* aChild,
- bool aNotify)
- {
- // Insert aChild appropriately into aParent, accounting for a
- // 'pos' attribute set on aChild.
- nsAutoString posStr;
- bool wasInserted = false;
- // insert after an element of a given id
- aChild->GetAttr(kNameSpaceID_None, nsGkAtoms::insertafter, posStr);
- bool isInsertAfter = true;
- if (posStr.IsEmpty()) {
- aChild->GetAttr(kNameSpaceID_None, nsGkAtoms::insertbefore, posStr);
- isInsertAfter = false;
- }
- if (!posStr.IsEmpty()) {
- nsIDocument *document = aParent->OwnerDoc();
- nsIContent *content = nullptr;
- char* str = ToNewCString(posStr);
- char* rest;
- char* token = nsCRT::strtok(str, ", ", &rest);
- while (token) {
- content = document->GetElementById(NS_ConvertASCIItoUTF16(token));
- if (content)
- break;
- token = nsCRT::strtok(rest, ", ", &rest);
- }
- free(str);
- if (content) {
- int32_t pos = aParent->IndexOf(content);
- if (pos != -1) {
- pos = isInsertAfter ? pos + 1 : pos;
- nsresult rv = aParent->InsertChildAt(aChild, pos, aNotify);
- if (NS_FAILED(rv))
- return rv;
- wasInserted = true;
- }
- }
- }
- if (!wasInserted) {
- aChild->GetAttr(kNameSpaceID_None, nsGkAtoms::position, posStr);
- if (!posStr.IsEmpty()) {
- nsresult rv;
- // Positions are one-indexed.
- int32_t pos = posStr.ToInteger(&rv);
- // Note: if the insertion index (which is |pos - 1|) would be less
- // than 0 or greater than the number of children aParent has, then
- // don't insert, since the position is bogus. Just skip on to
- // appending.
- if (NS_SUCCEEDED(rv) && pos > 0 &&
- uint32_t(pos - 1) <= aParent->GetChildCount()) {
- rv = aParent->InsertChildAt(aChild, pos - 1, aNotify);
- if (NS_SUCCEEDED(rv))
- wasInserted = true;
- // If the insertion fails, then we should still
- // attempt an append. Thus, rather than returning rv
- // immediately, we fall through to the final
- // "catch-all" case that just does an AppendChildTo.
- }
- }
- }
- if (!wasInserted) {
- return aParent->AppendChildTo(aChild, aNotify);
- }
- return NS_OK;
- }
- nsresult
- XULDocument::RemoveElement(nsINode* aParent, nsINode* aChild)
- {
- int32_t nodeOffset = aParent->IndexOf(aChild);
- aParent->RemoveChildAt(nodeOffset, true);
- return NS_OK;
- }
- //----------------------------------------------------------------------
- //
- // CachedChromeStreamListener
- //
- XULDocument::CachedChromeStreamListener::CachedChromeStreamListener(XULDocument* aDocument, bool aProtoLoaded)
- : mDocument(aDocument),
- mProtoLoaded(aProtoLoaded)
- {
- }
- XULDocument::CachedChromeStreamListener::~CachedChromeStreamListener()
- {
- }
- NS_IMPL_ISUPPORTS(XULDocument::CachedChromeStreamListener,
- nsIRequestObserver, nsIStreamListener)
- NS_IMETHODIMP
- XULDocument::CachedChromeStreamListener::OnStartRequest(nsIRequest *request,
- nsISupports* acontext)
- {
- return NS_ERROR_PARSED_DATA_CACHED;
- }
- NS_IMETHODIMP
- XULDocument::CachedChromeStreamListener::OnStopRequest(nsIRequest *request,
- nsISupports* aContext,
- nsresult aStatus)
- {
- if (! mProtoLoaded)
- return NS_OK;
- return mDocument->OnPrototypeLoadDone(true);
- }
- NS_IMETHODIMP
- XULDocument::CachedChromeStreamListener::OnDataAvailable(nsIRequest *request,
- nsISupports* aContext,
- nsIInputStream* aInStr,
- uint64_t aSourceOffset,
- uint32_t aCount)
- {
- NS_NOTREACHED("CachedChromeStream doesn't receive data");
- return NS_ERROR_UNEXPECTED;
- }
- //----------------------------------------------------------------------
- //
- // ParserObserver
- //
- XULDocument::ParserObserver::ParserObserver(XULDocument* aDocument,
- nsXULPrototypeDocument* aPrototype)
- : mDocument(aDocument), mPrototype(aPrototype)
- {
- }
- XULDocument::ParserObserver::~ParserObserver()
- {
- }
- NS_IMPL_ISUPPORTS(XULDocument::ParserObserver, nsIRequestObserver)
- NS_IMETHODIMP
- XULDocument::ParserObserver::OnStartRequest(nsIRequest *request,
- nsISupports* aContext)
- {
- // Guard against buggy channels calling OnStartRequest multiple times.
- if (mPrototype) {
- nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
- nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
- if (channel && secMan) {
- nsCOMPtr<nsIPrincipal> principal;
- secMan->GetChannelResultPrincipal(channel, getter_AddRefs(principal));
- // Failure there is ok -- it'll just set a (safe) null principal
- mPrototype->SetDocumentPrincipal(principal);
- }
- // Make sure to avoid cycles
- mPrototype = nullptr;
- }
-
- return NS_OK;
- }
- NS_IMETHODIMP
- XULDocument::ParserObserver::OnStopRequest(nsIRequest *request,
- nsISupports* aContext,
- nsresult aStatus)
- {
- nsresult rv = NS_OK;
- if (NS_FAILED(aStatus)) {
- // If an overlay load fails, we need to nudge the prototype
- // walk along.
- nsCOMPtr<nsIChannel> aChannel = do_QueryInterface(request);
- if (aChannel) {
- nsCOMPtr<nsIURI> uri;
- aChannel->GetOriginalURI(getter_AddRefs(uri));
- if (uri) {
- mDocument->ReportMissingOverlay(uri);
- }
- }
- rv = mDocument->ResumeWalk();
- }
- // Drop the reference to the document to break cycle between the
- // document, the parser, the content sink, and the parser
- // observer.
- mDocument = nullptr;
- return rv;
- }
- already_AddRefed<nsPIWindowRoot>
- XULDocument::GetWindowRoot()
- {
- if (!mDocumentContainer) {
- return nullptr;
- }
- nsCOMPtr<nsPIDOMWindowOuter> piWin = mDocumentContainer->GetWindow();
- return piWin ? piWin->GetTopWindowRoot() : nullptr;
- }
- bool
- XULDocument::IsDocumentRightToLeft()
- {
- // setting the localedir attribute on the root element forces a
- // specific direction for the document.
- Element* element = GetRootElement();
- if (element) {
- static nsIContent::AttrValuesArray strings[] =
- {&nsGkAtoms::ltr, &nsGkAtoms::rtl, nullptr};
- switch (element->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::localedir,
- strings, eCaseMatters)) {
- case 0: return false;
- case 1: return true;
- default: break; // otherwise, not a valid value, so fall through
- }
- }
- // otherwise, get the locale from the chrome registry and
- // look up the intl.uidirection.<locale> preference
- nsCOMPtr<nsIXULChromeRegistry> reg =
- mozilla::services::GetXULChromeRegistryService();
- if (!reg)
- return false;
- nsAutoCString package;
- bool isChrome;
- if (NS_SUCCEEDED(mDocumentURI->SchemeIs("chrome", &isChrome)) &&
- isChrome) {
- mDocumentURI->GetHostPort(package);
- }
- else {
- // use the 'global' package for about and resource uris.
- // otherwise, just default to left-to-right.
- bool isAbout, isResource;
- if (NS_SUCCEEDED(mDocumentURI->SchemeIs("about", &isAbout)) &&
- isAbout) {
- package.AssignLiteral("global");
- }
- else if (NS_SUCCEEDED(mDocumentURI->SchemeIs("resource", &isResource)) &&
- isResource) {
- package.AssignLiteral("global");
- }
- else {
- return false;
- }
- }
- bool isRTL = false;
- reg->IsLocaleRTL(package, &isRTL);
- return isRTL;
- }
- void
- XULDocument::ResetDocumentDirection()
- {
- DocumentStatesChanged(NS_DOCUMENT_STATE_RTL_LOCALE);
- }
- void
- XULDocument::DirectionChanged(const char* aPrefName, void* aData)
- {
- // Reset the direction and restyle the document if necessary.
- XULDocument* doc = (XULDocument *)aData;
- if (doc) {
- doc->ResetDocumentDirection();
- }
- }
- int
- XULDocument::GetDocumentLWTheme()
- {
- if (mDocLWTheme == Doc_Theme_Uninitialized) {
- mDocLWTheme = Doc_Theme_None; // No lightweight theme by default
- Element* element = GetRootElement();
- nsAutoString hasLWTheme;
- if (element &&
- element->GetAttr(kNameSpaceID_None, nsGkAtoms::lwtheme, hasLWTheme) &&
- !(hasLWTheme.IsEmpty()) &&
- hasLWTheme.EqualsLiteral("true")) {
- mDocLWTheme = Doc_Theme_Neutral;
- nsAutoString lwTheme;
- element->GetAttr(kNameSpaceID_None, nsGkAtoms::lwthemetextcolor, lwTheme);
- if (!(lwTheme.IsEmpty())) {
- if (lwTheme.EqualsLiteral("dark"))
- mDocLWTheme = Doc_Theme_Dark;
- else if (lwTheme.EqualsLiteral("bright"))
- mDocLWTheme = Doc_Theme_Bright;
- }
- }
- }
- return mDocLWTheme;
- }
- NS_IMETHODIMP
- XULDocument::GetBoxObjectFor(nsIDOMElement* aElement, nsIBoxObject** aResult)
- {
- ErrorResult rv;
- nsCOMPtr<Element> el = do_QueryInterface(aElement);
- *aResult = GetBoxObjectFor(el, rv).take();
- return rv.StealNSResult();
- }
- JSObject*
- XULDocument::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
- {
- return XULDocumentBinding::Wrap(aCx, this, aGivenProto);
- }
- } // namespace dom
- } // namespace mozilla
|