1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/xul/templates/src/nsXULTemplateBuilder.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,503 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#ifndef nsXULTemplateBuilder_h__ 1.10 +#define nsXULTemplateBuilder_h__ 1.11 + 1.12 +#include "nsStubDocumentObserver.h" 1.13 +#include "nsIScriptSecurityManager.h" 1.14 +#include "nsIObserver.h" 1.15 +#include "nsIRDFCompositeDataSource.h" 1.16 +#include "nsIRDFContainer.h" 1.17 +#include "nsIRDFContainerUtils.h" 1.18 +#include "nsIRDFDataSource.h" 1.19 +#include "nsIRDFObserver.h" 1.20 +#include "nsIRDFService.h" 1.21 +#include "nsIXULTemplateBuilder.h" 1.22 + 1.23 +#include "nsCOMArray.h" 1.24 +#include "nsTArray.h" 1.25 +#include "nsDataHashtable.h" 1.26 +#include "nsTemplateRule.h" 1.27 +#include "nsTemplateMatch.h" 1.28 +#include "nsIXULTemplateQueryProcessor.h" 1.29 +#include "nsCycleCollectionParticipant.h" 1.30 + 1.31 +#include "prlog.h" 1.32 +#ifdef PR_LOGGING 1.33 +extern PRLogModuleInfo* gXULTemplateLog; 1.34 +#endif 1.35 + 1.36 +class nsIContent; 1.37 +class nsIObserverService; 1.38 +class nsIRDFCompositeDataSource; 1.39 +class nsIXULDocument; 1.40 + 1.41 +/** 1.42 + * An object that translates an RDF graph into a presentation using a 1.43 + * set of rules. 1.44 + */ 1.45 +class nsXULTemplateBuilder : public nsIXULTemplateBuilder, 1.46 + public nsIObserver, 1.47 + public nsStubDocumentObserver 1.48 +{ 1.49 + void CleanUp(bool aIsFinal); 1.50 + 1.51 +public: 1.52 + nsXULTemplateBuilder(); 1.53 + virtual ~nsXULTemplateBuilder(); 1.54 + 1.55 + nsresult InitGlobals(); 1.56 + 1.57 + /** 1.58 + * Clear the template builder structures. The aIsFinal flag is set to true 1.59 + * when the template is going away. 1.60 + */ 1.61 + virtual void Uninit(bool aIsFinal); 1.62 + 1.63 + // nsISupports interface 1.64 + NS_DECL_CYCLE_COLLECTING_ISUPPORTS 1.65 + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXULTemplateBuilder, 1.66 + nsIXULTemplateBuilder) 1.67 + 1.68 + // nsIXULTemplateBuilder interface 1.69 + NS_DECL_NSIXULTEMPLATEBUILDER 1.70 + 1.71 + // nsIObserver Interface 1.72 + NS_DECL_NSIOBSERVER 1.73 + 1.74 + // nsIMutationObserver 1.75 + NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED 1.76 + NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED 1.77 + NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED 1.78 + 1.79 + /** 1.80 + * Remove an old result and/or add a new result. This method will retrieve 1.81 + * the set of containers where the result could be inserted and either add 1.82 + * the new result to those containers, or remove the result from those 1.83 + * containers. UpdateResultInContainer is called for each container. 1.84 + * 1.85 + * @param aOldResult result to remove 1.86 + * @param aNewResult result to add 1.87 + * @param aQueryNode query node for new result 1.88 + */ 1.89 + nsresult 1.90 + UpdateResult(nsIXULTemplateResult* aOldResult, 1.91 + nsIXULTemplateResult* aNewResult, 1.92 + nsIDOMNode* aQueryNode); 1.93 + 1.94 + /** 1.95 + * Remove an old result and/or add a new result from a specific container. 1.96 + * 1.97 + * @param aOldResult result to remove 1.98 + * @param aNewResult result to add 1.99 + * @param aQueryNode queryset for the new result 1.100 + * @param aOldId id of old result 1.101 + * @param aNewId id of new result 1.102 + * @param aInsertionPoint container to remove or add result inside 1.103 + */ 1.104 + nsresult 1.105 + UpdateResultInContainer(nsIXULTemplateResult* aOldResult, 1.106 + nsIXULTemplateResult* aNewResult, 1.107 + nsTemplateQuerySet* aQuerySet, 1.108 + nsIRDFResource* aOldId, 1.109 + nsIRDFResource* aNewId, 1.110 + nsIContent* aInsertionPoint); 1.111 + 1.112 + nsresult 1.113 + ComputeContainmentProperties(); 1.114 + 1.115 + static bool 1.116 + IsTemplateElement(nsIContent* aContent); 1.117 + 1.118 + virtual nsresult 1.119 + RebuildAll() = 0; // must be implemented by subclasses 1.120 + 1.121 + void RunnableRebuild() { Rebuild(); } 1.122 + void RunnableLoadAndRebuild() { 1.123 + Uninit(false); // Reset results 1.124 + 1.125 + nsCOMPtr<nsIDocument> doc = mRoot ? mRoot->GetDocument() : nullptr; 1.126 + if (doc) { 1.127 + bool shouldDelay; 1.128 + LoadDataSources(doc, &shouldDelay); 1.129 + if (!shouldDelay) { 1.130 + Rebuild(); 1.131 + } 1.132 + } 1.133 + } 1.134 + 1.135 + // mRoot should not be cleared until after Uninit is finished so that 1.136 + // generated content can be removed during uninitialization. 1.137 + void UninitFalse() { Uninit(false); mRoot = nullptr; } 1.138 + void UninitTrue() { Uninit(true); mRoot = nullptr; } 1.139 + 1.140 + /** 1.141 + * Find the <template> tag that applies for this builder 1.142 + */ 1.143 + nsresult 1.144 + GetTemplateRoot(nsIContent** aResult); 1.145 + 1.146 + /** 1.147 + * Compile the template's queries 1.148 + */ 1.149 + nsresult 1.150 + CompileQueries(); 1.151 + 1.152 + /** 1.153 + * Compile the template given a <template> in aTemplate. This function 1.154 + * is called recursively to handle queries inside a queryset. For the 1.155 + * outer pass, aIsQuerySet will be false, while the inner pass this will 1.156 + * be true. 1.157 + * 1.158 + * aCanUseTemplate will be set to true if the template's queries could be 1.159 + * compiled, and false otherwise. If false, the entire template is 1.160 + * invalid. 1.161 + * 1.162 + * @param aTemplate <template> to compile 1.163 + * @param aQuerySet first queryset 1.164 + * @param aIsQuerySet true if 1.165 + * @param aPriority the queryset index, incremented when a new one is added 1.166 + * @param aCanUseTemplate true if template is valid 1.167 + */ 1.168 + nsresult 1.169 + CompileTemplate(nsIContent* aTemplate, 1.170 + nsTemplateQuerySet* aQuerySet, 1.171 + bool aIsQuerySet, 1.172 + int32_t* aPriority, 1.173 + bool* aCanUseTemplate); 1.174 + 1.175 + /** 1.176 + * Compile a query using the extended syntax. For backwards compatible RDF 1.177 + * syntax where there is no <query>, the <conditions> becomes the query. 1.178 + * 1.179 + * @param aRuleElement <rule> element 1.180 + * @param aActionElement <action> element 1.181 + * @param aMemberVariable member variable for the query 1.182 + * @param aQuerySet the queryset 1.183 + */ 1.184 + nsresult 1.185 + CompileExtendedQuery(nsIContent* aRuleElement, 1.186 + nsIContent* aActionElement, 1.187 + nsIAtom* aMemberVariable, 1.188 + nsTemplateQuerySet* aQuerySet); 1.189 + 1.190 + /** 1.191 + * Determine the ref variable and tag from inside a RDF query. 1.192 + */ 1.193 + void DetermineRDFQueryRef(nsIContent* aQueryElement, nsIAtom** tag); 1.194 + 1.195 + /** 1.196 + * Determine the member variable from inside an action body. It will be 1.197 + * the value of the uri attribute on a node. 1.198 + */ 1.199 + already_AddRefed<nsIAtom> DetermineMemberVariable(nsIContent* aElement); 1.200 + 1.201 + /** 1.202 + * Compile a simple query. A simple query is one that doesn't have a 1.203 + * <query> and should use a default query which would normally just return 1.204 + * a list of children of the reference point. 1.205 + * 1.206 + * @param aRuleElement the <rule> 1.207 + * @param aQuerySet the query set 1.208 + * @param aCanUseTemplate true if the query is valid 1.209 + */ 1.210 + nsresult 1.211 + CompileSimpleQuery(nsIContent* aRuleElement, 1.212 + nsTemplateQuerySet* aQuerySet, 1.213 + bool* aCanUseTemplate); 1.214 + 1.215 + /** 1.216 + * Compile the <conditions> tag in a rule 1.217 + * 1.218 + * @param aRule template rule 1.219 + * @param aConditions <conditions> element 1.220 + */ 1.221 + nsresult 1.222 + CompileConditions(nsTemplateRule* aRule, nsIContent* aConditions); 1.223 + 1.224 + /** 1.225 + * Compile a <where> tag in a condition. The caller should set 1.226 + * *aCurrentCondition to null for the first condition. This value will be 1.227 + * updated to point to the new condition before returning. The conditions 1.228 + * will be added to the rule aRule by this method. 1.229 + * 1.230 + * @param aRule template rule 1.231 + * @param aCondition <where> element 1.232 + * @param aCurrentCondition compiled condition 1.233 + */ 1.234 + nsresult 1.235 + CompileWhereCondition(nsTemplateRule* aRule, 1.236 + nsIContent* aCondition, 1.237 + nsTemplateCondition** aCurrentCondition); 1.238 + 1.239 + /** 1.240 + * Compile the <bindings> for an extended template syntax rule. 1.241 + */ 1.242 + nsresult 1.243 + CompileBindings(nsTemplateRule* aRule, nsIContent* aBindings); 1.244 + 1.245 + /** 1.246 + * Compile a single binding for an extended template syntax rule. 1.247 + */ 1.248 + nsresult 1.249 + CompileBinding(nsTemplateRule* aRule, nsIContent* aBinding); 1.250 + 1.251 + /** 1.252 + * Add automatic bindings for simple rules 1.253 + */ 1.254 + nsresult 1.255 + AddSimpleRuleBindings(nsTemplateRule* aRule, nsIContent* aElement); 1.256 + 1.257 + static void 1.258 + AddBindingsFor(nsXULTemplateBuilder* aSelf, 1.259 + const nsAString& aVariable, 1.260 + void* aClosure); 1.261 + 1.262 + /** 1.263 + * Load the datasources for the template. shouldDelayBuilding is an out 1.264 + * parameter which will be set to true to indicate that content building 1.265 + * should not be performed yet as the datasource has not yet loaded. If 1.266 + * false, the datasource has already loaded so building can proceed 1.267 + * immediately. In the former case, the datasource or query processor 1.268 + * should either rebuild the template or update results when the 1.269 + * datasource is loaded as needed. 1.270 + */ 1.271 + nsresult 1.272 + LoadDataSources(nsIDocument* aDoc, bool* shouldDelayBuilding); 1.273 + 1.274 + /** 1.275 + * Called by LoadDataSources to load a datasource given a uri list 1.276 + * in aDataSource. The list is a set of uris separated by spaces. 1.277 + * If aIsRDFQuery is true, then this is for an RDF datasource which 1.278 + * causes the method to check for additional flags specific to the 1.279 + * RDF processor. 1.280 + */ 1.281 + nsresult 1.282 + LoadDataSourceUrls(nsIDocument* aDocument, 1.283 + const nsAString& aDataSources, 1.284 + bool aIsRDFQuery, 1.285 + bool* aShouldDelayBuilding); 1.286 + 1.287 + nsresult 1.288 + InitHTMLTemplateRoot(); 1.289 + 1.290 + /** 1.291 + * Determine which rule matches a given result. aContainer is used for 1.292 + * tag matching and is optional for non-content generating builders. 1.293 + * The returned matched rule is always one of the rules owned by the 1.294 + * query set aQuerySet. 1.295 + * 1.296 + * @param aContainer parent where generated content will be inserted 1.297 + * @param aResult result to match 1.298 + * @param aQuerySet query set to examine the rules of 1.299 + * @param aMatchedRule [out] rule that has matched, or null if any. 1.300 + * @param aRuleIndex [out] index of the rule 1.301 + */ 1.302 + nsresult 1.303 + DetermineMatchedRule(nsIContent* aContainer, 1.304 + nsIXULTemplateResult* aResult, 1.305 + nsTemplateQuerySet* aQuerySet, 1.306 + nsTemplateRule** aMatchedRule, 1.307 + int16_t *aRuleIndex); 1.308 + 1.309 + // XXX sigh, the string template foo doesn't mix with 1.310 + // operator->*() on egcs-1.1.2, so we'll need to explicitly pass 1.311 + // "this" and use good ol' fashioned static callbacks. 1.312 + void 1.313 + ParseAttribute(const nsAString& aAttributeValue, 1.314 + void (*aVariableCallback)(nsXULTemplateBuilder* aThis, const nsAString&, void*), 1.315 + void (*aTextCallback)(nsXULTemplateBuilder* aThis, const nsAString&, void*), 1.316 + void* aClosure); 1.317 + 1.318 + nsresult 1.319 + SubstituteText(nsIXULTemplateResult* aMatch, 1.320 + const nsAString& aAttributeValue, 1.321 + nsAString& aResult); 1.322 + 1.323 + static void 1.324 + SubstituteTextAppendText(nsXULTemplateBuilder* aThis, const nsAString& aText, void* aClosure); 1.325 + 1.326 + static void 1.327 + SubstituteTextReplaceVariable(nsXULTemplateBuilder* aThis, const nsAString& aVariable, void* aClosure); 1.328 + 1.329 + nsresult 1.330 + IsSystemPrincipal(nsIPrincipal *principal, bool *result); 1.331 + 1.332 + /** 1.333 + * Convenience method which gets a resource for a result. If a result 1.334 + * doesn't have a resource set, it will create one from the result's id. 1.335 + */ 1.336 + nsresult GetResultResource(nsIXULTemplateResult* aResult, 1.337 + nsIRDFResource** aResource); 1.338 + 1.339 +protected: 1.340 + nsCOMPtr<nsISupports> mDataSource; 1.341 + nsCOMPtr<nsIRDFDataSource> mDB; 1.342 + nsCOMPtr<nsIRDFCompositeDataSource> mCompDB; 1.343 + 1.344 + /** 1.345 + * Circular reference, broken when the document is destroyed. 1.346 + */ 1.347 + nsCOMPtr<nsIContent> mRoot; 1.348 + 1.349 + /** 1.350 + * The root result, translated from the root element's ref 1.351 + */ 1.352 + nsCOMPtr<nsIXULTemplateResult> mRootResult; 1.353 + 1.354 + nsCOMArray<nsIXULBuilderListener> mListeners; 1.355 + 1.356 + /** 1.357 + * The query processor which generates results 1.358 + */ 1.359 + nsCOMPtr<nsIXULTemplateQueryProcessor> mQueryProcessor; 1.360 + 1.361 + /** 1.362 + * The list of querysets 1.363 + */ 1.364 + nsTArray<nsTemplateQuerySet *> mQuerySets; 1.365 + 1.366 + /** 1.367 + * Set to true if the rules have already been compiled 1.368 + */ 1.369 + bool mQueriesCompiled; 1.370 + 1.371 + /** 1.372 + * The default reference and member variables. 1.373 + */ 1.374 + nsCOMPtr<nsIAtom> mRefVariable; 1.375 + nsCOMPtr<nsIAtom> mMemberVariable; 1.376 + 1.377 + /** 1.378 + * The match map contains nsTemplateMatch objects, one for each unique 1.379 + * match found, keyed by the resource for that match. A particular match 1.380 + * will contain a linked list of all of the matches for that unique result 1.381 + * id. Only one is active at a time. When a match is retracted, look in 1.382 + * the match map, remove it, and apply the next valid match in sequence to 1.383 + * make active. 1.384 + */ 1.385 + nsDataHashtable<nsISupportsHashKey, nsTemplateMatch*> mMatchMap; 1.386 + 1.387 + // pseudo-constants 1.388 + static nsrefcnt gRefCnt; 1.389 + static nsIRDFService* gRDFService; 1.390 + static nsIRDFContainerUtils* gRDFContainerUtils; 1.391 + static nsIScriptSecurityManager* gScriptSecurityManager; 1.392 + static nsIPrincipal* gSystemPrincipal; 1.393 + static nsIObserverService* gObserverService; 1.394 + 1.395 + enum { 1.396 + eDontTestEmpty = (1 << 0), 1.397 + eDontRecurse = (1 << 1), 1.398 + eLoggingEnabled = (1 << 2) 1.399 + }; 1.400 + 1.401 + int32_t mFlags; 1.402 + 1.403 + /** 1.404 + * Stack-based helper class to maintain a list of ``activated'' 1.405 + * resources; i.e., resources for which we are currently building 1.406 + * content. 1.407 + */ 1.408 + class ActivationEntry { 1.409 + public: 1.410 + nsIRDFResource *mResource; 1.411 + ActivationEntry *mPrevious; 1.412 + ActivationEntry **mLink; 1.413 + 1.414 + ActivationEntry(nsIRDFResource *aResource, ActivationEntry **aLink) 1.415 + : mResource(aResource), 1.416 + mPrevious(*aLink), 1.417 + mLink(aLink) { *mLink = this; } 1.418 + 1.419 + ~ActivationEntry() { *mLink = mPrevious; } 1.420 + }; 1.421 + 1.422 + /** 1.423 + * The top of the stack of resources that we're currently building 1.424 + * content for. 1.425 + */ 1.426 + ActivationEntry *mTop; 1.427 + 1.428 + /** 1.429 + * Determine if a resource is currently on the activation stack. 1.430 + */ 1.431 + bool 1.432 + IsActivated(nsIRDFResource *aResource); 1.433 + 1.434 + /** 1.435 + * Returns true if content may be generated for a result, or false if it 1.436 + * cannot, for example, if it would be created inside a closed container. 1.437 + * Those results will be generated when the container is opened. 1.438 + * If false is returned, no content should be generated. Possible 1.439 + * insertion locations may optionally be set for new content, depending on 1.440 + * the builder being used. Note that *aLocations or some items within 1.441 + * aLocations may be null. 1.442 + */ 1.443 + virtual bool 1.444 + GetInsertionLocations(nsIXULTemplateResult* aResult, 1.445 + nsCOMArray<nsIContent>** aLocations) = 0; 1.446 + 1.447 + /** 1.448 + * Must be implemented by subclasses. Handle removing the generated 1.449 + * output for aOldMatch and adding new output for aNewMatch. Either 1.450 + * aOldMatch or aNewMatch may be null. aContext is the location returned 1.451 + * from the call to MayGenerateResult. 1.452 + */ 1.453 + virtual nsresult 1.454 + ReplaceMatch(nsIXULTemplateResult* aOldResult, 1.455 + nsTemplateMatch* aNewMatch, 1.456 + nsTemplateRule* aNewMatchRule, 1.457 + void *aContext) = 0; 1.458 + 1.459 + /** 1.460 + * Must be implemented by subclasses. Handle change in bound 1.461 + * variable values for aResult. aModifiedVars contains the set 1.462 + * of variables that have changed. 1.463 + * @param aResult the ersult for which variable bindings has changed. 1.464 + * @param aModifiedVars the set of variables for which the bindings 1.465 + * have changed. 1.466 + */ 1.467 + virtual nsresult 1.468 + SynchronizeResult(nsIXULTemplateResult* aResult) = 0; 1.469 + 1.470 + /** 1.471 + * Output a new match or removed match to the console. 1.472 + * 1.473 + * @param aId id of the result 1.474 + * @param aMatch new or removed match 1.475 + * @param aIsNew true for new matched, false for removed matches 1.476 + */ 1.477 + void 1.478 + OutputMatchToLog(nsIRDFResource* aId, 1.479 + nsTemplateMatch* aMatch, 1.480 + bool aIsNew); 1.481 + 1.482 + virtual void Traverse(nsCycleCollectionTraversalCallback &cb) const 1.483 + { 1.484 + } 1.485 + 1.486 + /** 1.487 + * Start observing events from the observer service and the given 1.488 + * document. 1.489 + * 1.490 + * @param aDocument the document to observe 1.491 + */ 1.492 + void StartObserving(nsIDocument* aDocument); 1.493 + 1.494 + /** 1.495 + * Stop observing events from the observer service and any associated 1.496 + * document. 1.497 + */ 1.498 + void StopObserving(); 1.499 + 1.500 + /** 1.501 + * Document that we're observing. Weak ref! 1.502 + */ 1.503 + nsIDocument* mObservedDocument; 1.504 +}; 1.505 + 1.506 +#endif // nsXULTemplateBuilder_h__