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