content/xul/templates/src/nsXULTemplateBuilder.h

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:d642d7ccb2bb
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #ifndef nsXULTemplateBuilder_h__
7 #define nsXULTemplateBuilder_h__
8
9 #include "nsStubDocumentObserver.h"
10 #include "nsIScriptSecurityManager.h"
11 #include "nsIObserver.h"
12 #include "nsIRDFCompositeDataSource.h"
13 #include "nsIRDFContainer.h"
14 #include "nsIRDFContainerUtils.h"
15 #include "nsIRDFDataSource.h"
16 #include "nsIRDFObserver.h"
17 #include "nsIRDFService.h"
18 #include "nsIXULTemplateBuilder.h"
19
20 #include "nsCOMArray.h"
21 #include "nsTArray.h"
22 #include "nsDataHashtable.h"
23 #include "nsTemplateRule.h"
24 #include "nsTemplateMatch.h"
25 #include "nsIXULTemplateQueryProcessor.h"
26 #include "nsCycleCollectionParticipant.h"
27
28 #include "prlog.h"
29 #ifdef PR_LOGGING
30 extern PRLogModuleInfo* gXULTemplateLog;
31 #endif
32
33 class nsIContent;
34 class nsIObserverService;
35 class nsIRDFCompositeDataSource;
36 class nsIXULDocument;
37
38 /**
39 * An object that translates an RDF graph into a presentation using a
40 * set of rules.
41 */
42 class nsXULTemplateBuilder : public nsIXULTemplateBuilder,
43 public nsIObserver,
44 public nsStubDocumentObserver
45 {
46 void CleanUp(bool aIsFinal);
47
48 public:
49 nsXULTemplateBuilder();
50 virtual ~nsXULTemplateBuilder();
51
52 nsresult InitGlobals();
53
54 /**
55 * Clear the template builder structures. The aIsFinal flag is set to true
56 * when the template is going away.
57 */
58 virtual void Uninit(bool aIsFinal);
59
60 // nsISupports interface
61 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
62 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXULTemplateBuilder,
63 nsIXULTemplateBuilder)
64
65 // nsIXULTemplateBuilder interface
66 NS_DECL_NSIXULTEMPLATEBUILDER
67
68 // nsIObserver Interface
69 NS_DECL_NSIOBSERVER
70
71 // nsIMutationObserver
72 NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
73 NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
74 NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
75
76 /**
77 * Remove an old result and/or add a new result. This method will retrieve
78 * the set of containers where the result could be inserted and either add
79 * the new result to those containers, or remove the result from those
80 * containers. UpdateResultInContainer is called for each container.
81 *
82 * @param aOldResult result to remove
83 * @param aNewResult result to add
84 * @param aQueryNode query node for new result
85 */
86 nsresult
87 UpdateResult(nsIXULTemplateResult* aOldResult,
88 nsIXULTemplateResult* aNewResult,
89 nsIDOMNode* aQueryNode);
90
91 /**
92 * Remove an old result and/or add a new result from a specific container.
93 *
94 * @param aOldResult result to remove
95 * @param aNewResult result to add
96 * @param aQueryNode queryset for the new result
97 * @param aOldId id of old result
98 * @param aNewId id of new result
99 * @param aInsertionPoint container to remove or add result inside
100 */
101 nsresult
102 UpdateResultInContainer(nsIXULTemplateResult* aOldResult,
103 nsIXULTemplateResult* aNewResult,
104 nsTemplateQuerySet* aQuerySet,
105 nsIRDFResource* aOldId,
106 nsIRDFResource* aNewId,
107 nsIContent* aInsertionPoint);
108
109 nsresult
110 ComputeContainmentProperties();
111
112 static bool
113 IsTemplateElement(nsIContent* aContent);
114
115 virtual nsresult
116 RebuildAll() = 0; // must be implemented by subclasses
117
118 void RunnableRebuild() { Rebuild(); }
119 void RunnableLoadAndRebuild() {
120 Uninit(false); // Reset results
121
122 nsCOMPtr<nsIDocument> doc = mRoot ? mRoot->GetDocument() : nullptr;
123 if (doc) {
124 bool shouldDelay;
125 LoadDataSources(doc, &shouldDelay);
126 if (!shouldDelay) {
127 Rebuild();
128 }
129 }
130 }
131
132 // mRoot should not be cleared until after Uninit is finished so that
133 // generated content can be removed during uninitialization.
134 void UninitFalse() { Uninit(false); mRoot = nullptr; }
135 void UninitTrue() { Uninit(true); mRoot = nullptr; }
136
137 /**
138 * Find the <template> tag that applies for this builder
139 */
140 nsresult
141 GetTemplateRoot(nsIContent** aResult);
142
143 /**
144 * Compile the template's queries
145 */
146 nsresult
147 CompileQueries();
148
149 /**
150 * Compile the template given a <template> in aTemplate. This function
151 * is called recursively to handle queries inside a queryset. For the
152 * outer pass, aIsQuerySet will be false, while the inner pass this will
153 * be true.
154 *
155 * aCanUseTemplate will be set to true if the template's queries could be
156 * compiled, and false otherwise. If false, the entire template is
157 * invalid.
158 *
159 * @param aTemplate <template> to compile
160 * @param aQuerySet first queryset
161 * @param aIsQuerySet true if
162 * @param aPriority the queryset index, incremented when a new one is added
163 * @param aCanUseTemplate true if template is valid
164 */
165 nsresult
166 CompileTemplate(nsIContent* aTemplate,
167 nsTemplateQuerySet* aQuerySet,
168 bool aIsQuerySet,
169 int32_t* aPriority,
170 bool* aCanUseTemplate);
171
172 /**
173 * Compile a query using the extended syntax. For backwards compatible RDF
174 * syntax where there is no <query>, the <conditions> becomes the query.
175 *
176 * @param aRuleElement <rule> element
177 * @param aActionElement <action> element
178 * @param aMemberVariable member variable for the query
179 * @param aQuerySet the queryset
180 */
181 nsresult
182 CompileExtendedQuery(nsIContent* aRuleElement,
183 nsIContent* aActionElement,
184 nsIAtom* aMemberVariable,
185 nsTemplateQuerySet* aQuerySet);
186
187 /**
188 * Determine the ref variable and tag from inside a RDF query.
189 */
190 void DetermineRDFQueryRef(nsIContent* aQueryElement, nsIAtom** tag);
191
192 /**
193 * Determine the member variable from inside an action body. It will be
194 * the value of the uri attribute on a node.
195 */
196 already_AddRefed<nsIAtom> DetermineMemberVariable(nsIContent* aElement);
197
198 /**
199 * Compile a simple query. A simple query is one that doesn't have a
200 * <query> and should use a default query which would normally just return
201 * a list of children of the reference point.
202 *
203 * @param aRuleElement the <rule>
204 * @param aQuerySet the query set
205 * @param aCanUseTemplate true if the query is valid
206 */
207 nsresult
208 CompileSimpleQuery(nsIContent* aRuleElement,
209 nsTemplateQuerySet* aQuerySet,
210 bool* aCanUseTemplate);
211
212 /**
213 * Compile the <conditions> tag in a rule
214 *
215 * @param aRule template rule
216 * @param aConditions <conditions> element
217 */
218 nsresult
219 CompileConditions(nsTemplateRule* aRule, nsIContent* aConditions);
220
221 /**
222 * Compile a <where> tag in a condition. The caller should set
223 * *aCurrentCondition to null for the first condition. This value will be
224 * updated to point to the new condition before returning. The conditions
225 * will be added to the rule aRule by this method.
226 *
227 * @param aRule template rule
228 * @param aCondition <where> element
229 * @param aCurrentCondition compiled condition
230 */
231 nsresult
232 CompileWhereCondition(nsTemplateRule* aRule,
233 nsIContent* aCondition,
234 nsTemplateCondition** aCurrentCondition);
235
236 /**
237 * Compile the <bindings> for an extended template syntax rule.
238 */
239 nsresult
240 CompileBindings(nsTemplateRule* aRule, nsIContent* aBindings);
241
242 /**
243 * Compile a single binding for an extended template syntax rule.
244 */
245 nsresult
246 CompileBinding(nsTemplateRule* aRule, nsIContent* aBinding);
247
248 /**
249 * Add automatic bindings for simple rules
250 */
251 nsresult
252 AddSimpleRuleBindings(nsTemplateRule* aRule, nsIContent* aElement);
253
254 static void
255 AddBindingsFor(nsXULTemplateBuilder* aSelf,
256 const nsAString& aVariable,
257 void* aClosure);
258
259 /**
260 * Load the datasources for the template. shouldDelayBuilding is an out
261 * parameter which will be set to true to indicate that content building
262 * should not be performed yet as the datasource has not yet loaded. If
263 * false, the datasource has already loaded so building can proceed
264 * immediately. In the former case, the datasource or query processor
265 * should either rebuild the template or update results when the
266 * datasource is loaded as needed.
267 */
268 nsresult
269 LoadDataSources(nsIDocument* aDoc, bool* shouldDelayBuilding);
270
271 /**
272 * Called by LoadDataSources to load a datasource given a uri list
273 * in aDataSource. The list is a set of uris separated by spaces.
274 * If aIsRDFQuery is true, then this is for an RDF datasource which
275 * causes the method to check for additional flags specific to the
276 * RDF processor.
277 */
278 nsresult
279 LoadDataSourceUrls(nsIDocument* aDocument,
280 const nsAString& aDataSources,
281 bool aIsRDFQuery,
282 bool* aShouldDelayBuilding);
283
284 nsresult
285 InitHTMLTemplateRoot();
286
287 /**
288 * Determine which rule matches a given result. aContainer is used for
289 * tag matching and is optional for non-content generating builders.
290 * The returned matched rule is always one of the rules owned by the
291 * query set aQuerySet.
292 *
293 * @param aContainer parent where generated content will be inserted
294 * @param aResult result to match
295 * @param aQuerySet query set to examine the rules of
296 * @param aMatchedRule [out] rule that has matched, or null if any.
297 * @param aRuleIndex [out] index of the rule
298 */
299 nsresult
300 DetermineMatchedRule(nsIContent* aContainer,
301 nsIXULTemplateResult* aResult,
302 nsTemplateQuerySet* aQuerySet,
303 nsTemplateRule** aMatchedRule,
304 int16_t *aRuleIndex);
305
306 // XXX sigh, the string template foo doesn't mix with
307 // operator->*() on egcs-1.1.2, so we'll need to explicitly pass
308 // "this" and use good ol' fashioned static callbacks.
309 void
310 ParseAttribute(const nsAString& aAttributeValue,
311 void (*aVariableCallback)(nsXULTemplateBuilder* aThis, const nsAString&, void*),
312 void (*aTextCallback)(nsXULTemplateBuilder* aThis, const nsAString&, void*),
313 void* aClosure);
314
315 nsresult
316 SubstituteText(nsIXULTemplateResult* aMatch,
317 const nsAString& aAttributeValue,
318 nsAString& aResult);
319
320 static void
321 SubstituteTextAppendText(nsXULTemplateBuilder* aThis, const nsAString& aText, void* aClosure);
322
323 static void
324 SubstituteTextReplaceVariable(nsXULTemplateBuilder* aThis, const nsAString& aVariable, void* aClosure);
325
326 nsresult
327 IsSystemPrincipal(nsIPrincipal *principal, bool *result);
328
329 /**
330 * Convenience method which gets a resource for a result. If a result
331 * doesn't have a resource set, it will create one from the result's id.
332 */
333 nsresult GetResultResource(nsIXULTemplateResult* aResult,
334 nsIRDFResource** aResource);
335
336 protected:
337 nsCOMPtr<nsISupports> mDataSource;
338 nsCOMPtr<nsIRDFDataSource> mDB;
339 nsCOMPtr<nsIRDFCompositeDataSource> mCompDB;
340
341 /**
342 * Circular reference, broken when the document is destroyed.
343 */
344 nsCOMPtr<nsIContent> mRoot;
345
346 /**
347 * The root result, translated from the root element's ref
348 */
349 nsCOMPtr<nsIXULTemplateResult> mRootResult;
350
351 nsCOMArray<nsIXULBuilderListener> mListeners;
352
353 /**
354 * The query processor which generates results
355 */
356 nsCOMPtr<nsIXULTemplateQueryProcessor> mQueryProcessor;
357
358 /**
359 * The list of querysets
360 */
361 nsTArray<nsTemplateQuerySet *> mQuerySets;
362
363 /**
364 * Set to true if the rules have already been compiled
365 */
366 bool mQueriesCompiled;
367
368 /**
369 * The default reference and member variables.
370 */
371 nsCOMPtr<nsIAtom> mRefVariable;
372 nsCOMPtr<nsIAtom> mMemberVariable;
373
374 /**
375 * The match map contains nsTemplateMatch objects, one for each unique
376 * match found, keyed by the resource for that match. A particular match
377 * will contain a linked list of all of the matches for that unique result
378 * id. Only one is active at a time. When a match is retracted, look in
379 * the match map, remove it, and apply the next valid match in sequence to
380 * make active.
381 */
382 nsDataHashtable<nsISupportsHashKey, nsTemplateMatch*> mMatchMap;
383
384 // pseudo-constants
385 static nsrefcnt gRefCnt;
386 static nsIRDFService* gRDFService;
387 static nsIRDFContainerUtils* gRDFContainerUtils;
388 static nsIScriptSecurityManager* gScriptSecurityManager;
389 static nsIPrincipal* gSystemPrincipal;
390 static nsIObserverService* gObserverService;
391
392 enum {
393 eDontTestEmpty = (1 << 0),
394 eDontRecurse = (1 << 1),
395 eLoggingEnabled = (1 << 2)
396 };
397
398 int32_t mFlags;
399
400 /**
401 * Stack-based helper class to maintain a list of ``activated''
402 * resources; i.e., resources for which we are currently building
403 * content.
404 */
405 class ActivationEntry {
406 public:
407 nsIRDFResource *mResource;
408 ActivationEntry *mPrevious;
409 ActivationEntry **mLink;
410
411 ActivationEntry(nsIRDFResource *aResource, ActivationEntry **aLink)
412 : mResource(aResource),
413 mPrevious(*aLink),
414 mLink(aLink) { *mLink = this; }
415
416 ~ActivationEntry() { *mLink = mPrevious; }
417 };
418
419 /**
420 * The top of the stack of resources that we're currently building
421 * content for.
422 */
423 ActivationEntry *mTop;
424
425 /**
426 * Determine if a resource is currently on the activation stack.
427 */
428 bool
429 IsActivated(nsIRDFResource *aResource);
430
431 /**
432 * Returns true if content may be generated for a result, or false if it
433 * cannot, for example, if it would be created inside a closed container.
434 * Those results will be generated when the container is opened.
435 * If false is returned, no content should be generated. Possible
436 * insertion locations may optionally be set for new content, depending on
437 * the builder being used. Note that *aLocations or some items within
438 * aLocations may be null.
439 */
440 virtual bool
441 GetInsertionLocations(nsIXULTemplateResult* aResult,
442 nsCOMArray<nsIContent>** aLocations) = 0;
443
444 /**
445 * Must be implemented by subclasses. Handle removing the generated
446 * output for aOldMatch and adding new output for aNewMatch. Either
447 * aOldMatch or aNewMatch may be null. aContext is the location returned
448 * from the call to MayGenerateResult.
449 */
450 virtual nsresult
451 ReplaceMatch(nsIXULTemplateResult* aOldResult,
452 nsTemplateMatch* aNewMatch,
453 nsTemplateRule* aNewMatchRule,
454 void *aContext) = 0;
455
456 /**
457 * Must be implemented by subclasses. Handle change in bound
458 * variable values for aResult. aModifiedVars contains the set
459 * of variables that have changed.
460 * @param aResult the ersult for which variable bindings has changed.
461 * @param aModifiedVars the set of variables for which the bindings
462 * have changed.
463 */
464 virtual nsresult
465 SynchronizeResult(nsIXULTemplateResult* aResult) = 0;
466
467 /**
468 * Output a new match or removed match to the console.
469 *
470 * @param aId id of the result
471 * @param aMatch new or removed match
472 * @param aIsNew true for new matched, false for removed matches
473 */
474 void
475 OutputMatchToLog(nsIRDFResource* aId,
476 nsTemplateMatch* aMatch,
477 bool aIsNew);
478
479 virtual void Traverse(nsCycleCollectionTraversalCallback &cb) const
480 {
481 }
482
483 /**
484 * Start observing events from the observer service and the given
485 * document.
486 *
487 * @param aDocument the document to observe
488 */
489 void StartObserving(nsIDocument* aDocument);
490
491 /**
492 * Stop observing events from the observer service and any associated
493 * document.
494 */
495 void StopObserving();
496
497 /**
498 * Document that we're observing. Weak ref!
499 */
500 nsIDocument* mObservedDocument;
501 };
502
503 #endif // nsXULTemplateBuilder_h__

mercurial