diff options
Diffstat (limited to 'dom/xul/templates/nsXULTemplateBuilder.h')
-rw-r--r-- | dom/xul/templates/nsXULTemplateBuilder.h | 502 |
1 files changed, 502 insertions, 0 deletions
diff --git a/dom/xul/templates/nsXULTemplateBuilder.h b/dom/xul/templates/nsXULTemplateBuilder.h new file mode 100644 index 000000000..7da8ffc98 --- /dev/null +++ b/dom/xul/templates/nsXULTemplateBuilder.h @@ -0,0 +1,502 @@ +/* -*- 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/. */ + +#ifndef nsXULTemplateBuilder_h__ +#define nsXULTemplateBuilder_h__ + +#include "nsStubDocumentObserver.h" +#include "nsIScriptSecurityManager.h" +#include "nsIObserver.h" +#include "nsIRDFCompositeDataSource.h" +#include "nsIRDFContainer.h" +#include "nsIRDFContainerUtils.h" +#include "nsIRDFDataSource.h" +#include "nsIRDFObserver.h" +#include "nsIRDFService.h" +#include "nsIXULTemplateBuilder.h" + +#include "nsCOMArray.h" +#include "nsTArray.h" +#include "nsDataHashtable.h" +#include "nsTemplateRule.h" +#include "nsTemplateMatch.h" +#include "nsIXULTemplateQueryProcessor.h" +#include "nsCycleCollectionParticipant.h" + +#include "mozilla/Logging.h" +extern mozilla::LazyLogModule gXULTemplateLog; + +class nsIContent; +class nsIObserverService; +class nsIRDFCompositeDataSource; + +/** + * An object that translates an RDF graph into a presentation using a + * set of rules. + */ +class nsXULTemplateBuilder : public nsIXULTemplateBuilder, + public nsIObserver, + public nsStubDocumentObserver +{ + void CleanUp(bool aIsFinal); + void DestroyMatchMap(); + +public: + nsXULTemplateBuilder(); + + nsresult InitGlobals(); + + /** + * Clear the template builder structures. The aIsFinal flag is set to true + * when the template is going away. + */ + virtual void Uninit(bool aIsFinal); + + // nsISupports interface + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXULTemplateBuilder, + nsIXULTemplateBuilder) + + // nsIXULTemplateBuilder interface + NS_DECL_NSIXULTEMPLATEBUILDER + + // nsIObserver Interface + NS_DECL_NSIOBSERVER + + // nsIMutationObserver + NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED + NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED + NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED + + /** + * Remove an old result and/or add a new result. This method will retrieve + * the set of containers where the result could be inserted and either add + * the new result to those containers, or remove the result from those + * containers. UpdateResultInContainer is called for each container. + * + * @param aOldResult result to remove + * @param aNewResult result to add + * @param aQueryNode query node for new result + */ + nsresult + UpdateResult(nsIXULTemplateResult* aOldResult, + nsIXULTemplateResult* aNewResult, + nsIDOMNode* aQueryNode); + + /** + * Remove an old result and/or add a new result from a specific container. + * + * @param aOldResult result to remove + * @param aNewResult result to add + * @param aQueryNode queryset for the new result + * @param aOldId id of old result + * @param aNewId id of new result + * @param aInsertionPoint container to remove or add result inside + */ + nsresult + UpdateResultInContainer(nsIXULTemplateResult* aOldResult, + nsIXULTemplateResult* aNewResult, + nsTemplateQuerySet* aQuerySet, + nsIRDFResource* aOldId, + nsIRDFResource* aNewId, + nsIContent* aInsertionPoint); + + nsresult + ComputeContainmentProperties(); + + static bool + IsTemplateElement(nsIContent* aContent); + + virtual nsresult + RebuildAll() = 0; // must be implemented by subclasses + + void RunnableRebuild() { Rebuild(); } + void RunnableLoadAndRebuild() { + Uninit(false); // Reset results + + nsCOMPtr<nsIDocument> doc = mRoot ? mRoot->GetComposedDoc() : nullptr; + if (doc) { + bool shouldDelay; + LoadDataSources(doc, &shouldDelay); + if (!shouldDelay) { + Rebuild(); + } + } + } + + // mRoot should not be cleared until after Uninit is finished so that + // generated content can be removed during uninitialization. + void UninitFalse() { Uninit(false); mRoot = nullptr; } + void UninitTrue() { Uninit(true); mRoot = nullptr; } + + /** + * Find the <template> tag that applies for this builder + */ + nsresult + GetTemplateRoot(nsIContent** aResult); + + /** + * Compile the template's queries + */ + nsresult + CompileQueries(); + + /** + * Compile the template given a <template> in aTemplate. This function + * is called recursively to handle queries inside a queryset. For the + * outer pass, aIsQuerySet will be false, while the inner pass this will + * be true. + * + * aCanUseTemplate will be set to true if the template's queries could be + * compiled, and false otherwise. If false, the entire template is + * invalid. + * + * @param aTemplate <template> to compile + * @param aQuerySet first queryset + * @param aIsQuerySet true if + * @param aPriority the queryset index, incremented when a new one is added + * @param aCanUseTemplate true if template is valid + */ + nsresult + CompileTemplate(nsIContent* aTemplate, + nsTemplateQuerySet* aQuerySet, + bool aIsQuerySet, + int32_t* aPriority, + bool* aCanUseTemplate); + + /** + * Compile a query using the extended syntax. For backwards compatible RDF + * syntax where there is no <query>, the <conditions> becomes the query. + * + * @param aRuleElement <rule> element + * @param aActionElement <action> element + * @param aMemberVariable member variable for the query + * @param aQuerySet the queryset + */ + nsresult + CompileExtendedQuery(nsIContent* aRuleElement, + nsIContent* aActionElement, + nsIAtom* aMemberVariable, + nsTemplateQuerySet* aQuerySet); + + /** + * Determine the ref variable and tag from inside a RDF query. + */ + void DetermineRDFQueryRef(nsIContent* aQueryElement, nsIAtom** tag); + + /** + * Determine the member variable from inside an action body. It will be + * the value of the uri attribute on a node. + */ + already_AddRefed<nsIAtom> DetermineMemberVariable(nsIContent* aElement); + + /** + * Compile a simple query. A simple query is one that doesn't have a + * <query> and should use a default query which would normally just return + * a list of children of the reference point. + * + * @param aRuleElement the <rule> + * @param aQuerySet the query set + * @param aCanUseTemplate true if the query is valid + */ + nsresult + CompileSimpleQuery(nsIContent* aRuleElement, + nsTemplateQuerySet* aQuerySet, + bool* aCanUseTemplate); + + /** + * Compile the <conditions> tag in a rule + * + * @param aRule template rule + * @param aConditions <conditions> element + */ + nsresult + CompileConditions(nsTemplateRule* aRule, nsIContent* aConditions); + + /** + * Compile a <where> tag in a condition. The caller should set + * *aCurrentCondition to null for the first condition. This value will be + * updated to point to the new condition before returning. The conditions + * will be added to the rule aRule by this method. + * + * @param aRule template rule + * @param aCondition <where> element + * @param aCurrentCondition compiled condition + */ + nsresult + CompileWhereCondition(nsTemplateRule* aRule, + nsIContent* aCondition, + nsTemplateCondition** aCurrentCondition); + + /** + * Compile the <bindings> for an extended template syntax rule. + */ + nsresult + CompileBindings(nsTemplateRule* aRule, nsIContent* aBindings); + + /** + * Compile a single binding for an extended template syntax rule. + */ + nsresult + CompileBinding(nsTemplateRule* aRule, nsIContent* aBinding); + + /** + * Add automatic bindings for simple rules + */ + nsresult + AddSimpleRuleBindings(nsTemplateRule* aRule, nsIContent* aElement); + + static void + AddBindingsFor(nsXULTemplateBuilder* aSelf, + const nsAString& aVariable, + void* aClosure); + + /** + * Load the datasources for the template. shouldDelayBuilding is an out + * parameter which will be set to true to indicate that content building + * should not be performed yet as the datasource has not yet loaded. If + * false, the datasource has already loaded so building can proceed + * immediately. In the former case, the datasource or query processor + * should either rebuild the template or update results when the + * datasource is loaded as needed. + */ + nsresult + LoadDataSources(nsIDocument* aDoc, bool* shouldDelayBuilding); + + /** + * Called by LoadDataSources to load a datasource given a uri list + * in aDataSource. The list is a set of uris separated by spaces. + * If aIsRDFQuery is true, then this is for an RDF datasource which + * causes the method to check for additional flags specific to the + * RDF processor. + */ + nsresult + LoadDataSourceUrls(nsIDocument* aDocument, + const nsAString& aDataSources, + bool aIsRDFQuery, + bool* aShouldDelayBuilding); + + nsresult + InitHTMLTemplateRoot(); + + /** + * Determine which rule matches a given result. aContainer is used for + * tag matching and is optional for non-content generating builders. + * The returned matched rule is always one of the rules owned by the + * query set aQuerySet. + * + * @param aContainer parent where generated content will be inserted + * @param aResult result to match + * @param aQuerySet query set to examine the rules of + * @param aMatchedRule [out] rule that has matched, or null if any. + * @param aRuleIndex [out] index of the rule + */ + nsresult + DetermineMatchedRule(nsIContent* aContainer, + nsIXULTemplateResult* aResult, + nsTemplateQuerySet* aQuerySet, + nsTemplateRule** aMatchedRule, + int16_t *aRuleIndex); + + // XXX sigh, the string template foo doesn't mix with + // operator->*() on egcs-1.1.2, so we'll need to explicitly pass + // "this" and use good ol' fashioned static callbacks. + void + ParseAttribute(const nsAString& aAttributeValue, + void (*aVariableCallback)(nsXULTemplateBuilder* aThis, const nsAString&, void*), + void (*aTextCallback)(nsXULTemplateBuilder* aThis, const nsAString&, void*), + void* aClosure); + + nsresult + SubstituteText(nsIXULTemplateResult* aMatch, + const nsAString& aAttributeValue, + nsAString& aResult); + + static void + SubstituteTextAppendText(nsXULTemplateBuilder* aThis, const nsAString& aText, void* aClosure); + + static void + SubstituteTextReplaceVariable(nsXULTemplateBuilder* aThis, const nsAString& aVariable, void* aClosure); + + nsresult + IsSystemPrincipal(nsIPrincipal *principal, bool *result); + + /** + * Convenience method which gets a resource for a result. If a result + * doesn't have a resource set, it will create one from the result's id. + */ + nsresult GetResultResource(nsIXULTemplateResult* aResult, + nsIRDFResource** aResource); + +protected: + virtual ~nsXULTemplateBuilder(); + + nsCOMPtr<nsISupports> mDataSource; + nsCOMPtr<nsIRDFDataSource> mDB; + nsCOMPtr<nsIRDFCompositeDataSource> mCompDB; + + /** + * Circular reference, broken when the document is destroyed. + */ + nsCOMPtr<nsIContent> mRoot; + + /** + * The root result, translated from the root element's ref + */ + nsCOMPtr<nsIXULTemplateResult> mRootResult; + + nsCOMArray<nsIXULBuilderListener> mListeners; + + /** + * The query processor which generates results + */ + nsCOMPtr<nsIXULTemplateQueryProcessor> mQueryProcessor; + + /** + * The list of querysets + */ + nsTArray<nsTemplateQuerySet *> mQuerySets; + + /** + * Set to true if the rules have already been compiled + */ + bool mQueriesCompiled; + + /** + * The default reference and member variables. + */ + nsCOMPtr<nsIAtom> mRefVariable; + nsCOMPtr<nsIAtom> mMemberVariable; + + /** + * The match map contains nsTemplateMatch objects, one for each unique + * match found, keyed by the resource for that match. A particular match + * will contain a linked list of all of the matches for that unique result + * id. Only one is active at a time. When a match is retracted, look in + * the match map, remove it, and apply the next valid match in sequence to + * make active. + */ + nsDataHashtable<nsISupportsHashKey, nsTemplateMatch*> mMatchMap; + + // pseudo-constants + static nsrefcnt gRefCnt; + static nsIRDFService* gRDFService; + static nsIRDFContainerUtils* gRDFContainerUtils; + static nsIScriptSecurityManager* gScriptSecurityManager; + static nsIPrincipal* gSystemPrincipal; + static nsIObserverService* gObserverService; + + enum { + eDontTestEmpty = (1 << 0), + eDontRecurse = (1 << 1), + eLoggingEnabled = (1 << 2) + }; + + int32_t mFlags; + + /** + * Stack-based helper class to maintain a list of ``activated'' + * resources; i.e., resources for which we are currently building + * content. + */ + class ActivationEntry { + public: + nsIRDFResource *mResource; + ActivationEntry *mPrevious; + ActivationEntry **mLink; + + ActivationEntry(nsIRDFResource *aResource, ActivationEntry **aLink) + : mResource(aResource), + mPrevious(*aLink), + mLink(aLink) { *mLink = this; } + + ~ActivationEntry() { *mLink = mPrevious; } + }; + + /** + * The top of the stack of resources that we're currently building + * content for. + */ + ActivationEntry *mTop; + + /** + * Determine if a resource is currently on the activation stack. + */ + bool + IsActivated(nsIRDFResource *aResource); + + /** + * Returns true if content may be generated for a result, or false if it + * cannot, for example, if it would be created inside a closed container. + * Those results will be generated when the container is opened. + * If false is returned, no content should be generated. Possible + * insertion locations may optionally be set for new content, depending on + * the builder being used. Note that *aLocations or some items within + * aLocations may be null. + */ + virtual bool + GetInsertionLocations(nsIXULTemplateResult* aResult, + nsCOMArray<nsIContent>** aLocations) = 0; + + /** + * Must be implemented by subclasses. Handle removing the generated + * output for aOldMatch and adding new output for aNewMatch. Either + * aOldMatch or aNewMatch may be null. aContext is the location returned + * from the call to MayGenerateResult. + */ + virtual nsresult + ReplaceMatch(nsIXULTemplateResult* aOldResult, + nsTemplateMatch* aNewMatch, + nsTemplateRule* aNewMatchRule, + void *aContext) = 0; + + /** + * Must be implemented by subclasses. Handle change in bound + * variable values for aResult. aModifiedVars contains the set + * of variables that have changed. + * @param aResult the ersult for which variable bindings has changed. + * @param aModifiedVars the set of variables for which the bindings + * have changed. + */ + virtual nsresult + SynchronizeResult(nsIXULTemplateResult* aResult) = 0; + + /** + * Output a new match or removed match to the console. + * + * @param aId id of the result + * @param aMatch new or removed match + * @param aIsNew true for new matched, false for removed matches + */ + void + OutputMatchToLog(nsIRDFResource* aId, + nsTemplateMatch* aMatch, + bool aIsNew); + + virtual void Traverse(nsCycleCollectionTraversalCallback &cb) const + { + } + + /** + * Start observing events from the observer service and the given + * document. + * + * @param aDocument the document to observe + */ + void StartObserving(nsIDocument* aDocument); + + /** + * Stop observing events from the observer service and any associated + * document. + */ + void StopObserving(); + + /** + * Document that we're observing. Weak ref! + */ + nsIDocument* mObservedDocument; +}; + +#endif // nsXULTemplateBuilder_h__ |