1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/xul/templates/src/nsXULContentBuilder.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1988 @@ 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 +#include "mozilla/ArrayUtils.h" 1.10 + 1.11 +#include "nsContentCID.h" 1.12 +#include "nsIDocument.h" 1.13 +#include "nsIDOMNodeList.h" 1.14 +#include "nsIDOMXULDocument.h" 1.15 +#include "nsINodeInfo.h" 1.16 +#include "nsIServiceManager.h" 1.17 +#include "nsIXULDocument.h" 1.18 + 1.19 +#include "nsContentSupportMap.h" 1.20 +#include "nsRDFConMemberTestNode.h" 1.21 +#include "nsRDFPropertyTestNode.h" 1.22 +#include "nsXULSortService.h" 1.23 +#include "nsTemplateRule.h" 1.24 +#include "nsTemplateMap.h" 1.25 +#include "nsTArray.h" 1.26 +#include "nsXPIDLString.h" 1.27 +#include "nsGkAtoms.h" 1.28 +#include "nsXULContentUtils.h" 1.29 +#include "nsXULElement.h" 1.30 +#include "nsXULTemplateBuilder.h" 1.31 +#include "nsNodeInfoManager.h" 1.32 +#include "nsContentCreatorFunctions.h" 1.33 +#include "nsContentUtils.h" 1.34 +#include "nsAttrName.h" 1.35 +#include "nsNodeUtils.h" 1.36 +#include "mozAutoDocUpdate.h" 1.37 +#include "nsTextNode.h" 1.38 +#include "mozilla/dom/Element.h" 1.39 + 1.40 +#include "pldhash.h" 1.41 +#include "rdf.h" 1.42 + 1.43 +using namespace mozilla; 1.44 +using namespace mozilla::dom; 1.45 + 1.46 +//---------------------------------------------------------------------- 1.47 +// 1.48 +// Return values for EnsureElementHasGenericChild() 1.49 +// 1.50 +#define NS_ELEMENT_GOT_CREATED NS_RDF_NO_VALUE 1.51 +#define NS_ELEMENT_WAS_THERE NS_OK 1.52 + 1.53 +//---------------------------------------------------------------------- 1.54 +// 1.55 +// nsXULContentBuilder 1.56 +// 1.57 + 1.58 +/** 1.59 + * The content builder generates DOM nodes from a template. The actual content 1.60 + * generation is done entirely inside BuildContentFromTemplate. 1.61 + * 1.62 + * Content generation is centered around the generation node (the node with 1.63 + * uri="?member" on it). Nodes above the generation node are unique and 1.64 + * generated only once. BuildContentFromTemplate will be passed the unique 1.65 + * flag as an argument for content at this point and will recurse until it 1.66 + * finds the generation node. 1.67 + * 1.68 + * Once the generation node has been found, the results for that content node 1.69 + * are added to the content map, stored in mContentSupportMap. 1.70 + * 1.71 + * If recursion is allowed, generation continues, where the generation node 1.72 + * becomes the container to insert into. 1.73 + */ 1.74 +class nsXULContentBuilder : public nsXULTemplateBuilder 1.75 +{ 1.76 +public: 1.77 + // nsIXULTemplateBuilder interface 1.78 + NS_IMETHOD CreateContents(nsIContent* aElement, bool aForceCreation); 1.79 + 1.80 + NS_IMETHOD HasGeneratedContent(nsIRDFResource* aResource, 1.81 + nsIAtom* aTag, 1.82 + bool* aGenerated); 1.83 + 1.84 + NS_IMETHOD GetResultForContent(nsIDOMElement* aContent, 1.85 + nsIXULTemplateResult** aResult); 1.86 + 1.87 + // nsIMutationObserver interface 1.88 + NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED 1.89 + NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED 1.90 + 1.91 +protected: 1.92 + friend nsresult 1.93 + NS_NewXULContentBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult); 1.94 + 1.95 + nsXULContentBuilder(); 1.96 + 1.97 + void Traverse(nsCycleCollectionTraversalCallback &cb) const 1.98 + { 1.99 + mSortState.Traverse(cb); 1.100 + } 1.101 + 1.102 + virtual void Uninit(bool aIsFinal); 1.103 + 1.104 + // Implementation methods 1.105 + nsresult 1.106 + OpenContainer(nsIContent* aElement); 1.107 + 1.108 + nsresult 1.109 + CloseContainer(nsIContent* aElement); 1.110 + 1.111 + /** 1.112 + * Build content from a template for a given result. This will be called 1.113 + * recursively or on demand and will be called for every node in the 1.114 + * generated content tree. 1.115 + */ 1.116 + nsresult 1.117 + BuildContentFromTemplate(nsIContent *aTemplateNode, 1.118 + nsIContent *aResourceNode, 1.119 + nsIContent *aRealNode, 1.120 + bool aIsUnique, 1.121 + bool aIsSelfReference, 1.122 + nsIXULTemplateResult* aChild, 1.123 + bool aNotify, 1.124 + nsTemplateMatch* aMatch, 1.125 + nsIContent** aContainer, 1.126 + int32_t* aNewIndexInContainer); 1.127 + 1.128 + /** 1.129 + * Copy the attributes from the template node to the node generated 1.130 + * from it, performing any substitutions. 1.131 + * 1.132 + * @param aTemplateNode node within template 1.133 + * @param aRealNode generated node to set attibutes upon 1.134 + * @param aResult result to look up variable->value bindings in 1.135 + * @param aNotify true to notify of DOM changes 1.136 + */ 1.137 + nsresult 1.138 + CopyAttributesToElement(nsIContent* aTemplateNode, 1.139 + nsIContent* aRealNode, 1.140 + nsIXULTemplateResult* aResult, 1.141 + bool aNotify); 1.142 + 1.143 + /** 1.144 + * Add any necessary persistent attributes (persist="...") from the 1.145 + * local store to a generated node. 1.146 + * 1.147 + * @param aTemplateNode node within template 1.148 + * @param aRealNode generated node to set persisted attibutes upon 1.149 + * @param aResult result to look up variable->value bindings in 1.150 + */ 1.151 + nsresult 1.152 + AddPersistentAttributes(Element* aTemplateNode, 1.153 + nsIXULTemplateResult* aResult, 1.154 + nsIContent* aRealNode); 1.155 + 1.156 + /** 1.157 + * Recalculate any attributes that have variable references. This will 1.158 + * be called when a binding has been changed to update the attributes. 1.159 + * The attributes are copied from the node aTemplateNode in the template 1.160 + * to the generated node aRealNode, using the values from the result 1.161 + * aResult. This method will operate recursively. 1.162 + * 1.163 + * @param aTemplateNode node within template 1.164 + * @param aRealNode generated node to set attibutes upon 1.165 + * @param aResult result to look up variable->value bindings in 1.166 + */ 1.167 + nsresult 1.168 + SynchronizeUsingTemplate(nsIContent *aTemplateNode, 1.169 + nsIContent* aRealNode, 1.170 + nsIXULTemplateResult* aResult); 1.171 + 1.172 + /** 1.173 + * Remove the generated node aContent from the DOM and the hashtables 1.174 + * used by the content builder. 1.175 + */ 1.176 + nsresult 1.177 + RemoveMember(nsIContent* aContent); 1.178 + 1.179 + /** 1.180 + * Create the appropriate generated content for aElement, by calling 1.181 + * CreateContainerContents. 1.182 + * 1.183 + * @param aElement element to generate content inside 1.184 + * @param aForceCreation true to force creation for closed items such as menus 1.185 + */ 1.186 + nsresult 1.187 + CreateTemplateAndContainerContents(nsIContent* aElement, 1.188 + bool aForceCreation); 1.189 + 1.190 + /** 1.191 + * Generate the results for a template, by calling 1.192 + * CreateContainerContentsForQuerySet for each queryset. 1.193 + * 1.194 + * @param aElement element to generate content inside 1.195 + * @param aResult reference point for query 1.196 + * @param aForceCreation true to force creation for closed items such as menus 1.197 + * @param aNotify true to notify of DOM changes as each element is inserted 1.198 + * @param aNotifyAtEnd notify at the end of all DOM changes 1.199 + */ 1.200 + nsresult 1.201 + CreateContainerContents(nsIContent* aElement, 1.202 + nsIXULTemplateResult* aResult, 1.203 + bool aForceCreation, 1.204 + bool aNotify, 1.205 + bool aNotifyAtEnd); 1.206 + 1.207 + /** 1.208 + * Generate the results for a query. 1.209 + * 1.210 + * @param aElement element to generate content inside 1.211 + * @param aResult reference point for query 1.212 + * @param aNotify true to notify of DOM changes 1.213 + * @param aContainer container content was added inside 1.214 + * @param aNewIndexInContainer index with container in which content was added 1.215 + */ 1.216 + nsresult 1.217 + CreateContainerContentsForQuerySet(nsIContent* aElement, 1.218 + nsIXULTemplateResult* aResult, 1.219 + bool aNotify, 1.220 + nsTemplateQuerySet* aQuerySet, 1.221 + nsIContent** aContainer, 1.222 + int32_t* aNewIndexInContainer); 1.223 + 1.224 + /** 1.225 + * Check if an element with a particular tag exists with a container. 1.226 + * If it is not present, append a new element with that tag into the 1.227 + * container. 1.228 + * 1.229 + * @param aParent parent container 1.230 + * @param aNameSpaceID namespace of tag to locate or create 1.231 + * @param aTag tag to locate or create 1.232 + * @param aNotify true to notify of DOM changes 1.233 + * @param aResult set to the found or created node. 1.234 + */ 1.235 + nsresult 1.236 + EnsureElementHasGenericChild(nsIContent* aParent, 1.237 + int32_t aNameSpaceID, 1.238 + nsIAtom* aTag, 1.239 + bool aNotify, 1.240 + nsIContent** aResult); 1.241 + 1.242 + bool 1.243 + IsOpen(nsIContent* aElement); 1.244 + 1.245 + nsresult 1.246 + RemoveGeneratedContent(nsIContent* aElement); 1.247 + 1.248 + nsresult 1.249 + GetElementsForResult(nsIXULTemplateResult* aResult, 1.250 + nsCOMArray<nsIContent>& aElements); 1.251 + 1.252 + nsresult 1.253 + CreateElement(int32_t aNameSpaceID, 1.254 + nsIAtom* aTag, 1.255 + Element** aResult); 1.256 + 1.257 + /** 1.258 + * Set the container and empty attributes on a node. If 1.259 + * aIgnoreNonContainers is true, then the element is not changed 1.260 + * for non-containers. Otherwise, the container attribute will be set to 1.261 + * false. 1.262 + * 1.263 + * @param aElement element to set attributes on 1.264 + * @param aResult result to use to determine state of attributes 1.265 + * @param aIgnoreNonContainers true to not change for non-containers 1.266 + * @param aNotify true to notify of DOM changes 1.267 + */ 1.268 + nsresult 1.269 + SetContainerAttrs(nsIContent *aElement, 1.270 + nsIXULTemplateResult* aResult, 1.271 + bool aIgnoreNonContainers, 1.272 + bool aNotify); 1.273 + 1.274 + virtual nsresult 1.275 + RebuildAll(); 1.276 + 1.277 + // GetInsertionLocations, ReplaceMatch and SynchronizeResult are inherited 1.278 + // from nsXULTemplateBuilder 1.279 + 1.280 + /** 1.281 + * Return true if the result can be inserted into the template as 1.282 + * generated content. For the content builder, aLocations will be set 1.283 + * to the list of containers where the content should be inserted. 1.284 + */ 1.285 + virtual bool 1.286 + GetInsertionLocations(nsIXULTemplateResult* aOldResult, 1.287 + nsCOMArray<nsIContent>** aLocations); 1.288 + 1.289 + /** 1.290 + * Remove the content associated with aOldResult which no longer matches, 1.291 + * and/or generate content for a new match. 1.292 + */ 1.293 + virtual nsresult 1.294 + ReplaceMatch(nsIXULTemplateResult* aOldResult, 1.295 + nsTemplateMatch* aNewMatch, 1.296 + nsTemplateRule* aNewMatchRule, 1.297 + void *aContext); 1.298 + 1.299 + /** 1.300 + * Synchronize a result bindings with the generated content for that 1.301 + * result. This will be called as a result of the template builder's 1.302 + * ResultBindingChanged method. 1.303 + */ 1.304 + virtual nsresult 1.305 + SynchronizeResult(nsIXULTemplateResult* aResult); 1.306 + 1.307 + /** 1.308 + * Compare a result to a content node. If the generated content for the 1.309 + * result should come before aContent, set aSortOrder to -1. If it should 1.310 + * come after, set sortOrder to 1. If both are equal, set to 0. 1.311 + */ 1.312 + nsresult 1.313 + CompareResultToNode(nsIXULTemplateResult* aResult, nsIContent* aContent, 1.314 + int32_t* aSortOrder); 1.315 + 1.316 + /** 1.317 + * Insert a generated node into the container where it should go according 1.318 + * to the current sort. aNode is the generated content node and aResult is 1.319 + * the result for the generated node. 1.320 + */ 1.321 + nsresult 1.322 + InsertSortedNode(nsIContent* aContainer, 1.323 + nsIContent* aNode, 1.324 + nsIXULTemplateResult* aResult, 1.325 + bool aNotify); 1.326 + 1.327 + /** 1.328 + * Maintains a mapping between elements in the DOM and the matches 1.329 + * that they support. 1.330 + */ 1.331 + nsContentSupportMap mContentSupportMap; 1.332 + 1.333 + /** 1.334 + * Maintains a mapping from an element in the DOM to the template 1.335 + * element that it was created from. 1.336 + */ 1.337 + nsTemplateMap mTemplateMap; 1.338 + 1.339 + /** 1.340 + * Information about the currently active sort 1.341 + */ 1.342 + nsSortState mSortState; 1.343 +}; 1.344 + 1.345 +nsresult 1.346 +NS_NewXULContentBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult) 1.347 +{ 1.348 + NS_PRECONDITION(aOuter == nullptr, "no aggregation"); 1.349 + if (aOuter) 1.350 + return NS_ERROR_NO_AGGREGATION; 1.351 + 1.352 + nsresult rv; 1.353 + nsXULContentBuilder* result = new nsXULContentBuilder(); 1.354 + if (!result) 1.355 + return NS_ERROR_OUT_OF_MEMORY; 1.356 + 1.357 + NS_ADDREF(result); // stabilize 1.358 + 1.359 + rv = result->InitGlobals(); 1.360 + 1.361 + if (NS_SUCCEEDED(rv)) 1.362 + rv = result->QueryInterface(aIID, aResult); 1.363 + 1.364 + NS_RELEASE(result); 1.365 + return rv; 1.366 +} 1.367 + 1.368 +nsXULContentBuilder::nsXULContentBuilder() 1.369 +{ 1.370 + mSortState.initialized = false; 1.371 +} 1.372 + 1.373 +void 1.374 +nsXULContentBuilder::Uninit(bool aIsFinal) 1.375 +{ 1.376 + if (! aIsFinal && mRoot) { 1.377 + nsresult rv = RemoveGeneratedContent(mRoot); 1.378 + if (NS_FAILED(rv)) 1.379 + return; 1.380 + } 1.381 + 1.382 + // Nuke the content support map completely. 1.383 + mContentSupportMap.Clear(); 1.384 + mTemplateMap.Clear(); 1.385 + 1.386 + mSortState.initialized = false; 1.387 + 1.388 + nsXULTemplateBuilder::Uninit(aIsFinal); 1.389 +} 1.390 + 1.391 +nsresult 1.392 +nsXULContentBuilder::BuildContentFromTemplate(nsIContent *aTemplateNode, 1.393 + nsIContent *aResourceNode, 1.394 + nsIContent *aRealNode, 1.395 + bool aIsUnique, 1.396 + bool aIsSelfReference, 1.397 + nsIXULTemplateResult* aChild, 1.398 + bool aNotify, 1.399 + nsTemplateMatch* aMatch, 1.400 + nsIContent** aContainer, 1.401 + int32_t* aNewIndexInContainer) 1.402 +{ 1.403 + // This is the mother lode. Here is where we grovel through an 1.404 + // element in the template, copying children from the template 1.405 + // into the "real" content tree, performing substitution as we go 1.406 + // by looking stuff up using the results. 1.407 + // 1.408 + // |aTemplateNode| is the element in the "template tree", whose 1.409 + // children we will duplicate and move into the "real" content 1.410 + // tree. 1.411 + // 1.412 + // |aResourceNode| is the element in the "real" content tree that 1.413 + // has the "id" attribute set to an result's id. This is 1.414 + // not directly used here, but rather passed down to the XUL 1.415 + // sort service to perform container-level sort. 1.416 + // 1.417 + // |aRealNode| is the element in the "real" content tree to which 1.418 + // the new elements will be copied. 1.419 + // 1.420 + // |aIsUnique| is set to "true" so long as content has been 1.421 + // "unique" (or "above" the resource element) so far in the 1.422 + // template. 1.423 + // 1.424 + // |aIsSelfReference| should be set to "true" for cases where 1.425 + // the reference and member variables are the same, indicating 1.426 + // that the generated node is the same as the reference point, 1.427 + // so generation should not recurse, or else an infinite loop 1.428 + // would occur. 1.429 + // 1.430 + // |aChild| is the result for which we are building content. 1.431 + // 1.432 + // |aNotify| is set to "true" if content should be constructed 1.433 + // "noisily"; that is, whether the document observers should be 1.434 + // notified when new content is added to the content model. 1.435 + // 1.436 + // |aContainer| is an out parameter that will be set to the first 1.437 + // container element in the "real" content tree to which content 1.438 + // was appended. 1.439 + // 1.440 + // |aNewIndexInContainer| is an out parameter that will be set to 1.441 + // the index in aContainer at which new content is first 1.442 + // constructed. 1.443 + // 1.444 + // If |aNotify| is "false", then |aContainer| and 1.445 + // |aNewIndexInContainer| are used to determine where in the 1.446 + // content model new content is constructed. This allows a single 1.447 + // notification to be propagated to document observers. 1.448 + // 1.449 + 1.450 + nsresult rv; 1.451 + 1.452 +#ifdef PR_LOGGING 1.453 + if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { 1.454 + PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS, 1.455 + ("nsXULContentBuilder::BuildContentFromTemplate (is unique: %d)", 1.456 + aIsUnique)); 1.457 + 1.458 + nsAutoString id; 1.459 + aChild->GetId(id); 1.460 + 1.461 + PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS, 1.462 + ("Tags: [Template: %s Resource: %s Real: %s] for id %s", 1.463 + nsAtomCString(aTemplateNode->Tag()).get(), 1.464 + nsAtomCString(aResourceNode->Tag()).get(), 1.465 + nsAtomCString(aRealNode->Tag()).get(), NS_ConvertUTF16toUTF8(id).get())); 1.466 + } 1.467 +#endif 1.468 + 1.469 + // Iterate through all of the template children, constructing 1.470 + // "real" content model nodes for each "template" child. 1.471 + for (nsIContent* tmplKid = aTemplateNode->GetFirstChild(); 1.472 + tmplKid; 1.473 + tmplKid = tmplKid->GetNextSibling()) { 1.474 + 1.475 + int32_t nameSpaceID = tmplKid->GetNameSpaceID(); 1.476 + 1.477 + // Check whether this element is the generation element. The generation 1.478 + // element is the element that is cookie-cutter copied once for each 1.479 + // different result specified by |aChild|. 1.480 + // 1.481 + // Nodes that appear -above- the generation element 1.482 + // (that is, are ancestors of the generation element in the 1.483 + // content model) are unique across all values of |aChild|, 1.484 + // and are created only once. 1.485 + // 1.486 + // Nodes that appear -below- the generation element (that is, 1.487 + // are descendants of the generation element in the content 1.488 + // model), are cookie-cutter copied for each distinct value of 1.489 + // |aChild|. 1.490 + // 1.491 + // For example, in a <tree> template: 1.492 + // 1.493 + // <tree> 1.494 + // <template> 1.495 + // <treechildren> [1] 1.496 + // <treeitem uri="rdf:*"> [2] 1.497 + // <treerow> [3] 1.498 + // <treecell value="rdf:urn:foo" /> [4] 1.499 + // <treecell value="rdf:urn:bar" /> [5] 1.500 + // </treerow> 1.501 + // </treeitem> 1.502 + // </treechildren> 1.503 + // </template> 1.504 + // </tree> 1.505 + // 1.506 + // The <treeitem> element [2] is the generation element. This 1.507 + // element, and all of its descendants ([3], [4], and [5]) 1.508 + // will be duplicated for each different |aChild|. 1.509 + // It's ancestor <treechildren> [1] is unique, and 1.510 + // will only be created -once-, no matter how many <treeitem>s 1.511 + // are created below it. 1.512 + // 1.513 + // isUnique will be true for nodes above the generation element, 1.514 + // isGenerationElement will be true for the generation element, 1.515 + // and both will be false for descendants 1.516 + bool isGenerationElement = false; 1.517 + bool isUnique = aIsUnique; 1.518 + 1.519 + // We identify the resource element by presence of a 1.520 + // "uri='rdf:*'" attribute. (We also support the older 1.521 + // "uri='...'" syntax.) 1.522 + if (tmplKid->HasAttr(kNameSpaceID_None, nsGkAtoms::uri) && aMatch->IsActive()) { 1.523 + isGenerationElement = true; 1.524 + isUnique = false; 1.525 + } 1.526 + 1.527 + MOZ_ASSERT_IF(isGenerationElement, tmplKid->IsElement()); 1.528 + 1.529 + nsIAtom *tag = tmplKid->Tag(); 1.530 + 1.531 +#ifdef PR_LOGGING 1.532 + if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { 1.533 + PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, 1.534 + ("xultemplate[%p] building %s %s %s", 1.535 + this, nsAtomCString(tag).get(), 1.536 + (isGenerationElement ? "[resource]" : ""), 1.537 + (isUnique ? "[unique]" : ""))); 1.538 + } 1.539 +#endif 1.540 + 1.541 + // Set to true if the child we're trying to create now 1.542 + // already existed in the content model. 1.543 + bool realKidAlreadyExisted = false; 1.544 + 1.545 + nsCOMPtr<nsIContent> realKid; 1.546 + if (isUnique) { 1.547 + // The content is "unique"; that is, we haven't descended 1.548 + // far enough into the template to hit the generation 1.549 + // element yet. |EnsureElementHasGenericChild()| will 1.550 + // conditionally create the element iff it isn't there 1.551 + // already. 1.552 + rv = EnsureElementHasGenericChild(aRealNode, nameSpaceID, tag, aNotify, getter_AddRefs(realKid)); 1.553 + if (NS_FAILED(rv)) 1.554 + return rv; 1.555 + 1.556 + if (rv == NS_ELEMENT_WAS_THERE) { 1.557 + realKidAlreadyExisted = true; 1.558 + } 1.559 + else { 1.560 + // Potentially remember the index of this element as the first 1.561 + // element that we've generated. Note that we remember 1.562 + // this -before- we recurse! 1.563 + if (aContainer && !*aContainer) { 1.564 + *aContainer = aRealNode; 1.565 + NS_ADDREF(*aContainer); 1.566 + 1.567 + uint32_t indx = aRealNode->GetChildCount(); 1.568 + 1.569 + // Since EnsureElementHasGenericChild() added us, make 1.570 + // sure to subtract one for our real index. 1.571 + *aNewIndexInContainer = indx - 1; 1.572 + } 1.573 + } 1.574 + 1.575 + // Recurse until we get to the resource element. Since 1.576 + // -we're- unique, assume that our child will be 1.577 + // unique. The check for the "resource" element at the top 1.578 + // of the function will trip this to |false| as soon as we 1.579 + // encounter it. 1.580 + rv = BuildContentFromTemplate(tmplKid, aResourceNode, realKid, true, 1.581 + aIsSelfReference, aChild, aNotify, aMatch, 1.582 + aContainer, aNewIndexInContainer); 1.583 + 1.584 + if (NS_FAILED(rv)) 1.585 + return rv; 1.586 + } 1.587 + else if (isGenerationElement) { 1.588 + // It's the "resource" element. Create a new element using 1.589 + // the namespace ID and tag from the template element. 1.590 + nsCOMPtr<Element> element; 1.591 + rv = CreateElement(nameSpaceID, tag, getter_AddRefs(element)); 1.592 + if (NS_FAILED(rv)) 1.593 + return rv; 1.594 + realKid = element.forget(); 1.595 + 1.596 + // Add the resource element to the content support map so 1.597 + // we can remove the match based on the content node later. 1.598 + mContentSupportMap.Put(realKid, aMatch); 1.599 + 1.600 + // Assign the element an 'id' attribute using result's id 1.601 + nsAutoString id; 1.602 + rv = aChild->GetId(id); 1.603 + if (NS_FAILED(rv)) 1.604 + return rv; 1.605 + 1.606 + rv = realKid->SetAttr(kNameSpaceID_None, nsGkAtoms::id, id, false); 1.607 + if (NS_FAILED(rv)) 1.608 + return rv; 1.609 + 1.610 + // Set up the element's 'container' and 'empty' attributes. 1.611 + SetContainerAttrs(realKid, aChild, true, false); 1.612 + } 1.613 + else if (tag == nsGkAtoms::textnode && 1.614 + nameSpaceID == kNameSpaceID_XUL) { 1.615 + // <xul:text value="..."> is replaced by text of the 1.616 + // actual value of the 'rdf:resource' attribute for the 1.617 + // given node. 1.618 + // SynchronizeUsingTemplate contains code used to update textnodes, 1.619 + // so make sure to modify both when changing this 1.620 + char16_t attrbuf[128]; 1.621 + nsFixedString attrValue(attrbuf, ArrayLength(attrbuf), 0); 1.622 + tmplKid->GetAttr(kNameSpaceID_None, nsGkAtoms::value, attrValue); 1.623 + if (!attrValue.IsEmpty()) { 1.624 + nsAutoString value; 1.625 + rv = SubstituteText(aChild, attrValue, value); 1.626 + if (NS_FAILED(rv)) return rv; 1.627 + 1.628 + nsRefPtr<nsTextNode> content = 1.629 + new nsTextNode(mRoot->NodeInfo()->NodeInfoManager()); 1.630 + 1.631 + content->SetText(value, false); 1.632 + 1.633 + rv = aRealNode->AppendChildTo(content, aNotify); 1.634 + if (NS_FAILED(rv)) return rv; 1.635 + 1.636 + // XXX Don't bother remembering text nodes as the 1.637 + // first element we've generated? 1.638 + } 1.639 + } 1.640 + else if (tmplKid->IsNodeOfType(nsINode::eTEXT)) { 1.641 + nsCOMPtr<nsIDOMNode> tmplTextNode = do_QueryInterface(tmplKid); 1.642 + if (!tmplTextNode) { 1.643 + NS_ERROR("textnode not implementing nsIDOMNode??"); 1.644 + return NS_ERROR_FAILURE; 1.645 + } 1.646 + nsCOMPtr<nsIDOMNode> clonedNode; 1.647 + tmplTextNode->CloneNode(false, 1, getter_AddRefs(clonedNode)); 1.648 + nsCOMPtr<nsIContent> clonedContent = do_QueryInterface(clonedNode); 1.649 + if (!clonedContent) { 1.650 + NS_ERROR("failed to clone textnode"); 1.651 + return NS_ERROR_FAILURE; 1.652 + } 1.653 + rv = aRealNode->AppendChildTo(clonedContent, aNotify); 1.654 + if (NS_FAILED(rv)) return rv; 1.655 + } 1.656 + else { 1.657 + // It's just a generic element. Create it! 1.658 + nsCOMPtr<Element> element; 1.659 + rv = CreateElement(nameSpaceID, tag, getter_AddRefs(element)); 1.660 + if (NS_FAILED(rv)) return rv; 1.661 + realKid = element.forget(); 1.662 + } 1.663 + 1.664 + if (realKid && !realKidAlreadyExisted) { 1.665 + // Potentially remember the index of this element as the 1.666 + // first element that we've generated. 1.667 + if (aContainer && !*aContainer) { 1.668 + *aContainer = aRealNode; 1.669 + NS_ADDREF(*aContainer); 1.670 + 1.671 + uint32_t indx = aRealNode->GetChildCount(); 1.672 + 1.673 + // Since we haven't inserted any content yet, our new 1.674 + // index in the container will be the current count of 1.675 + // elements in the container. 1.676 + *aNewIndexInContainer = indx; 1.677 + } 1.678 + 1.679 + // Remember the template kid from which we created the 1.680 + // real kid. This allows us to sync back up with the 1.681 + // template to incrementally build content. 1.682 + mTemplateMap.Put(realKid, tmplKid); 1.683 + 1.684 + rv = CopyAttributesToElement(tmplKid, realKid, aChild, false); 1.685 + if (NS_FAILED(rv)) return rv; 1.686 + 1.687 + // Add any persistent attributes 1.688 + if (isGenerationElement) { 1.689 + rv = AddPersistentAttributes(tmplKid->AsElement(), aChild, 1.690 + realKid); 1.691 + if (NS_FAILED(rv)) return rv; 1.692 + } 1.693 + 1.694 + // the unique content recurses up above. Also, don't recurse if 1.695 + // this is a self reference (a reference to the same resource) 1.696 + // or we'll end up regenerating the same content. 1.697 + if (!aIsSelfReference && !isUnique) { 1.698 + // this call creates the content inside the generation node, 1.699 + // for example the label below: 1.700 + // <vbox uri="?"> 1.701 + // <label value="?title"/> 1.702 + // </vbox> 1.703 + rv = BuildContentFromTemplate(tmplKid, aResourceNode, realKid, false, 1.704 + false, aChild, false, aMatch, 1.705 + nullptr /* don't care */, 1.706 + nullptr /* don't care */); 1.707 + if (NS_FAILED(rv)) return rv; 1.708 + 1.709 + if (isGenerationElement) { 1.710 + // build the next level of children 1.711 + rv = CreateContainerContents(realKid, aChild, false, 1.712 + false, false); 1.713 + if (NS_FAILED(rv)) return rv; 1.714 + } 1.715 + } 1.716 + 1.717 + // We'll _already_ have added the unique elements; but if 1.718 + // it's -not- unique, then use the XUL sort service now to 1.719 + // append the element to the content model. 1.720 + if (! isUnique) { 1.721 + rv = NS_ERROR_UNEXPECTED; 1.722 + 1.723 + if (isGenerationElement) 1.724 + rv = InsertSortedNode(aRealNode, realKid, aChild, aNotify); 1.725 + 1.726 + if (NS_FAILED(rv)) { 1.727 + rv = aRealNode->AppendChildTo(realKid, aNotify); 1.728 + NS_ASSERTION(NS_SUCCEEDED(rv), "unable to insert element"); 1.729 + } 1.730 + } 1.731 + } 1.732 + } 1.733 + 1.734 + return NS_OK; 1.735 +} 1.736 + 1.737 +nsresult 1.738 +nsXULContentBuilder::CopyAttributesToElement(nsIContent* aTemplateNode, 1.739 + nsIContent* aRealNode, 1.740 + nsIXULTemplateResult* aResult, 1.741 + bool aNotify) 1.742 +{ 1.743 + nsresult rv; 1.744 + 1.745 + // Copy all attributes from the template to the new element 1.746 + uint32_t numAttribs = aTemplateNode->GetAttrCount(); 1.747 + 1.748 + for (uint32_t attr = 0; attr < numAttribs; attr++) { 1.749 + const nsAttrName* name = aTemplateNode->GetAttrNameAt(attr); 1.750 + int32_t attribNameSpaceID = name->NamespaceID(); 1.751 + // Hold a strong reference here so that the atom doesn't go away 1.752 + // during UnsetAttr. 1.753 + nsCOMPtr<nsIAtom> attribName = name->LocalName(); 1.754 + 1.755 + // XXXndeakin ignore namespaces until bug 321182 is fixed 1.756 + if (attribName != nsGkAtoms::id && attribName != nsGkAtoms::uri) { 1.757 + // Create a buffer here, because there's a chance that an 1.758 + // attribute in the template is going to be an RDF URI, which is 1.759 + // usually longish. 1.760 + char16_t attrbuf[128]; 1.761 + nsFixedString attribValue(attrbuf, ArrayLength(attrbuf), 0); 1.762 + aTemplateNode->GetAttr(attribNameSpaceID, attribName, attribValue); 1.763 + if (!attribValue.IsEmpty()) { 1.764 + nsAutoString value; 1.765 + rv = SubstituteText(aResult, attribValue, value); 1.766 + if (NS_FAILED(rv)) 1.767 + return rv; 1.768 + 1.769 + // if the string is empty after substitutions, remove the 1.770 + // attribute 1.771 + if (!value.IsEmpty()) { 1.772 + rv = aRealNode->SetAttr(attribNameSpaceID, 1.773 + attribName, 1.774 + name->GetPrefix(), 1.775 + value, 1.776 + aNotify); 1.777 + } 1.778 + else { 1.779 + rv = aRealNode->UnsetAttr(attribNameSpaceID, 1.780 + attribName, 1.781 + aNotify); 1.782 + } 1.783 + 1.784 + if (NS_FAILED(rv)) 1.785 + return rv; 1.786 + } 1.787 + } 1.788 + } 1.789 + 1.790 + return NS_OK; 1.791 +} 1.792 + 1.793 +nsresult 1.794 +nsXULContentBuilder::AddPersistentAttributes(Element* aTemplateNode, 1.795 + nsIXULTemplateResult* aResult, 1.796 + nsIContent* aRealNode) 1.797 +{ 1.798 + if (!mRoot) 1.799 + return NS_OK; 1.800 + 1.801 + nsCOMPtr<nsIRDFResource> resource; 1.802 + nsresult rv = GetResultResource(aResult, getter_AddRefs(resource)); 1.803 + NS_ENSURE_SUCCESS(rv, rv); 1.804 + 1.805 + nsAutoString attribute, persist; 1.806 + aTemplateNode->GetAttr(kNameSpaceID_None, nsGkAtoms::persist, persist); 1.807 + 1.808 + while (!persist.IsEmpty()) { 1.809 + attribute.Truncate(); 1.810 + 1.811 + int32_t offset = persist.FindCharInSet(" ,"); 1.812 + if (offset > 0) { 1.813 + persist.Left(attribute, offset); 1.814 + persist.Cut(0, offset + 1); 1.815 + } 1.816 + else { 1.817 + attribute = persist; 1.818 + persist.Truncate(); 1.819 + } 1.820 + 1.821 + attribute.Trim(" "); 1.822 + 1.823 + if (attribute.IsEmpty()) 1.824 + break; 1.825 + 1.826 + nsCOMPtr<nsIAtom> tag; 1.827 + int32_t nameSpaceID; 1.828 + 1.829 + nsCOMPtr<nsINodeInfo> ni = 1.830 + aTemplateNode->GetExistingAttrNameFromQName(attribute); 1.831 + if (ni) { 1.832 + tag = ni->NameAtom(); 1.833 + nameSpaceID = ni->NamespaceID(); 1.834 + } 1.835 + else { 1.836 + tag = do_GetAtom(attribute); 1.837 + NS_ENSURE_TRUE(tag, NS_ERROR_OUT_OF_MEMORY); 1.838 + 1.839 + nameSpaceID = kNameSpaceID_None; 1.840 + } 1.841 + 1.842 + nsCOMPtr<nsIRDFResource> property; 1.843 + rv = nsXULContentUtils::GetResource(nameSpaceID, tag, getter_AddRefs(property)); 1.844 + NS_ENSURE_SUCCESS(rv, rv); 1.845 + 1.846 + nsCOMPtr<nsIRDFNode> target; 1.847 + rv = mDB->GetTarget(resource, property, true, getter_AddRefs(target)); 1.848 + NS_ENSURE_SUCCESS(rv, rv); 1.849 + 1.850 + if (! target) 1.851 + continue; 1.852 + 1.853 + nsCOMPtr<nsIRDFLiteral> value = do_QueryInterface(target); 1.854 + NS_ASSERTION(value != nullptr, "unable to stomach that sort of node"); 1.855 + if (! value) 1.856 + continue; 1.857 + 1.858 + const char16_t* valueStr; 1.859 + rv = value->GetValueConst(&valueStr); 1.860 + NS_ENSURE_SUCCESS(rv, rv); 1.861 + 1.862 + rv = aRealNode->SetAttr(nameSpaceID, tag, nsDependentString(valueStr), 1.863 + false); 1.864 + NS_ENSURE_SUCCESS(rv, rv); 1.865 + } 1.866 + 1.867 + return NS_OK; 1.868 +} 1.869 + 1.870 +nsresult 1.871 +nsXULContentBuilder::SynchronizeUsingTemplate(nsIContent* aTemplateNode, 1.872 + nsIContent* aRealElement, 1.873 + nsIXULTemplateResult* aResult) 1.874 +{ 1.875 + // check all attributes on the template node; if they reference a resource, 1.876 + // update the equivalent attribute on the content node 1.877 + nsresult rv; 1.878 + rv = CopyAttributesToElement(aTemplateNode, aRealElement, aResult, true); 1.879 + if (NS_FAILED(rv)) 1.880 + return rv; 1.881 + 1.882 + uint32_t count = aTemplateNode->GetChildCount(); 1.883 + 1.884 + for (uint32_t loop = 0; loop < count; ++loop) { 1.885 + nsIContent *tmplKid = aTemplateNode->GetChildAt(loop); 1.886 + 1.887 + if (! tmplKid) 1.888 + break; 1.889 + 1.890 + nsIContent *realKid = aRealElement->GetChildAt(loop); 1.891 + if (! realKid) 1.892 + break; 1.893 + 1.894 + // check for text nodes and update them accordingly. 1.895 + // This code is similar to that in BuildContentFromTemplate 1.896 + if (tmplKid->NodeInfo()->Equals(nsGkAtoms::textnode, 1.897 + kNameSpaceID_XUL)) { 1.898 + char16_t attrbuf[128]; 1.899 + nsFixedString attrValue(attrbuf, ArrayLength(attrbuf), 0); 1.900 + tmplKid->GetAttr(kNameSpaceID_None, nsGkAtoms::value, attrValue); 1.901 + if (!attrValue.IsEmpty()) { 1.902 + nsAutoString value; 1.903 + rv = SubstituteText(aResult, attrValue, value); 1.904 + if (NS_FAILED(rv)) return rv; 1.905 + realKid->SetText(value, true); 1.906 + } 1.907 + } 1.908 + 1.909 + rv = SynchronizeUsingTemplate(tmplKid, realKid, aResult); 1.910 + if (NS_FAILED(rv)) return rv; 1.911 + } 1.912 + 1.913 + return NS_OK; 1.914 +} 1.915 + 1.916 +nsresult 1.917 +nsXULContentBuilder::RemoveMember(nsIContent* aContent) 1.918 +{ 1.919 + nsCOMPtr<nsIContent> parent = aContent->GetParent(); 1.920 + if (parent) { 1.921 + int32_t pos = parent->IndexOf(aContent); 1.922 + 1.923 + NS_ASSERTION(pos >= 0, "parent doesn't think this child has an index"); 1.924 + if (pos < 0) return NS_OK; 1.925 + 1.926 + // Note: RemoveChildAt sets |child|'s document to null so that 1.927 + // it'll get knocked out of the XUL doc's resource-to-element 1.928 + // map. 1.929 + parent->RemoveChildAt(pos, true); 1.930 + } 1.931 + 1.932 + // Remove from the content support map. 1.933 + mContentSupportMap.Remove(aContent); 1.934 + 1.935 + // Remove from the template map 1.936 + mTemplateMap.Remove(aContent); 1.937 + 1.938 + return NS_OK; 1.939 +} 1.940 + 1.941 +nsresult 1.942 +nsXULContentBuilder::CreateTemplateAndContainerContents(nsIContent* aElement, 1.943 + bool aForceCreation) 1.944 +{ 1.945 + // Generate both 1) the template content for the current element, 1.946 + // and 2) recursive subcontent (if the current element refers to a 1.947 + // container result). 1.948 + 1.949 + PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS, 1.950 + ("nsXULContentBuilder::CreateTemplateAndContainerContents start - flags: %d", 1.951 + mFlags)); 1.952 + 1.953 + if (! mQueryProcessor) 1.954 + return NS_OK; 1.955 + 1.956 + // for the root element, get the ref attribute and generate content 1.957 + if (aElement == mRoot) { 1.958 + if (! mRootResult) { 1.959 + nsAutoString ref; 1.960 + mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::ref, ref); 1.961 + 1.962 + if (! ref.IsEmpty()) { 1.963 + nsresult rv = mQueryProcessor->TranslateRef(mDataSource, ref, 1.964 + getter_AddRefs(mRootResult)); 1.965 + if (NS_FAILED(rv)) 1.966 + return rv; 1.967 + } 1.968 + } 1.969 + 1.970 + if (mRootResult) { 1.971 + CreateContainerContents(aElement, mRootResult, aForceCreation, 1.972 + false, true); 1.973 + } 1.974 + } 1.975 + else if (!(mFlags & eDontRecurse)) { 1.976 + // The content map will contain the generation elements (the ones that 1.977 + // are given ids) and only those elements, so get the reference point 1.978 + // from the corresponding match. 1.979 + nsTemplateMatch *match = nullptr; 1.980 + if (mContentSupportMap.Get(aElement, &match)) 1.981 + CreateContainerContents(aElement, match->mResult, aForceCreation, 1.982 + false, true); 1.983 + } 1.984 + 1.985 + PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS, 1.986 + ("nsXULContentBuilder::CreateTemplateAndContainerContents end")); 1.987 + 1.988 + return NS_OK; 1.989 +} 1.990 + 1.991 +nsresult 1.992 +nsXULContentBuilder::CreateContainerContents(nsIContent* aElement, 1.993 + nsIXULTemplateResult* aResult, 1.994 + bool aForceCreation, 1.995 + bool aNotify, 1.996 + bool aNotifyAtEnd) 1.997 +{ 1.998 + if (!aForceCreation && !IsOpen(aElement)) 1.999 + return NS_OK; 1.1000 + 1.1001 + // don't generate children if recursion or child processing isn't allowed 1.1002 + if (aResult != mRootResult) { 1.1003 + if (mFlags & eDontRecurse) 1.1004 + return NS_OK; 1.1005 + 1.1006 + bool mayProcessChildren; 1.1007 + nsresult rv = aResult->GetMayProcessChildren(&mayProcessChildren); 1.1008 + if (NS_FAILED(rv) || !mayProcessChildren) 1.1009 + return rv; 1.1010 + } 1.1011 + 1.1012 + nsCOMPtr<nsIRDFResource> refResource; 1.1013 + GetResultResource(aResult, getter_AddRefs(refResource)); 1.1014 + if (! refResource) 1.1015 + return NS_ERROR_FAILURE; 1.1016 + 1.1017 + // Avoid re-entrant builds for the same resource. 1.1018 + if (IsActivated(refResource)) 1.1019 + return NS_OK; 1.1020 + 1.1021 + ActivationEntry entry(refResource, &mTop); 1.1022 + 1.1023 + // Compile the rules now, if they haven't been already. 1.1024 + if (! mQueriesCompiled) { 1.1025 + nsresult rv = CompileQueries(); 1.1026 + if (NS_FAILED(rv)) 1.1027 + return rv; 1.1028 + } 1.1029 + 1.1030 + if (mQuerySets.Length() == 0) 1.1031 + return NS_OK; 1.1032 + 1.1033 + // See if the element's templates contents have been generated: 1.1034 + // this prevents a re-entrant call from triggering another 1.1035 + // generation. 1.1036 + nsXULElement *xulcontent = nsXULElement::FromContent(aElement); 1.1037 + if (xulcontent) { 1.1038 + if (xulcontent->GetTemplateGenerated()) 1.1039 + return NS_OK; 1.1040 + 1.1041 + // Now mark the element's contents as being generated so that 1.1042 + // any re-entrant calls don't trigger an infinite recursion. 1.1043 + xulcontent->SetTemplateGenerated(); 1.1044 + } 1.1045 + 1.1046 + int32_t newIndexInContainer = -1; 1.1047 + nsIContent* container = nullptr; 1.1048 + 1.1049 + int32_t querySetCount = mQuerySets.Length(); 1.1050 + 1.1051 + for (int32_t r = 0; r < querySetCount; r++) { 1.1052 + nsTemplateQuerySet* queryset = mQuerySets[r]; 1.1053 + 1.1054 + nsIAtom* tag = queryset->GetTag(); 1.1055 + if (tag && tag != aElement->Tag()) 1.1056 + continue; 1.1057 + 1.1058 + CreateContainerContentsForQuerySet(aElement, aResult, aNotify, queryset, 1.1059 + &container, &newIndexInContainer); 1.1060 + } 1.1061 + 1.1062 + if (aNotifyAtEnd && container) { 1.1063 + MOZ_AUTO_DOC_UPDATE(container->GetCurrentDoc(), UPDATE_CONTENT_MODEL, 1.1064 + true); 1.1065 + nsNodeUtils::ContentAppended(container, 1.1066 + container->GetChildAt(newIndexInContainer), 1.1067 + newIndexInContainer); 1.1068 + } 1.1069 + 1.1070 + NS_IF_RELEASE(container); 1.1071 + 1.1072 + return NS_OK; 1.1073 +} 1.1074 + 1.1075 +nsresult 1.1076 +nsXULContentBuilder::CreateContainerContentsForQuerySet(nsIContent* aElement, 1.1077 + nsIXULTemplateResult* aResult, 1.1078 + bool aNotify, 1.1079 + nsTemplateQuerySet* aQuerySet, 1.1080 + nsIContent** aContainer, 1.1081 + int32_t* aNewIndexInContainer) 1.1082 +{ 1.1083 +#ifdef PR_LOGGING 1.1084 + if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { 1.1085 + nsAutoString id; 1.1086 + aResult->GetId(id); 1.1087 + PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS, 1.1088 + ("nsXULContentBuilder::CreateContainerContentsForQuerySet start for ref %s\n", 1.1089 + NS_ConvertUTF16toUTF8(id).get())); 1.1090 + } 1.1091 +#endif 1.1092 + 1.1093 + if (! mQueryProcessor) 1.1094 + return NS_OK; 1.1095 + 1.1096 + nsCOMPtr<nsISimpleEnumerator> results; 1.1097 + nsresult rv = mQueryProcessor->GenerateResults(mDataSource, aResult, 1.1098 + aQuerySet->mCompiledQuery, 1.1099 + getter_AddRefs(results)); 1.1100 + if (NS_FAILED(rv) || !results) 1.1101 + return rv; 1.1102 + 1.1103 + bool hasMoreResults; 1.1104 + rv = results->HasMoreElements(&hasMoreResults); 1.1105 + 1.1106 + for (; NS_SUCCEEDED(rv) && hasMoreResults; 1.1107 + rv = results->HasMoreElements(&hasMoreResults)) { 1.1108 + nsCOMPtr<nsISupports> nr; 1.1109 + rv = results->GetNext(getter_AddRefs(nr)); 1.1110 + if (NS_FAILED(rv)) 1.1111 + return rv; 1.1112 + 1.1113 + nsCOMPtr<nsIXULTemplateResult> nextresult = do_QueryInterface(nr); 1.1114 + if (!nextresult) 1.1115 + return NS_ERROR_UNEXPECTED; 1.1116 + 1.1117 + nsCOMPtr<nsIRDFResource> resultid; 1.1118 + rv = GetResultResource(nextresult, getter_AddRefs(resultid)); 1.1119 + if (NS_FAILED(rv)) 1.1120 + return rv; 1.1121 + 1.1122 + if (!resultid) 1.1123 + continue; 1.1124 + 1.1125 + nsTemplateMatch *newmatch = 1.1126 + nsTemplateMatch::Create(aQuerySet->Priority(), 1.1127 + nextresult, aElement); 1.1128 + if (!newmatch) 1.1129 + return NS_ERROR_OUT_OF_MEMORY; 1.1130 + 1.1131 + // check if there is already an existing match. If so, a previous 1.1132 + // query already generated content so the match is just added to the 1.1133 + // end of the set of matches. 1.1134 + 1.1135 + bool generateContent = true; 1.1136 + 1.1137 + nsTemplateMatch* prevmatch = nullptr; 1.1138 + nsTemplateMatch* existingmatch = nullptr; 1.1139 + nsTemplateMatch* removematch = nullptr; 1.1140 + if (mMatchMap.Get(resultid, &existingmatch)){ 1.1141 + // check if there is an existing match that matched a rule 1.1142 + while (existingmatch) { 1.1143 + // break out once we've reached a query in the list with a 1.1144 + // higher priority, as the new match list is sorted by 1.1145 + // priority, and the new match should be inserted here 1.1146 + int32_t priority = existingmatch->QuerySetPriority(); 1.1147 + if (priority > aQuerySet->Priority()) 1.1148 + break; 1.1149 + 1.1150 + // skip over non-matching containers 1.1151 + if (existingmatch->GetContainer() == aElement) { 1.1152 + // if the same priority is already found, replace it. This can happen 1.1153 + // when a container is removed and readded 1.1154 + if (priority == aQuerySet->Priority()) { 1.1155 + removematch = existingmatch; 1.1156 + break; 1.1157 + } 1.1158 + 1.1159 + if (existingmatch->IsActive()) 1.1160 + generateContent = false; 1.1161 + } 1.1162 + 1.1163 + prevmatch = existingmatch; 1.1164 + existingmatch = existingmatch->mNext; 1.1165 + } 1.1166 + } 1.1167 + 1.1168 + if (removematch) { 1.1169 + // remove the generated content for the existing match 1.1170 + rv = ReplaceMatch(removematch->mResult, nullptr, nullptr, aElement); 1.1171 + if (NS_FAILED(rv)) 1.1172 + return rv; 1.1173 + 1.1174 + if (mFlags & eLoggingEnabled) 1.1175 + OutputMatchToLog(resultid, removematch, false); 1.1176 + } 1.1177 + 1.1178 + if (generateContent) { 1.1179 + // find the rule that matches. If none match, the content does not 1.1180 + // need to be generated 1.1181 + 1.1182 + int16_t ruleindex; 1.1183 + nsTemplateRule* matchedrule = nullptr; 1.1184 + rv = DetermineMatchedRule(aElement, nextresult, aQuerySet, 1.1185 + &matchedrule, &ruleindex); 1.1186 + if (NS_FAILED(rv)) { 1.1187 + nsTemplateMatch::Destroy(newmatch, false); 1.1188 + return rv; 1.1189 + } 1.1190 + 1.1191 + if (matchedrule) { 1.1192 + rv = newmatch->RuleMatched(aQuerySet, matchedrule, 1.1193 + ruleindex, nextresult); 1.1194 + if (NS_FAILED(rv)) { 1.1195 + nsTemplateMatch::Destroy(newmatch, false); 1.1196 + return rv; 1.1197 + } 1.1198 + 1.1199 + // Grab the template node 1.1200 + nsCOMPtr<nsIContent> action = matchedrule->GetAction(); 1.1201 + BuildContentFromTemplate(action, aElement, aElement, true, 1.1202 + mRefVariable == matchedrule->GetMemberVariable(), 1.1203 + nextresult, aNotify, newmatch, 1.1204 + aContainer, aNewIndexInContainer); 1.1205 + } 1.1206 + } 1.1207 + 1.1208 + if (mFlags & eLoggingEnabled) 1.1209 + OutputMatchToLog(resultid, newmatch, true); 1.1210 + 1.1211 + if (prevmatch) { 1.1212 + prevmatch->mNext = newmatch; 1.1213 + } 1.1214 + else { 1.1215 + mMatchMap.Put(resultid, newmatch); 1.1216 + } 1.1217 + 1.1218 + if (removematch) { 1.1219 + newmatch->mNext = removematch->mNext; 1.1220 + nsTemplateMatch::Destroy(removematch, true); 1.1221 + } 1.1222 + else { 1.1223 + newmatch->mNext = existingmatch; 1.1224 + } 1.1225 + } 1.1226 + 1.1227 + return rv; 1.1228 +} 1.1229 + 1.1230 +nsresult 1.1231 +nsXULContentBuilder::EnsureElementHasGenericChild(nsIContent* parent, 1.1232 + int32_t nameSpaceID, 1.1233 + nsIAtom* tag, 1.1234 + bool aNotify, 1.1235 + nsIContent** result) 1.1236 +{ 1.1237 + nsresult rv; 1.1238 + 1.1239 + rv = nsXULContentUtils::FindChildByTag(parent, nameSpaceID, tag, result); 1.1240 + if (NS_FAILED(rv)) 1.1241 + return rv; 1.1242 + 1.1243 + if (rv == NS_RDF_NO_VALUE) { 1.1244 + // we need to construct a new child element. 1.1245 + nsCOMPtr<Element> element; 1.1246 + 1.1247 + rv = CreateElement(nameSpaceID, tag, getter_AddRefs(element)); 1.1248 + if (NS_FAILED(rv)) 1.1249 + return rv; 1.1250 + 1.1251 + // XXX Note that the notification ensures we won't batch insertions! This could be bad! - Dave 1.1252 + rv = parent->AppendChildTo(element, aNotify); 1.1253 + if (NS_FAILED(rv)) 1.1254 + return rv; 1.1255 + 1.1256 + *result = element; 1.1257 + NS_ADDREF(*result); 1.1258 + return NS_ELEMENT_GOT_CREATED; 1.1259 + } 1.1260 + else { 1.1261 + return NS_ELEMENT_WAS_THERE; 1.1262 + } 1.1263 +} 1.1264 + 1.1265 +bool 1.1266 +nsXULContentBuilder::IsOpen(nsIContent* aElement) 1.1267 +{ 1.1268 + // Determine if this is a <treeitem> or <menu> element 1.1269 + if (!aElement->IsXUL()) 1.1270 + return true; 1.1271 + 1.1272 + // XXXhyatt Use the XBL service to obtain a base tag. 1.1273 + nsIAtom *tag = aElement->Tag(); 1.1274 + if (tag == nsGkAtoms::menu || 1.1275 + tag == nsGkAtoms::menubutton || 1.1276 + tag == nsGkAtoms::toolbarbutton || 1.1277 + tag == nsGkAtoms::button || 1.1278 + tag == nsGkAtoms::treeitem) 1.1279 + return aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::open, 1.1280 + nsGkAtoms::_true, eCaseMatters); 1.1281 + return true; 1.1282 +} 1.1283 + 1.1284 +nsresult 1.1285 +nsXULContentBuilder::RemoveGeneratedContent(nsIContent* aElement) 1.1286 +{ 1.1287 + // Keep a queue of "ungenerated" elements that we have to probe 1.1288 + // for generated content. 1.1289 + nsAutoTArray<nsIContent*, 8> ungenerated; 1.1290 + if (ungenerated.AppendElement(aElement) == nullptr) 1.1291 + return NS_ERROR_OUT_OF_MEMORY; 1.1292 + 1.1293 + uint32_t count; 1.1294 + while (0 != (count = ungenerated.Length())) { 1.1295 + // Pull the next "ungenerated" element off the queue. 1.1296 + uint32_t last = count - 1; 1.1297 + nsCOMPtr<nsIContent> element = ungenerated[last]; 1.1298 + ungenerated.RemoveElementAt(last); 1.1299 + 1.1300 + uint32_t i = element->GetChildCount(); 1.1301 + 1.1302 + while (i-- > 0) { 1.1303 + nsCOMPtr<nsIContent> child = element->GetChildAt(i); 1.1304 + 1.1305 + // Optimize for the <template> element, because we *know* 1.1306 + // it won't have any generated content: there's no reason 1.1307 + // to even check this subtree. 1.1308 + // XXX should this check |child| rather than |element|? Otherwise 1.1309 + // it should be moved outside the inner loop. Bug 297290. 1.1310 + if (element->NodeInfo()->Equals(nsGkAtoms::_template, 1.1311 + kNameSpaceID_XUL) || 1.1312 + !element->IsElement()) 1.1313 + continue; 1.1314 + 1.1315 + // If the element is in the template map, then we 1.1316 + // assume it's been generated and nuke it. 1.1317 + nsCOMPtr<nsIContent> tmpl; 1.1318 + mTemplateMap.GetTemplateFor(child, getter_AddRefs(tmpl)); 1.1319 + 1.1320 + if (! tmpl) { 1.1321 + // No 'template' attribute, so this must not have been 1.1322 + // generated. We'll need to examine its kids. 1.1323 + if (ungenerated.AppendElement(child) == nullptr) 1.1324 + return NS_ERROR_OUT_OF_MEMORY; 1.1325 + continue; 1.1326 + } 1.1327 + 1.1328 + // If we get here, it's "generated". Bye bye! 1.1329 + element->RemoveChildAt(i, true); 1.1330 + 1.1331 + // Remove this and any children from the content support map. 1.1332 + mContentSupportMap.Remove(child); 1.1333 + 1.1334 + // Remove from the template map 1.1335 + mTemplateMap.Remove(child); 1.1336 + } 1.1337 + } 1.1338 + 1.1339 + return NS_OK; 1.1340 +} 1.1341 + 1.1342 +nsresult 1.1343 +nsXULContentBuilder::GetElementsForResult(nsIXULTemplateResult* aResult, 1.1344 + nsCOMArray<nsIContent>& aElements) 1.1345 +{ 1.1346 + // if the root has been removed from the document, just return 1.1347 + // since there won't be any generated content any more 1.1348 + nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(mRoot->GetDocument()); 1.1349 + if (! xuldoc) 1.1350 + return NS_OK; 1.1351 + 1.1352 + nsAutoString id; 1.1353 + aResult->GetId(id); 1.1354 + 1.1355 + xuldoc->GetElementsForID(id, aElements); 1.1356 + 1.1357 + return NS_OK; 1.1358 +} 1.1359 + 1.1360 +nsresult 1.1361 +nsXULContentBuilder::CreateElement(int32_t aNameSpaceID, 1.1362 + nsIAtom* aTag, 1.1363 + Element** aResult) 1.1364 +{ 1.1365 + nsCOMPtr<nsIDocument> doc = mRoot->GetDocument(); 1.1366 + NS_ASSERTION(doc != nullptr, "not initialized"); 1.1367 + if (! doc) 1.1368 + return NS_ERROR_NOT_INITIALIZED; 1.1369 + 1.1370 + nsCOMPtr<nsINodeInfo> nodeInfo = 1.1371 + doc->NodeInfoManager()->GetNodeInfo(aTag, nullptr, aNameSpaceID, 1.1372 + nsIDOMNode::ELEMENT_NODE); 1.1373 + 1.1374 + return NS_NewElement(aResult, nodeInfo.forget(), NOT_FROM_PARSER); 1.1375 +} 1.1376 + 1.1377 +nsresult 1.1378 +nsXULContentBuilder::SetContainerAttrs(nsIContent *aElement, 1.1379 + nsIXULTemplateResult* aResult, 1.1380 + bool aIgnoreNonContainers, 1.1381 + bool aNotify) 1.1382 +{ 1.1383 + NS_PRECONDITION(aResult != nullptr, "null ptr"); 1.1384 + if (! aResult) 1.1385 + return NS_ERROR_NULL_POINTER; 1.1386 + 1.1387 + bool iscontainer; 1.1388 + aResult->GetIsContainer(&iscontainer); 1.1389 + 1.1390 + if (aIgnoreNonContainers && !iscontainer) 1.1391 + return NS_OK; 1.1392 + 1.1393 + NS_NAMED_LITERAL_STRING(true_, "true"); 1.1394 + NS_NAMED_LITERAL_STRING(false_, "false"); 1.1395 + 1.1396 + const nsAString& newcontainer = 1.1397 + iscontainer ? true_ : false_; 1.1398 + 1.1399 + aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::container, 1.1400 + newcontainer, aNotify); 1.1401 + 1.1402 + if (iscontainer && !(mFlags & eDontTestEmpty)) { 1.1403 + bool isempty; 1.1404 + aResult->GetIsEmpty(&isempty); 1.1405 + 1.1406 + const nsAString& newempty = 1.1407 + (iscontainer && isempty) ? true_ : false_; 1.1408 + 1.1409 + aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::empty, 1.1410 + newempty, aNotify); 1.1411 + } 1.1412 + 1.1413 + return NS_OK; 1.1414 +} 1.1415 + 1.1416 + 1.1417 +//---------------------------------------------------------------------- 1.1418 +// 1.1419 +// nsIXULTemplateBuilder methods 1.1420 +// 1.1421 + 1.1422 +NS_IMETHODIMP 1.1423 +nsXULContentBuilder::CreateContents(nsIContent* aElement, bool aForceCreation) 1.1424 +{ 1.1425 + NS_PRECONDITION(aElement != nullptr, "null ptr"); 1.1426 + if (! aElement) 1.1427 + return NS_ERROR_NULL_POINTER; 1.1428 + 1.1429 + // don't build contents for closed elements. aForceCreation will be true 1.1430 + // when a menu is about to be opened, so the content should be built anyway. 1.1431 + if (!aForceCreation && !IsOpen(aElement)) 1.1432 + return NS_OK; 1.1433 + 1.1434 + return CreateTemplateAndContainerContents(aElement, aForceCreation); 1.1435 +} 1.1436 + 1.1437 +NS_IMETHODIMP 1.1438 +nsXULContentBuilder::HasGeneratedContent(nsIRDFResource* aResource, 1.1439 + nsIAtom* aTag, 1.1440 + bool* aGenerated) 1.1441 +{ 1.1442 + *aGenerated = false; 1.1443 + NS_ENSURE_TRUE(mRoot, NS_ERROR_NOT_INITIALIZED); 1.1444 + NS_ENSURE_STATE(mRootResult); 1.1445 + 1.1446 + nsCOMPtr<nsIRDFResource> rootresource; 1.1447 + nsresult rv = mRootResult->GetResource(getter_AddRefs(rootresource)); 1.1448 + if (NS_FAILED(rv)) 1.1449 + return rv; 1.1450 + 1.1451 + // the root resource is always acceptable 1.1452 + if (aResource == rootresource) { 1.1453 + if (!aTag || mRoot->Tag() == aTag) 1.1454 + *aGenerated = true; 1.1455 + } 1.1456 + else { 1.1457 + const char* uri; 1.1458 + aResource->GetValueConst(&uri); 1.1459 + 1.1460 + NS_ConvertUTF8toUTF16 refID(uri); 1.1461 + 1.1462 + // just return if the node is no longer in a document 1.1463 + nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(mRoot->GetDocument()); 1.1464 + if (! xuldoc) 1.1465 + return NS_OK; 1.1466 + 1.1467 + nsCOMArray<nsIContent> elements; 1.1468 + xuldoc->GetElementsForID(refID, elements); 1.1469 + 1.1470 + uint32_t cnt = elements.Count(); 1.1471 + 1.1472 + for (int32_t i = int32_t(cnt) - 1; i >= 0; --i) { 1.1473 + nsCOMPtr<nsIContent> content = elements.SafeObjectAt(i); 1.1474 + 1.1475 + do { 1.1476 + nsTemplateMatch* match; 1.1477 + if (content == mRoot || mContentSupportMap.Get(content, &match)) { 1.1478 + // If we've got a tag, check it to ensure we're consistent. 1.1479 + if (!aTag || content->Tag() == aTag) { 1.1480 + *aGenerated = true; 1.1481 + return NS_OK; 1.1482 + } 1.1483 + } 1.1484 + 1.1485 + content = content->GetParent(); 1.1486 + } while (content); 1.1487 + } 1.1488 + } 1.1489 + 1.1490 + return NS_OK; 1.1491 +} 1.1492 + 1.1493 +NS_IMETHODIMP 1.1494 +nsXULContentBuilder::GetResultForContent(nsIDOMElement* aElement, 1.1495 + nsIXULTemplateResult** aResult) 1.1496 +{ 1.1497 + NS_ENSURE_ARG_POINTER(aElement); 1.1498 + NS_ENSURE_ARG_POINTER(aResult); 1.1499 + 1.1500 + nsCOMPtr<nsIContent> content = do_QueryInterface(aElement); 1.1501 + if (content == mRoot) { 1.1502 + *aResult = mRootResult; 1.1503 + } 1.1504 + else { 1.1505 + nsTemplateMatch *match = nullptr; 1.1506 + if (mContentSupportMap.Get(content, &match)) 1.1507 + *aResult = match->mResult; 1.1508 + else 1.1509 + *aResult = nullptr; 1.1510 + } 1.1511 + 1.1512 + NS_IF_ADDREF(*aResult); 1.1513 + return NS_OK; 1.1514 +} 1.1515 + 1.1516 +//---------------------------------------------------------------------- 1.1517 +// 1.1518 +// nsIDocumentObserver methods 1.1519 +// 1.1520 + 1.1521 +void 1.1522 +nsXULContentBuilder::AttributeChanged(nsIDocument* aDocument, 1.1523 + Element* aElement, 1.1524 + int32_t aNameSpaceID, 1.1525 + nsIAtom* aAttribute, 1.1526 + int32_t aModType) 1.1527 +{ 1.1528 + nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); 1.1529 + 1.1530 + // Handle "open" and "close" cases. We do this handling before 1.1531 + // we've notified the observer, so that content is already created 1.1532 + // for the frame system to walk. 1.1533 + if (aElement->GetNameSpaceID() == kNameSpaceID_XUL && 1.1534 + aAttribute == nsGkAtoms::open) { 1.1535 + // We're on a XUL tag, and an ``open'' attribute changed. 1.1536 + if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::open, 1.1537 + nsGkAtoms::_true, eCaseMatters)) 1.1538 + OpenContainer(aElement); 1.1539 + else 1.1540 + CloseContainer(aElement); 1.1541 + } 1.1542 + 1.1543 + if ((aNameSpaceID == kNameSpaceID_XUL) && 1.1544 + ((aAttribute == nsGkAtoms::sort) || 1.1545 + (aAttribute == nsGkAtoms::sortDirection) || 1.1546 + (aAttribute == nsGkAtoms::sortResource) || 1.1547 + (aAttribute == nsGkAtoms::sortResource2))) 1.1548 + mSortState.initialized = false; 1.1549 + 1.1550 + // Pass along to the generic template builder. 1.1551 + nsXULTemplateBuilder::AttributeChanged(aDocument, aElement, aNameSpaceID, 1.1552 + aAttribute, aModType); 1.1553 +} 1.1554 + 1.1555 +void 1.1556 +nsXULContentBuilder::NodeWillBeDestroyed(const nsINode* aNode) 1.1557 +{ 1.1558 + nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); 1.1559 + // Break circular references 1.1560 + mContentSupportMap.Clear(); 1.1561 + 1.1562 + nsXULTemplateBuilder::NodeWillBeDestroyed(aNode); 1.1563 +} 1.1564 + 1.1565 + 1.1566 +//---------------------------------------------------------------------- 1.1567 +// 1.1568 +// nsXULTemplateBuilder methods 1.1569 +// 1.1570 + 1.1571 +bool 1.1572 +nsXULContentBuilder::GetInsertionLocations(nsIXULTemplateResult* aResult, 1.1573 + nsCOMArray<nsIContent>** aLocations) 1.1574 +{ 1.1575 + *aLocations = nullptr; 1.1576 + 1.1577 + nsAutoString ref; 1.1578 + nsresult rv = aResult->GetBindingFor(mRefVariable, ref); 1.1579 + if (NS_FAILED(rv)) 1.1580 + return false; 1.1581 + 1.1582 + nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(mRoot->GetDocument()); 1.1583 + if (! xuldoc) 1.1584 + return false; 1.1585 + 1.1586 + *aLocations = new nsCOMArray<nsIContent>; 1.1587 + NS_ENSURE_TRUE(*aLocations, false); 1.1588 + 1.1589 + xuldoc->GetElementsForID(ref, **aLocations); 1.1590 + uint32_t count = (*aLocations)->Count(); 1.1591 + 1.1592 + bool found = false; 1.1593 + 1.1594 + for (uint32_t t = 0; t < count; t++) { 1.1595 + nsCOMPtr<nsIContent> content = (*aLocations)->SafeObjectAt(t); 1.1596 + 1.1597 + nsTemplateMatch* refmatch; 1.1598 + if (content == mRoot || mContentSupportMap.Get(content, &refmatch)) { 1.1599 + // See if we've built the container contents for "content" 1.1600 + // yet. If not, we don't need to build any content. This 1.1601 + // happens, for example, if we receive an assertion on a 1.1602 + // closed folder in a tree widget or on a menu that hasn't 1.1603 + // yet been opened. 1.1604 + nsXULElement *xulcontent = nsXULElement::FromContent(content); 1.1605 + if (!xulcontent || xulcontent->GetTemplateGenerated()) { 1.1606 + found = true; 1.1607 + continue; 1.1608 + } 1.1609 + } 1.1610 + 1.1611 + // clear the item in the list since we don't want to insert there 1.1612 + (*aLocations)->ReplaceObjectAt(nullptr, t); 1.1613 + } 1.1614 + 1.1615 + return found; 1.1616 +} 1.1617 + 1.1618 +nsresult 1.1619 +nsXULContentBuilder::ReplaceMatch(nsIXULTemplateResult* aOldResult, 1.1620 + nsTemplateMatch* aNewMatch, 1.1621 + nsTemplateRule* aNewMatchRule, 1.1622 + void *aContext) 1.1623 + 1.1624 +{ 1.1625 + nsresult rv; 1.1626 + nsIContent* content = static_cast<nsIContent*>(aContext); 1.1627 + 1.1628 + // update the container attributes for the match 1.1629 + if (content) { 1.1630 + nsAutoString ref; 1.1631 + if (aNewMatch) 1.1632 + rv = aNewMatch->mResult->GetBindingFor(mRefVariable, ref); 1.1633 + else 1.1634 + rv = aOldResult->GetBindingFor(mRefVariable, ref); 1.1635 + if (NS_FAILED(rv)) 1.1636 + return rv; 1.1637 + 1.1638 + if (!ref.IsEmpty()) { 1.1639 + nsCOMPtr<nsIXULTemplateResult> refResult; 1.1640 + rv = GetResultForId(ref, getter_AddRefs(refResult)); 1.1641 + if (NS_FAILED(rv)) 1.1642 + return rv; 1.1643 + 1.1644 + if (refResult) 1.1645 + SetContainerAttrs(content, refResult, false, true); 1.1646 + } 1.1647 + } 1.1648 + 1.1649 + if (aOldResult) { 1.1650 + nsCOMArray<nsIContent> elements; 1.1651 + rv = GetElementsForResult(aOldResult, elements); 1.1652 + if (NS_FAILED(rv)) 1.1653 + return rv; 1.1654 + 1.1655 + uint32_t count = elements.Count(); 1.1656 + 1.1657 + for (int32_t e = int32_t(count) - 1; e >= 0; --e) { 1.1658 + nsCOMPtr<nsIContent> child = elements.SafeObjectAt(e); 1.1659 + 1.1660 + nsTemplateMatch* match; 1.1661 + if (mContentSupportMap.Get(child, &match)) { 1.1662 + if (content == match->GetContainer()) 1.1663 + RemoveMember(child); 1.1664 + } 1.1665 + } 1.1666 + } 1.1667 + 1.1668 + if (aNewMatch) { 1.1669 + nsCOMPtr<nsIContent> action = aNewMatchRule->GetAction(); 1.1670 + return BuildContentFromTemplate(action, content, content, true, 1.1671 + mRefVariable == aNewMatchRule->GetMemberVariable(), 1.1672 + aNewMatch->mResult, true, aNewMatch, 1.1673 + nullptr, nullptr); 1.1674 + } 1.1675 + 1.1676 + return NS_OK; 1.1677 +} 1.1678 + 1.1679 + 1.1680 +nsresult 1.1681 +nsXULContentBuilder::SynchronizeResult(nsIXULTemplateResult* aResult) 1.1682 +{ 1.1683 + nsCOMArray<nsIContent> elements; 1.1684 + GetElementsForResult(aResult, elements); 1.1685 + 1.1686 + uint32_t cnt = elements.Count(); 1.1687 + 1.1688 + for (int32_t i = int32_t(cnt) - 1; i >= 0; --i) { 1.1689 + nsCOMPtr<nsIContent> element = elements.SafeObjectAt(i); 1.1690 + 1.1691 + nsTemplateMatch* match; 1.1692 + if (! mContentSupportMap.Get(element, &match)) 1.1693 + continue; 1.1694 + 1.1695 + nsCOMPtr<nsIContent> templateNode; 1.1696 + mTemplateMap.GetTemplateFor(element, getter_AddRefs(templateNode)); 1.1697 + 1.1698 + NS_ASSERTION(templateNode, "couldn't find template node for element"); 1.1699 + if (! templateNode) 1.1700 + continue; 1.1701 + 1.1702 + // this node was created by a XUL template, so update it accordingly 1.1703 + SynchronizeUsingTemplate(templateNode, element, aResult); 1.1704 + } 1.1705 + 1.1706 + return NS_OK; 1.1707 +} 1.1708 + 1.1709 +//---------------------------------------------------------------------- 1.1710 +// 1.1711 +// Implementation methods 1.1712 +// 1.1713 + 1.1714 +nsresult 1.1715 +nsXULContentBuilder::OpenContainer(nsIContent* aElement) 1.1716 +{ 1.1717 + if (aElement != mRoot) { 1.1718 + if (mFlags & eDontRecurse) 1.1719 + return NS_OK; 1.1720 + 1.1721 + bool rightBuilder = false; 1.1722 + 1.1723 + nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(aElement->GetDocument()); 1.1724 + if (! xuldoc) 1.1725 + return NS_OK; 1.1726 + 1.1727 + // See if we're responsible for this element 1.1728 + nsIContent* content = aElement; 1.1729 + do { 1.1730 + nsCOMPtr<nsIXULTemplateBuilder> builder; 1.1731 + xuldoc->GetTemplateBuilderFor(content, getter_AddRefs(builder)); 1.1732 + if (builder) { 1.1733 + if (builder == this) 1.1734 + rightBuilder = true; 1.1735 + break; 1.1736 + } 1.1737 + 1.1738 + content = content->GetParent(); 1.1739 + } while (content); 1.1740 + 1.1741 + if (! rightBuilder) 1.1742 + return NS_OK; 1.1743 + } 1.1744 + 1.1745 + CreateTemplateAndContainerContents(aElement, false); 1.1746 + 1.1747 + return NS_OK; 1.1748 +} 1.1749 + 1.1750 +nsresult 1.1751 +nsXULContentBuilder::CloseContainer(nsIContent* aElement) 1.1752 +{ 1.1753 + return NS_OK; 1.1754 +} 1.1755 + 1.1756 +nsresult 1.1757 +nsXULContentBuilder::RebuildAll() 1.1758 +{ 1.1759 + NS_ENSURE_TRUE(mRoot, NS_ERROR_NOT_INITIALIZED); 1.1760 + 1.1761 + // Bail out early if we are being torn down. 1.1762 + nsCOMPtr<nsIDocument> doc = mRoot->GetDocument(); 1.1763 + if (!doc) 1.1764 + return NS_OK; 1.1765 + 1.1766 + if (mQueriesCompiled) 1.1767 + Uninit(false); 1.1768 + 1.1769 + nsresult rv = CompileQueries(); 1.1770 + if (NS_FAILED(rv)) 1.1771 + return rv; 1.1772 + 1.1773 + if (mQuerySets.Length() == 0) 1.1774 + return NS_OK; 1.1775 + 1.1776 + nsXULElement *xulcontent = nsXULElement::FromContent(mRoot); 1.1777 + if (xulcontent) 1.1778 + xulcontent->ClearTemplateGenerated(); 1.1779 + 1.1780 + // Now, regenerate both the template- and container-generated 1.1781 + // contents for the current element... 1.1782 + CreateTemplateAndContainerContents(mRoot, false); 1.1783 + 1.1784 + return NS_OK; 1.1785 +} 1.1786 + 1.1787 +/**** Sorting Methods ****/ 1.1788 + 1.1789 +nsresult 1.1790 +nsXULContentBuilder::CompareResultToNode(nsIXULTemplateResult* aResult, 1.1791 + nsIContent* aContent, 1.1792 + int32_t* aSortOrder) 1.1793 +{ 1.1794 + NS_ASSERTION(aSortOrder, "CompareResultToNode: null out param aSortOrder"); 1.1795 + 1.1796 + *aSortOrder = 0; 1.1797 + 1.1798 + nsTemplateMatch *match = nullptr; 1.1799 + if (!mContentSupportMap.Get(aContent, &match)) { 1.1800 + *aSortOrder = mSortState.sortStaticsLast ? -1 : 1; 1.1801 + return NS_OK; 1.1802 + } 1.1803 + 1.1804 + if (!mQueryProcessor) 1.1805 + return NS_OK; 1.1806 + 1.1807 + if (mSortState.direction == nsSortState_natural) { 1.1808 + // sort in natural order 1.1809 + nsresult rv = mQueryProcessor->CompareResults(aResult, match->mResult, 1.1810 + nullptr, mSortState.sortHints, 1.1811 + aSortOrder); 1.1812 + NS_ENSURE_SUCCESS(rv, rv); 1.1813 + } 1.1814 + else { 1.1815 + // iterate over each sort key and compare. If the nodes are equal, 1.1816 + // continue to compare using the next sort key. If not equal, stop. 1.1817 + int32_t length = mSortState.sortKeys.Count(); 1.1818 + for (int32_t t = 0; t < length; t++) { 1.1819 + nsresult rv = mQueryProcessor->CompareResults(aResult, match->mResult, 1.1820 + mSortState.sortKeys[t], 1.1821 + mSortState.sortHints, aSortOrder); 1.1822 + NS_ENSURE_SUCCESS(rv, rv); 1.1823 + 1.1824 + if (*aSortOrder) 1.1825 + break; 1.1826 + } 1.1827 + } 1.1828 + 1.1829 + // flip the sort order if performing a descending sorting 1.1830 + if (mSortState.direction == nsSortState_descending) 1.1831 + *aSortOrder = -*aSortOrder; 1.1832 + 1.1833 + return NS_OK; 1.1834 +} 1.1835 + 1.1836 +nsresult 1.1837 +nsXULContentBuilder::InsertSortedNode(nsIContent* aContainer, 1.1838 + nsIContent* aNode, 1.1839 + nsIXULTemplateResult* aResult, 1.1840 + bool aNotify) 1.1841 +{ 1.1842 + nsresult rv; 1.1843 + 1.1844 + if (!mSortState.initialized) { 1.1845 + nsAutoString sort, sortDirection, sortHints; 1.1846 + mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::sort, sort); 1.1847 + mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::sortDirection, sortDirection); 1.1848 + mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::sorthints, sortHints); 1.1849 + sortDirection.AppendLiteral(" "); 1.1850 + sortDirection += sortHints; 1.1851 + rv = XULSortServiceImpl::InitializeSortState(mRoot, aContainer, 1.1852 + sort, sortDirection, &mSortState); 1.1853 + NS_ENSURE_SUCCESS(rv, rv); 1.1854 + } 1.1855 + 1.1856 + // when doing a natural sort, items will typically be sorted according to 1.1857 + // the order they appear in the datasource. For RDF, cache whether the 1.1858 + // reference parent is an RDF Seq. That way, the items can be sorted in the 1.1859 + // order they are in the Seq. 1.1860 + mSortState.isContainerRDFSeq = false; 1.1861 + if (mSortState.direction == nsSortState_natural) { 1.1862 + nsCOMPtr<nsISupports> ref; 1.1863 + nsresult rv = aResult->GetBindingObjectFor(mRefVariable, getter_AddRefs(ref)); 1.1864 + NS_ENSURE_SUCCESS(rv, rv); 1.1865 + 1.1866 + nsCOMPtr<nsIRDFResource> container = do_QueryInterface(ref); 1.1867 + 1.1868 + if (container) { 1.1869 + rv = gRDFContainerUtils->IsSeq(mDB, container, &mSortState.isContainerRDFSeq); 1.1870 + NS_ENSURE_SUCCESS(rv, rv); 1.1871 + } 1.1872 + } 1.1873 + 1.1874 + bool childAdded = false; 1.1875 + uint32_t numChildren = aContainer->GetChildCount(); 1.1876 + 1.1877 + if (mSortState.direction != nsSortState_natural || 1.1878 + (mSortState.direction == nsSortState_natural && mSortState.isContainerRDFSeq)) 1.1879 + { 1.1880 + // because numChildren gets modified 1.1881 + int32_t realNumChildren = numChildren; 1.1882 + nsIContent *child = nullptr; 1.1883 + 1.1884 + // rjc says: determine where static XUL ends and generated XUL/RDF begins 1.1885 + int32_t staticCount = 0; 1.1886 + 1.1887 + nsAutoString staticValue; 1.1888 + aContainer->GetAttr(kNameSpaceID_None, nsGkAtoms::staticHint, staticValue); 1.1889 + if (!staticValue.IsEmpty()) 1.1890 + { 1.1891 + // found "static" XUL element count hint 1.1892 + nsresult strErr = NS_OK; 1.1893 + staticCount = staticValue.ToInteger(&strErr); 1.1894 + if (NS_FAILED(strErr)) 1.1895 + staticCount = 0; 1.1896 + } else { 1.1897 + // compute the "static" XUL element count 1.1898 + for (nsIContent* child = aContainer->GetFirstChild(); 1.1899 + child; 1.1900 + child = child->GetNextSibling()) { 1.1901 + 1.1902 + if (nsContentUtils::HasNonEmptyAttr(child, kNameSpaceID_None, 1.1903 + nsGkAtoms::_template)) 1.1904 + break; 1.1905 + else 1.1906 + ++staticCount; 1.1907 + } 1.1908 + 1.1909 + if (mSortState.sortStaticsLast) { 1.1910 + // indicate that static XUL comes after RDF-generated content by 1.1911 + // making negative 1.1912 + staticCount = -staticCount; 1.1913 + } 1.1914 + 1.1915 + // save the "static" XUL element count hint 1.1916 + nsAutoString valueStr; 1.1917 + valueStr.AppendInt(staticCount); 1.1918 + aContainer->SetAttr(kNameSpaceID_None, nsGkAtoms::staticHint, valueStr, false); 1.1919 + } 1.1920 + 1.1921 + if (staticCount <= 0) { 1.1922 + numChildren += staticCount; 1.1923 + staticCount = 0; 1.1924 + } else if (staticCount > (int32_t)numChildren) { 1.1925 + staticCount = numChildren; 1.1926 + numChildren -= staticCount; 1.1927 + } 1.1928 + 1.1929 + // figure out where to insert the node when a sort order is being imposed 1.1930 + if (numChildren > 0) { 1.1931 + nsIContent *temp; 1.1932 + int32_t direction; 1.1933 + 1.1934 + // rjc says: The following is an implementation of a fairly optimal 1.1935 + // binary search insertion sort... with interpolation at either end-point. 1.1936 + 1.1937 + if (mSortState.lastWasFirst) { 1.1938 + child = aContainer->GetChildAt(staticCount); 1.1939 + temp = child; 1.1940 + rv = CompareResultToNode(aResult, temp, &direction); 1.1941 + if (direction < 0) { 1.1942 + aContainer->InsertChildAt(aNode, staticCount, aNotify); 1.1943 + childAdded = true; 1.1944 + } else 1.1945 + mSortState.lastWasFirst = false; 1.1946 + } else if (mSortState.lastWasLast) { 1.1947 + child = aContainer->GetChildAt(realNumChildren - 1); 1.1948 + temp = child; 1.1949 + rv = CompareResultToNode(aResult, temp, &direction); 1.1950 + if (direction > 0) { 1.1951 + aContainer->InsertChildAt(aNode, realNumChildren, aNotify); 1.1952 + childAdded = true; 1.1953 + } else 1.1954 + mSortState.lastWasLast = false; 1.1955 + } 1.1956 + 1.1957 + int32_t left = staticCount + 1, right = realNumChildren, x; 1.1958 + while (!childAdded && right >= left) { 1.1959 + x = (left + right) / 2; 1.1960 + child = aContainer->GetChildAt(x - 1); 1.1961 + temp = child; 1.1962 + 1.1963 + rv = CompareResultToNode(aResult, temp, &direction); 1.1964 + if ((x == left && direction < 0) || 1.1965 + (x == right && direction >= 0) || 1.1966 + left == right) 1.1967 + { 1.1968 + int32_t thePos = (direction > 0 ? x : x - 1); 1.1969 + aContainer->InsertChildAt(aNode, thePos, aNotify); 1.1970 + childAdded = true; 1.1971 + 1.1972 + mSortState.lastWasFirst = (thePos == staticCount); 1.1973 + mSortState.lastWasLast = (thePos >= realNumChildren); 1.1974 + 1.1975 + break; 1.1976 + } 1.1977 + if (direction < 0) 1.1978 + right = x - 1; 1.1979 + else 1.1980 + left = x + 1; 1.1981 + } 1.1982 + } 1.1983 + } 1.1984 + 1.1985 + // if the child hasn't been inserted yet, just add it at the end. Note 1.1986 + // that an append isn't done as there may be static content afterwards. 1.1987 + if (!childAdded) 1.1988 + aContainer->InsertChildAt(aNode, numChildren, aNotify); 1.1989 + 1.1990 + return NS_OK; 1.1991 +}