зеркало из https://github.com/mozilla/gecko-dev.git
502 строки
16 KiB
C++
502 строки
16 KiB
C++
/* -*- 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 PRLogModuleInfo* 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);
|
|
|
|
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__
|