Index: mozilla/content/xul/document/src/nsXULDocument.cpp =================================================================== RCS file: /cvsroot/mozilla/content/xul/document/src/nsXULDocument.cpp,v retrieving revision 1.717 diff -pU500 -r1.717 mozilla/content/xul/document/src/nsXULDocument.cpp --- mozilla/content/xul/document/src/nsXULDocument.cpp +++ mozilla/content/xul/document/src/nsXULDocument.cpp @@ -889,1021 +889,1023 @@ nsXULDocument::AttributeChanged(nsIConte // First see if we need to update our element map. if ((aAttribute == nsXULAtoms::id) || (aAttribute == nsXULAtoms::ref)) { rv = mElementMap.Enumerate(RemoveElementsFromMapByContent, aElement); if (NS_FAILED(rv)) return; // That'll have removed _both_ the 'ref' and 'id' entries from // the map. So add 'em back now. rv = AddElementToMap(aElement); if (NS_FAILED(rv)) return; } // Synchronize broadcast listeners if (mBroadcasterMap && CanBroadcast(aNameSpaceID, aAttribute)) { nsCOMPtr domele = do_QueryInterface(aElement); BroadcasterMapEntry* entry = NS_STATIC_CAST(BroadcasterMapEntry*, PL_DHashTableOperate(mBroadcasterMap, domele.get(), PL_DHASH_LOOKUP)); if (PL_DHASH_ENTRY_IS_BUSY(entry)) { // We've got listeners: push the value. nsAutoString value; PRBool attrSet = aElement->GetAttr(kNameSpaceID_None, aAttribute, value); for (PRInt32 i = entry->mListeners.Count() - 1; i >= 0; --i) { BroadcastListener* bl = NS_STATIC_CAST(BroadcastListener*, entry->mListeners[i]); if ((bl->mAttribute == aAttribute) || (bl->mAttribute == nsXULAtoms::_asterix)) { nsCOMPtr listener = do_QueryInterface(bl->mListener); if (attrSet) { listener->SetAttr(kNameSpaceID_None, aAttribute, value, PR_TRUE); } else { listener->UnsetAttr(kNameSpaceID_None, aAttribute, PR_TRUE); } ExecuteOnBroadcastHandlerFor(aElement, bl->mListener, aAttribute); } } } } // Now notify external observers NS_DOCUMENT_NOTIFY_OBSERVERS(AttributeChanged, (this, aElement, aNameSpaceID, aAttribute, aModType)); // See if there is anything we need to persist in the localstore. // // XXX Namespace handling broken :-( nsAutoString persist; aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::persist, persist); if (!persist.IsEmpty()) { nsAutoString attr; rv = aAttribute->ToString(attr); if (NS_FAILED(rv)) return; // XXXldb This should check that it's a token, not just a substring. if (persist.Find(attr) >= 0) { rv = Persist(aElement, kNameSpaceID_None, aAttribute); if (NS_FAILED(rv)) return; } } } void nsXULDocument::ContentAppended(nsIContent* aContainer, PRInt32 aNewIndexInContainer) { // First update our element map PRUint32 count = aContainer->GetChildCount(); for (PRUint32 i = aNewIndexInContainer; i < count; ++i) { nsresult rv = AddSubtreeToDocument(aContainer->GetChildAt(i)); if (NS_FAILED(rv)) return; } nsXMLDocument::ContentAppended(aContainer, aNewIndexInContainer); } void nsXULDocument::ContentInserted(nsIContent* aContainer, nsIContent* aChild, PRInt32 aIndexInContainer) { nsresult rv = AddSubtreeToDocument(aChild); if (NS_FAILED(rv)) return; nsXMLDocument::ContentInserted(aContainer, aChild, aIndexInContainer); } void nsXULDocument::ContentRemoved(nsIContent* aContainer, nsIContent* aChild, PRInt32 aIndexInContainer) { nsresult rv; rv = RemoveSubtreeFromDocument(aChild); if (NS_FAILED(rv)) return; nsXMLDocument::ContentRemoved(aContainer, aChild, aIndexInContainer); } //---------------------------------------------------------------------- // // nsIXULDocument interface // NS_IMETHODIMP nsXULDocument::AddElementForID(const nsAString& aID, nsIContent* aElement) { NS_PRECONDITION(aElement != nsnull, "null ptr"); if (! aElement) return NS_ERROR_NULL_POINTER; mElementMap.Add(aID, aElement); return NS_OK; } NS_IMETHODIMP nsXULDocument::RemoveElementForID(const nsAString& aID, nsIContent* aElement) { NS_PRECONDITION(aElement != nsnull, "null ptr"); if (! aElement) return NS_ERROR_NULL_POINTER; mElementMap.Remove(aID, aElement); return NS_OK; } NS_IMETHODIMP nsXULDocument::GetElementsForID(const nsAString& aID, nsISupportsArray* aElements) { NS_PRECONDITION(aElements != nsnull, "null ptr"); if (! aElements) return NS_ERROR_NULL_POINTER; mElementMap.Find(aID, aElements); return NS_OK; } NS_IMETHODIMP nsXULDocument::AddForwardReference(nsForwardReference* aRef) { if (mResolutionPhase < aRef->GetPhase()) { mForwardReferences.AppendElement(aRef); } else { NS_ERROR("forward references have already been resolved"); delete aRef; } return NS_OK; } NS_IMETHODIMP nsXULDocument::ResolveForwardReferences() { if (mResolutionPhase == nsForwardReference::eDone) return NS_OK; // Resolve each outstanding 'forward' reference. We iterate // through the list of forward references until no more forward // references can be resolved. This annealing process is // guaranteed to converge because we've "closed the gate" to new // forward references. const nsForwardReference::Phase* pass = nsForwardReference::kPasses; while ((mResolutionPhase = *pass) != nsForwardReference::eDone) { PRInt32 previous = 0; while (mForwardReferences.Count() && mForwardReferences.Count() != previous) { previous = mForwardReferences.Count(); for (PRInt32 i = 0; i < mForwardReferences.Count(); ++i) { nsForwardReference* fwdref = NS_REINTERPRET_CAST(nsForwardReference*, mForwardReferences[i]); if (fwdref->GetPhase() == *pass) { nsForwardReference::Result result = fwdref->Resolve(); switch (result) { case nsForwardReference::eResolve_Succeeded: case nsForwardReference::eResolve_Error: mForwardReferences.RemoveElementAt(i); delete fwdref; // fixup because we removed from list --i; break; case nsForwardReference::eResolve_Later: // do nothing. we'll try again later ; } } } } ++pass; } DestroyForwardReferences(); return NS_OK; } NS_IMETHODIMP nsXULDocument::SetMasterPrototype(nsIXULPrototypeDocument* aDocument) { mMasterPrototype = aDocument; return NS_OK; } NS_IMETHODIMP nsXULDocument::GetMasterPrototype(nsIXULPrototypeDocument** aDocument) { *aDocument = mMasterPrototype; NS_IF_ADDREF(*aDocument); return NS_OK; } NS_IMETHODIMP nsXULDocument::SetCurrentPrototype(nsIXULPrototypeDocument* aDocument) { mCurrentPrototype = aDocument; return NS_OK; } //---------------------------------------------------------------------- // // nsIDOMDocument interface // NS_IMETHODIMP nsXULDocument::GetElementsByAttribute(const nsAString& aAttribute, const nsAString& aValue, nsIDOMNodeList** aReturn) { nsCOMPtr attrAtom(do_GetAtom(aAttribute)); NS_ENSURE_TRUE(attrAtom, NS_ERROR_OUT_OF_MEMORY); nsContentList *list = new nsContentList(this, MatchAttribute, aValue, nsnull, PR_TRUE, attrAtom, kNameSpaceID_None); NS_ENSURE_TRUE(list, NS_ERROR_OUT_OF_MEMORY); NS_ADDREF(*aReturn = list); return NS_OK; } NS_IMETHODIMP nsXULDocument::Persist(const nsAString& aID, const nsAString& aAttr) { // If we're currently reading persisted attributes out of the // localstore, _don't_ re-enter and try to set them again! if (mApplyingPersistedAttrs) return NS_OK; nsresult rv; nsCOMPtr domelement; rv = GetElementById(aID, getter_AddRefs(domelement)); if (NS_FAILED(rv)) return rv; if (! domelement) return NS_OK; nsCOMPtr element = do_QueryInterface(domelement); NS_ASSERTION(element != nsnull, "null ptr"); if (! element) return NS_ERROR_UNEXPECTED; nsCOMPtr tag; PRInt32 nameSpaceID; nsCOMPtr ni = element->GetExistingAttrNameFromQName(aAttr); if (ni) { tag = ni->NameAtom(); nameSpaceID = ni->NamespaceID(); } else { // Make sure that this QName is going to be valid. nsIParserService *parserService = nsContentUtils::GetParserService(); NS_ASSERTION(parserService, "Running scripts during shutdown?"); const PRUnichar *colon; rv = parserService->CheckQName(PromiseFlatString(aAttr), PR_TRUE, &colon); if (NS_FAILED(rv)) { // There was an invalid character or it was malformed. return NS_ERROR_INVALID_ARG; } if (colon) { // We don't really handle namespace qualifiers in attribute names. return NS_ERROR_NOT_IMPLEMENTED; } tag = do_GetAtom(aAttr); NS_ENSURE_TRUE(tag, NS_ERROR_OUT_OF_MEMORY); nameSpaceID = kNameSpaceID_None; } rv = Persist(element, nameSpaceID, tag); if (NS_FAILED(rv)) return rv; return NS_OK; } nsresult nsXULDocument::Persist(nsIContent* aElement, PRInt32 aNameSpaceID, nsIAtom* aAttribute) { // First make sure we _have_ a local store to stuff the persisted // information into. (We might not have one if profile information // hasn't been loaded yet...) if (! mLocalStore) return NS_OK; nsresult rv; nsCOMPtr element; rv = nsXULContentUtils::GetElementResource(aElement, getter_AddRefs(element)); if (NS_FAILED(rv)) return rv; // No ID, so nothing to persist. if (! element) return NS_OK; // Ick. Construct a property from the attribute. Punt on // namespaces for now. const char* attrstr; rv = aAttribute->GetUTF8String(&attrstr); if (NS_FAILED(rv)) return rv; // Don't bother with unreasonable attributes. We clamp long values, // but truncating attribute names turns it into a different attribute // so there's no point in persisting anything at all if (!attrstr || strlen(attrstr) > kMaxAttrNameLength) { NS_WARNING("Can't persist, Attribute name too long"); return NS_ERROR_ILLEGAL_VALUE; } nsCOMPtr attr; rv = gRDFService->GetResource(nsDependentCString(attrstr), getter_AddRefs(attr)); if (NS_FAILED(rv)) return rv; // Turn the value into a literal nsAutoString valuestr; aElement->GetAttr(kNameSpaceID_None, aAttribute, valuestr); // prevent over-long attributes that choke the parser (bug 319846) // (can't simply Truncate without testing, it's implemented // using SetLength and will grow a short string) if (valuestr.Length() > kMaxAttributeLength) { NS_WARNING("Truncating persisted attribute value"); valuestr.Truncate(kMaxAttributeLength); } // See if there was an old value... nsCOMPtr oldvalue; rv = mLocalStore->GetTarget(element, attr, PR_TRUE, getter_AddRefs(oldvalue)); if (NS_FAILED(rv)) return rv; if (oldvalue && valuestr.IsEmpty()) { // ...there was an oldvalue, and they've removed it. XXXThis // handling isn't quite right... rv = mLocalStore->Unassert(element, attr, oldvalue); } else { // Now either 'change' or 'assert' based on whether there was // an old value. nsCOMPtr newvalue; rv = gRDFService->GetLiteral(valuestr.get(), getter_AddRefs(newvalue)); if (NS_FAILED(rv)) return rv; if (oldvalue) { if (oldvalue != newvalue) rv = mLocalStore->Change(element, attr, oldvalue, newvalue); else rv = NS_OK; } else { rv = mLocalStore->Assert(element, attr, newvalue, PR_TRUE); } } if (NS_FAILED(rv)) return rv; // Add it to the persisted set for this document (if it's not // there already). { nsCAutoString docurl; rv = mDocumentURI->GetSpec(docurl); if (NS_FAILED(rv)) return rv; nsCOMPtr doc; rv = gRDFService->GetResource(docurl, getter_AddRefs(doc)); if (NS_FAILED(rv)) return rv; PRBool hasAssertion; rv = mLocalStore->HasAssertion(doc, kNC_persist, element, PR_TRUE, &hasAssertion); if (NS_FAILED(rv)) return rv; if (! hasAssertion) { rv = mLocalStore->Assert(doc, kNC_persist, element, PR_TRUE); if (NS_FAILED(rv)) return rv; } } return NS_OK; } nsresult nsXULDocument::DestroyForwardReferences() { for (PRInt32 i = mForwardReferences.Count() - 1; i >= 0; --i) { nsForwardReference* fwdref = NS_REINTERPRET_CAST(nsForwardReference*, mForwardReferences[i]); delete fwdref; } mForwardReferences.Clear(); return NS_OK; } nsresult nsXULDocument::GetPixelDimensions(nsIPresShell* aShell, PRInt32* aWidth, PRInt32* aHeight) { nsresult result = NS_OK; nsSize size; FlushPendingNotifications(Flush_Layout); nsIFrame* frame = aShell->GetPrimaryFrameFor(mRootContent); if (frame) { nsIView* view = frame->GetView(); // If we have a view check if it's scrollable. If not, // just use the view size itself if (view) { nsIScrollableView* scrollableView = view->ToScrollableView(); if (scrollableView) { scrollableView->GetScrolledView(view); } nsRect r = view->GetBounds(); size.height = r.height; size.width = r.width; } // If we don't have a view, use the frame size else { size = frame->GetSize(); } // Convert from twips to pixels float scale = aShell->GetPresContext()->TwipsToPixels(); *aWidth = NSTwipsToIntPixels(size.width, scale); *aHeight = NSTwipsToIntPixels(size.height, scale); } else { *aWidth = 0; *aHeight = 0; result = NS_ERROR_FAILURE; } return result; } NS_IMETHODIMP nsXULDocument::GetWidth(PRInt32* aWidth) { NS_ENSURE_ARG_POINTER(aWidth); + NS_ENSURE_STATE(mRootContent); nsresult rv = NS_OK; // We make the assumption that the first presentation shell // is the one for which we need information. nsIPresShell *shell = GetShellAt(0); if (shell) { PRInt32 width, height; rv = GetPixelDimensions(shell, &width, &height); *aWidth = width; } else *aWidth = 0; return rv; } NS_IMETHODIMP nsXULDocument::GetHeight(PRInt32* aHeight) { NS_ENSURE_ARG_POINTER(aHeight); + NS_ENSURE_STATE(mRootContent); nsresult rv = NS_OK; // We make the assumption that the first presentation shell // is the one for which we need information. nsIPresShell *shell = GetShellAt(0); if (shell) { PRInt32 width, height; rv = GetPixelDimensions(shell, &width, &height); *aHeight = height; } else *aHeight = 0; return rv; } //---------------------------------------------------------------------- // // nsIDOMXULDocument interface // NS_IMETHODIMP nsXULDocument::GetPopupNode(nsIDOMNode** aNode) { // Get popup node. nsresult rv = TrustedGetPopupNode(aNode); // addref happens here if (NS_SUCCEEDED(rv) && *aNode && !nsContentUtils::CanCallerAccess(*aNode)) { NS_RELEASE(*aNode); return NS_ERROR_DOM_SECURITY_ERR; } return rv; } NS_IMETHODIMP nsXULDocument::TrustedGetPopupNode(nsIDOMNode** aNode) { // Get the focus controller. nsCOMPtr focusController; GetFocusController(getter_AddRefs(focusController)); NS_ENSURE_TRUE(focusController, NS_ERROR_FAILURE); // Get the popup node. return focusController->GetPopupNode(aNode); // addref happens here } NS_IMETHODIMP nsXULDocument::SetPopupNode(nsIDOMNode* aNode) { nsresult rv; // get focus controller nsCOMPtr focusController; GetFocusController(getter_AddRefs(focusController)); NS_ENSURE_TRUE(focusController, NS_ERROR_FAILURE); // set popup node rv = focusController->SetPopupNode(aNode); return rv; } NS_IMETHODIMP nsXULDocument::GetPopupEvent(nsIDOMEvent** aEvent) { nsresult rv; nsCOMPtr focusController; GetFocusController(getter_AddRefs(focusController)); NS_ENSURE_TRUE(focusController, NS_ERROR_FAILURE); rv = focusController->GetPopupEvent(aEvent); return rv; } NS_IMETHODIMP nsXULDocument::SetPopupEvent(nsIDOMEvent* aEvent) { nsresult rv; nsCOMPtr focusController; GetFocusController(getter_AddRefs(focusController)); NS_ENSURE_TRUE(focusController, NS_ERROR_FAILURE); rv = focusController->SetPopupEvent(aEvent); return rv; } NS_IMETHODIMP nsXULDocument::GetTooltipNode(nsIDOMNode** aNode) { if (mTooltipNode && !nsContentUtils::CanCallerAccess(mTooltipNode)) { return NS_ERROR_DOM_SECURITY_ERR; } *aNode = mTooltipNode; NS_IF_ADDREF(*aNode); return NS_OK; } NS_IMETHODIMP nsXULDocument::TrustedGetTooltipNode(nsIDOMNode** aNode) { NS_IF_ADDREF(*aNode = mTooltipNode); return NS_OK; } NS_IMETHODIMP nsXULDocument::SetTooltipNode(nsIDOMNode* aNode) { mTooltipNode = aNode; return NS_OK; } NS_IMETHODIMP nsXULDocument::GetCommandDispatcher(nsIDOMXULCommandDispatcher** aTracker) { *aTracker = mCommandDispatcher; NS_IF_ADDREF(*aTracker); return NS_OK; } NS_IMETHODIMP nsXULDocument::GetElementById(const nsAString& aId, nsIDOMElement** aReturn) { NS_ENSURE_ARG_POINTER(aReturn); *aReturn = nsnull; NS_ASSERTION(!aId.IsEmpty(),"getElementById(\"\"), fix caller?"); if (aId.IsEmpty()) return NS_OK; nsresult rv; nsCOMPtr element; rv = mElementMap.FindFirst(aId, getter_AddRefs(element)); if (NS_FAILED(rv)) return rv; if (element) { rv = CallQueryInterface(element, aReturn); } return rv; } nsresult nsXULDocument::AddElementToDocumentPre(nsIContent* aElement) { // Do a bunch of work that's necessary when an element gets added // to the XUL Document. nsresult rv; // 1. Add the element to the resource-to-element map rv = AddElementToMap(aElement); if (NS_FAILED(rv)) return rv; // 2. If the element is a 'command updater' (i.e., has a // "commandupdater='true'" attribute), then add the element to the // document's command dispatcher if (aElement->AttrValueIs(kNameSpaceID_None, nsXULAtoms::commandupdater, nsXULAtoms::_true, eCaseMatters)) { rv = nsXULContentUtils::SetCommandUpdater(this, aElement); if (NS_FAILED(rv)) return rv; } // 3. Check for a broadcaster hookup attribute, in which case // we'll hook the node up as a listener on a broadcaster. PRBool listener, resolved; rv = CheckBroadcasterHookup(aElement, &listener, &resolved); if (NS_FAILED(rv)) return rv; // If it's not there yet, we may be able to defer hookup until // later. if (listener && !resolved && (mResolutionPhase != nsForwardReference::eDone)) { BroadcasterHookup* hookup = new BroadcasterHookup(this, aElement); if (! hookup) return NS_ERROR_OUT_OF_MEMORY; rv = AddForwardReference(hookup); if (NS_FAILED(rv)) return rv; } return NS_OK; } nsresult nsXULDocument::AddElementToDocumentPost(nsIContent* aElement) { // We need to pay special attention to the keyset tag to set up a listener if (aElement->NodeInfo()->Equals(nsXULAtoms::keyset, kNameSpaceID_XUL)) { // Create our XUL key listener and hook it up. nsCOMPtr xblService(do_GetService("@mozilla.org/xbl;1")); if (xblService) { nsCOMPtr rec(do_QueryInterface(aElement)); xblService->AttachGlobalKeyHandler(rec); } } // See if we need to attach a XUL template to this node PRBool needsHookup; nsresult rv = CheckTemplateBuilderHookup(aElement, &needsHookup); if (NS_FAILED(rv)) return rv; if (needsHookup) { if (mResolutionPhase == nsForwardReference::eDone) { rv = CreateTemplateBuilder(aElement); if (NS_FAILED(rv)) return rv; } else { TemplateBuilderHookup* hookup = new TemplateBuilderHookup(aElement); if (! hookup) return NS_ERROR_OUT_OF_MEMORY; rv = AddForwardReference(hookup); if (NS_FAILED(rv)) return rv; } } return NS_OK; } NS_IMETHODIMP nsXULDocument::AddSubtreeToDocument(nsIContent* aElement) { // From here on we only care about elements. if (!aElement->IsNodeOfType(nsINode::eELEMENT)) { return NS_OK; } // Do pre-order addition magic nsresult rv = AddElementToDocumentPre(aElement); if (NS_FAILED(rv)) return rv; // Recurse to children nsXULElement *xulcontent = nsXULElement::FromContent(aElement); PRUint32 count = xulcontent ? xulcontent->PeekChildCount() : aElement->GetChildCount(); while (count-- > 0) { rv = AddSubtreeToDocument(aElement->GetChildAt(count)); if (NS_FAILED(rv)) return rv; } // Do post-order addition magic return AddElementToDocumentPost(aElement); } NS_IMETHODIMP nsXULDocument::RemoveSubtreeFromDocument(nsIContent* aElement) { // From here on we only care about elements. if (!aElement->IsNodeOfType(nsINode::eELEMENT)) { return NS_OK; } // Do a bunch of cleanup to remove an element from the XUL // document. nsresult rv; // 1. Remove any children from the document. PRUint32 count = aElement->GetChildCount(); while (count-- > 0) { rv = RemoveSubtreeFromDocument(aElement->GetChildAt(count)); if (NS_FAILED(rv)) return rv; } // 2. Remove the element from the resource-to-element map rv = RemoveElementFromMap(aElement); if (NS_FAILED(rv)) return rv; // 3. If the element is a 'command updater', then remove the // element from the document's command dispatcher. if (aElement->AttrValueIs(kNameSpaceID_None, nsXULAtoms::commandupdater, nsXULAtoms::_true, eCaseMatters)) { nsCOMPtr domelement = do_QueryInterface(aElement); NS_ASSERTION(domelement != nsnull, "not a DOM element"); if (! domelement) return NS_ERROR_UNEXPECTED; rv = mCommandDispatcher->RemoveCommandUpdater(domelement); if (NS_FAILED(rv)) return rv; } // 4. Remove the element from our broadcaster map, since it is no longer // in the document. nsCOMPtr broadcaster, listener; nsAutoString attribute, broadcasterID; rv = FindBroadcaster(aElement, getter_AddRefs(listener), broadcasterID, attribute, getter_AddRefs(broadcaster)); if (rv == NS_FINDBROADCASTER_FOUND) { RemoveBroadcastListenerFor(broadcaster, listener, attribute); } return NS_OK; } NS_IMETHODIMP nsXULDocument::SetTemplateBuilderFor(nsIContent* aContent, nsIXULTemplateBuilder* aBuilder) { if (! mTemplateBuilderTable) { mTemplateBuilderTable = new nsSupportsHashtable(); if (! mTemplateBuilderTable) return NS_ERROR_OUT_OF_MEMORY; } nsISupportsKey key(aContent); if (aBuilder) { mTemplateBuilderTable->Put(&key, aBuilder); } else { mTemplateBuilderTable->Remove(&key); } return NS_OK; } NS_IMETHODIMP nsXULDocument::GetTemplateBuilderFor(nsIContent* aContent, nsIXULTemplateBuilder** aResult) { if (mTemplateBuilderTable) { nsISupportsKey key(aContent); *aResult = NS_STATIC_CAST(nsIXULTemplateBuilder*, mTemplateBuilderTable->Get(&key)); } else *aResult = nsnull; return NS_OK; } // Attributes that are used with getElementById() and the // resource-to-element map. nsIAtom** nsXULDocument::kIdentityAttrs[] = { &nsXULAtoms::id, &nsXULAtoms::ref, nsnull }; nsresult nsXULDocument::AddElementToMap(nsIContent* aElement) { // Look at the element's 'id' and 'ref' attributes, and if set, // add pointers in the resource-to-element map to the element. nsresult rv; for (PRInt32 i = 0; kIdentityAttrs[i] != nsnull; ++i) { nsAutoString value; aElement->GetAttr(kNameSpaceID_None, *kIdentityAttrs[i], value); if (!value.IsEmpty()) { rv = mElementMap.Add(value, aElement); if (NS_FAILED(rv)) return rv; } } return NS_OK; } nsresult nsXULDocument::RemoveElementFromMap(nsIContent* aElement) { // Remove the element from the resource-to-element map. nsresult rv; for (PRInt32 i = 0; kIdentityAttrs[i] != nsnull; ++i) { nsAutoString value; aElement->GetAttr(kNameSpaceID_None, *kIdentityAttrs[i], value); if (!value.IsEmpty()) { rv = mElementMap.Remove(value, aElement); if (NS_FAILED(rv)) return rv; } } return NS_OK; } PRIntn nsXULDocument::RemoveElementsFromMapByContent(const PRUnichar* aID, nsIContent* aElement, void* aClosure) { nsIContent* content = NS_REINTERPRET_CAST(nsIContent*, aClosure); return (aElement == content) ? HT_ENUMERATE_REMOVE : HT_ENUMERATE_NEXT; } //---------------------------------------------------------------------- // // nsIDOMNode interface // NS_IMETHODIMP nsXULDocument::CloneNode(PRBool aDeep, nsIDOMNode** aReturn) { // We don't allow cloning of a document *aReturn = nsnull; return NS_ERROR_DOM_NOT_SUPPORTED_ERR; } //---------------------------------------------------------------------- // // Implementation methods // nsresult nsXULDocument::Init() { nsresult rv = nsXMLDocument::Init(); NS_ENSURE_SUCCESS(rv, rv); // Create our command dispatcher and hook it up. rv = nsXULCommandDispatcher::Create(this, getter_AddRefs(mCommandDispatcher)); NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create a focus tracker"); if (NS_FAILED(rv)) return rv; // this _could_ fail; e.g., if we've tried to grab the local store // before profiles have initialized. If so, no big deal; nothing // will persist. mLocalStore = do_GetService(kLocalStoreCID); // Create a new nsISupportsArray for dealing with overlay references rv = NS_NewISupportsArray(getter_AddRefs(mUnloadedOverlays)); if (NS_FAILED(rv)) return rv; if (gRefCnt++ == 0) { // Keep the RDF service cached in a member variable to make using // it a bit less painful rv = CallGetService(kRDFServiceCID, &gRDFService); NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF Service"); if (NS_FAILED(rv)) return rv; gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "persist"), &kNC_persist); gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "attribute"), &kNC_attribute); gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "value"), &kNC_value); rv = CallGetService(kXULPrototypeCacheCID, &gXULCache); if (NS_FAILED(rv)) return rv; } #ifdef PR_LOGGING if (! gXULLog) gXULLog = PR_NewLogModule("nsXULDocument"); #endif return NS_OK; } nsresult nsXULDocument::StartLayout(void) { if (!mRootContent) { #ifdef PR_LOGGING if (PR_LOG_TEST(gXULLog, PR_LOG_WARNING)) { nsCAutoString urlspec; mDocumentURI->GetSpec(urlspec); PR_LOG(gXULLog, PR_LOG_WARNING, ("xul: unable to layout '%s'; no root content", urlspec.get())); } #endif return NS_OK; } PRUint32 count = GetNumberOfShells(); for (PRUint32 i = 0; i < count; ++i) { nsIPresShell *shell = GetShellAt(i); // Resize-reflow this time nsPresContext *cx = shell->GetPresContext(); NS_ASSERTION(cx != nsnull, "no pres context"); if (! cx) return NS_ERROR_UNEXPECTED; nsCOMPtr container = cx->GetContainer(); NS_ASSERTION(container != nsnull, "pres context has no container"); if (! container) return NS_ERROR_UNEXPECTED; @@ -2364,1630 +2366,1633 @@ nsXULDocument::PrepareToWalk() // Block onload until we've finished building the complete // document content model. BlockOnload(); } // There'd better not be anything on the context stack at this // point! This is the basis case for our "induction" in // ResumeWalk(), below, which'll assume that there's always a // content element on the context stack if either 1) we're in the // "master" document, or 2) we're in an overlay, and we've got // more than one prototype element (the single, root "overlay" // element) on the stack. NS_ASSERTION(mContextStack.Depth() == 0, "something's on the context stack already"); if (mContextStack.Depth() != 0) return NS_ERROR_UNEXPECTED; rv = mContextStack.Push(proto, root); if (NS_FAILED(rv)) return rv; return NS_OK; } nsresult nsXULDocument::AddChromeOverlays() { nsresult rv; nsCOMPtr docUri; rv = mCurrentPrototype->GetURI(getter_AddRefs(docUri)); NS_ENSURE_SUCCESS(rv, rv); /* overlays only apply to chrome, skip all content URIs */ if (!IsChromeURI(docUri)) return NS_OK; nsCOMPtr chromeReg(do_GetService(NS_CHROMEREGISTRY_CONTRACTID)); // In embedding situations, the chrome registry may not provide overlays, // or even exist at all; that's OK. NS_ENSURE_TRUE(chromeReg, NS_OK); nsCOMPtr overlays; rv = chromeReg->GetXULOverlays(docUri, getter_AddRefs(overlays)); NS_ENSURE_SUCCESS(rv, rv); PRBool moreOverlays; nsCOMPtr next; nsCOMPtr uri; while (NS_SUCCEEDED(rv = overlays->HasMoreElements(&moreOverlays)) && moreOverlays) { rv = overlays->GetNext(getter_AddRefs(next)); if (NS_FAILED(rv) || !next) continue; uri = do_QueryInterface(next); if (!uri) { NS_ERROR("Chrome registry handed me a non-nsIURI object!"); continue; } mUnloadedOverlays->AppendElement(uri); } return NS_OK; } NS_IMETHODIMP nsXULDocument::LoadOverlay(const nsAString& aURL, nsIObserver* aObserver) { nsresult rv; nsCOMPtr uri; rv = NS_NewURI(getter_AddRefs(uri), aURL, nsnull); if (NS_FAILED(rv)) return rv; if (aObserver) { nsIObserver* obs = nsnull; NS_ENSURE_TRUE(mOverlayLoadObservers.IsInitialized() || mOverlayLoadObservers.Init(), NS_ERROR_OUT_OF_MEMORY); obs = mOverlayLoadObservers.GetWeak(uri); if (obs) { // We don't support loading the same overlay twice into the same // document - that doesn't make sense anyway. return NS_ERROR_FAILURE; } mOverlayLoadObservers.Put(uri, aObserver); } PRBool shouldReturn, failureFromContent; rv = LoadOverlayInternal(uri, PR_TRUE, &shouldReturn, &failureFromContent); if (NS_FAILED(rv) && mOverlayLoadObservers.IsInitialized()) mOverlayLoadObservers.Remove(uri); // remove the observer if LoadOverlayInternal generated an error return rv; } nsresult nsXULDocument::LoadOverlayInternal(nsIURI* aURI, PRBool aIsDynamic, PRBool* aShouldReturn, PRBool* aFailureFromContent) { nsresult rv; *aShouldReturn = PR_FALSE; *aFailureFromContent = PR_FALSE; nsCOMPtr secMan = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); #ifdef PR_LOGGING if (PR_LOG_TEST(gXULLog, PR_LOG_DEBUG)) { nsCAutoString urlspec; aURI->GetSpec(urlspec); PR_LOG(gXULLog, PR_LOG_DEBUG, ("xul: loading overlay %s", urlspec.get())); } #endif if (aIsDynamic) mResolutionPhase = nsForwardReference::eStart; // Chrome documents are allowed to load overlays from anywhere. // Also, any document may load a chrome:// overlay. // In all other cases, the overlay is only allowed to load if // the master document and prototype document have the same origin. PRBool overlayIsChrome = IsChromeURI(aURI); if (!IsChromeURI(mDocumentURI) && !overlayIsChrome) { // Make sure we're allowed to load this overlay. rv = secMan->CheckSameOriginURI(mDocumentURI, aURI); if (NS_FAILED(rv)) { *aFailureFromContent = PR_TRUE; return rv; } } // Look in the prototype cache for the prototype document with // the specified overlay URI. if (overlayIsChrome) gXULCache->GetPrototype(aURI, getter_AddRefs(mCurrentPrototype)); else mCurrentPrototype = nsnull; // Same comment as nsChromeProtocolHandler::NewChannel and // nsXULDocument::StartDocumentLoad // - Ben Goodger // // We don't abort on failure here because there are too many valid // cases that can return failure, and the null-ness of |proto| is // enough to trigger the fail-safe parse-from-disk solution. // Example failure cases (for reference) include: // // NS_ERROR_NOT_AVAILABLE: the URI was not found in the FastLoad file, // parse from disk // other: the FastLoad file, XUL.mfl, could not be found, probably // due to being accessed before a profile has been selected // (e.g. loading chrome for the profile manager itself). // The .xul file must be parsed from disk. PRBool useXULCache; gXULCache->GetEnabled(&useXULCache); if (aIsDynamic) mIsWritingFastLoad = useXULCache; if (useXULCache && mCurrentPrototype) { PRBool loaded; rv = mCurrentPrototype->AwaitLoadDone(this, &loaded); if (NS_FAILED(rv)) return rv; if (! loaded) { // Return to the main event loop and eagerly await the // prototype overlay load's completion. When the content // sink completes, it will trigger an EndLoad(), which'll // wind us back up here, in ResumeWalk(). *aShouldReturn = PR_TRUE; return NS_OK; } // Found the overlay's prototype in the cache, fully loaded. rv = AddPrototypeSheets(); if (NS_FAILED(rv)) return rv; // Now prepare to walk the prototype to create its content rv = PrepareToWalk(); if (NS_FAILED(rv)) return rv; PR_LOG(gXULLog, PR_LOG_DEBUG, ("xul: overlay was cached")); // If this is a dynamic overlay and we have the prototype in the chrome // cache already, we must manually call ResumeWalk. if (aIsDynamic) return ResumeWalk(); } else { // Not there. Initiate a load. PR_LOG(gXULLog, PR_LOG_DEBUG, ("xul: overlay was not cached")); nsCOMPtr parser; rv = PrepareToLoadPrototype(aURI, "view", nsnull, getter_AddRefs(parser)); if (NS_FAILED(rv)) return rv; // Predicate mIsWritingFastLoad on the XUL cache being enabled, // so we don't have to re-check whether the cache is enabled all // the time. mIsWritingFastLoad = useXULCache; nsCOMPtr listener = do_QueryInterface(parser); if (! listener) return NS_ERROR_UNEXPECTED; // Add an observer to the parser; this'll get called when // Necko fires its On[Start|Stop]Request() notifications, // and will let us recover from a missing overlay. ParserObserver* parserObserver = new ParserObserver(this); if (! parserObserver) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(parserObserver); parser->Parse(aURI, parserObserver); NS_RELEASE(parserObserver); nsCOMPtr group = do_QueryReferent(mDocumentLoadGroup); rv = NS_OpenURI(listener, nsnull, aURI, nsnull, group); if (NS_FAILED(rv)) { // Abandon this prototype mCurrentPrototype = nsnull; // The parser won't get an OnStartRequest and // OnStopRequest, so it needs a Terminate. parser->Terminate(); // Just move on to the next overlay. NS_OpenURI could fail // just because a channel could not be opened, which can happen // if a file or chrome package does not exist. ReportMissingOverlay(aURI); *aFailureFromContent = PR_TRUE; return rv; } // If it's a 'chrome:' prototype document, then put it into // the prototype cache; other XUL documents will be reloaded // each time. We must do this after NS_OpenURI and AsyncOpen, // or chrome code will wrongly create a cached chrome channel // instead of a real one. if (useXULCache && overlayIsChrome) { rv = gXULCache->PutPrototype(mCurrentPrototype); if (NS_FAILED(rv)) return rv; } // Return to the main event loop and eagerly await the // overlay load's completion. When the content sink // completes, it will trigger an EndLoad(), which'll wind // us back in ResumeWalk(). if (!aIsDynamic) *aShouldReturn = PR_TRUE; } return NS_OK; } PR_STATIC_CALLBACK(PLDHashOperator) FirePendingMergeNotification(nsIURI* aKey, nsCOMPtr& aObserver, void* aClosure) { aObserver->Observe(aKey, "xul-overlay-merged", EmptyString().get()); typedef nsInterfaceHashtable table; table* observers = NS_STATIC_CAST(table*, aClosure); observers->Remove(aKey); return PL_DHASH_REMOVE; } nsresult nsXULDocument::ResumeWalk() { // Walk the prototype and build the delegate content model. The // walk is performed in a top-down, left-to-right fashion. That // is, a parent is built before any of its children; a node is // only built after all of its siblings to the left are fully // constructed. // // It is interruptable so that transcluded documents (e.g., // ) can be properly re-loaded if the // cached copy of the document becomes stale. nsresult rv; while (1) { // Begin (or resume) walking the current prototype. while (mContextStack.Depth() > 0) { // Look at the top of the stack to determine what we're // currently working on. nsXULPrototypeElement* proto; nsCOMPtr element; PRInt32 indx; rv = mContextStack.Peek(&proto, getter_AddRefs(element), &indx); if (NS_FAILED(rv)) return rv; if (indx >= (PRInt32)proto->mNumChildren) { if (element) { // We've processed all of the prototype's children. If // we're in the master prototype, do post-order // document-level hookup. (An overlay will get its // document hookup done when it's successfully // resolved.) if (mState == eState_Master) { AddElementToDocumentPost(element); } #ifdef MOZ_XTF if (element->GetNameSpaceID() > kNameSpaceID_LastBuiltin) { element->DoneAddingChildren(PR_FALSE); } #endif } // Now pop the context stack back up to the parent // element and continue the prototype walk. mContextStack.Pop(); continue; } // Grab the next child, and advance the current context stack // to the next sibling to our right. nsXULPrototypeNode* childproto = proto->mChildren[indx]; mContextStack.SetTopIndex(++indx); switch (childproto->mType) { case nsXULPrototypeNode::eType_Element: { // An 'element', which may contain more content. nsXULPrototypeElement* protoele = NS_REINTERPRET_CAST(nsXULPrototypeElement*, childproto); nsCOMPtr child; if ((mState == eState_Master) || (mContextStack.Depth() > 1)) { // We're in the master document -or -we're in an // overlay, and far enough down into the overlay's // content that we can simply build the delegates // and attach them to the parent node. NS_ASSERTION(element != nsnull, "no element on context stack"); rv = CreateElementFromPrototype(protoele, getter_AddRefs(child)); if (NS_FAILED(rv)) return rv; // ...and append it to the content model. rv = element->AppendChildTo(child, PR_FALSE); if (NS_FAILED(rv)) return rv; // do pre-order document-level hookup, but only if // we're in the master document. For an overlay, // this will happen when the overlay is // successfully resolved. if (mState == eState_Master) AddElementToDocumentPre(child); } else { // We're in the "first ply" of an overlay: the // "hookup" nodes. Create an 'overlay' element so // that we can continue to build content, and // enter a forward reference so we can hook it up // later. rv = CreateOverlayElement(protoele, getter_AddRefs(child)); if (NS_FAILED(rv)) return rv; } // If it has children, push the element onto the context // stack and begin to process them. if (protoele->mNumChildren > 0) { rv = mContextStack.Push(protoele, child); if (NS_FAILED(rv)) return rv; } else { if (mState == eState_Master) { // If there are no children, and we're in the // master document, do post-order document hookup // immediately. AddElementToDocumentPost(child); } #ifdef MOZ_XTF if (child && child->GetNameSpaceID() > kNameSpaceID_LastBuiltin) { child->DoneAddingChildren(PR_FALSE); } #endif } } break; case nsXULPrototypeNode::eType_Script: { // A script reference. Execute the script immediately; // this may have side effects in the content model. nsXULPrototypeScript* scriptproto = NS_REINTERPRET_CAST(nsXULPrototypeScript*, childproto); if (scriptproto->mSrcURI) { // A transcluded script reference; this may // "block" our prototype walk if the script isn't // cached, or the cached copy of the script is // stale and must be reloaded. PRBool blocked; rv = LoadScript(scriptproto, &blocked); // If the script cannot be loaded, just keep going! if (NS_SUCCEEDED(rv) && blocked) return NS_OK; } else if (scriptproto->mJSObject) { // An inline script rv = ExecuteScript(scriptproto->mJSObject); if (NS_FAILED(rv)) return rv; } } break; case nsXULPrototypeNode::eType_Text: { // A simple text node. if ((mState == eState_Master) || (mContextStack.Depth() > 1)) { // We're in the master document -or -we're in an // overlay, and far enough down into the overlay's // content that we can simply build the delegates // and attach them to the parent node. NS_ASSERTION(element, "no element on context stack"); nsCOMPtr text; rv = NS_NewTextNode(getter_AddRefs(text), mNodeInfoManager); NS_ENSURE_SUCCESS(rv, rv); nsXULPrototypeText* textproto = NS_REINTERPRET_CAST(nsXULPrototypeText*, childproto); text->SetText(textproto->mValue, PR_FALSE); nsCOMPtr child = do_QueryInterface(text); if (! child) return NS_ERROR_UNEXPECTED; rv = element->AppendChildTo(child, PR_FALSE); if (NS_FAILED(rv)) return rv; } } break; } } // Once we get here, the context stack will have been // depleted. That means that the entire prototype has been // walked and content has been constructed. // If we're not already, mark us as now processing overlays. mState = eState_Overlay; PRUint32 count; mUnloadedOverlays->Count(&count); // If there are no overlay URIs, then we're done. if (! count) break; nsCOMPtr uri = dont_AddRef(NS_REINTERPRET_CAST(nsIURI*, mUnloadedOverlays->ElementAt(count - 1))); mUnloadedOverlays->RemoveElementAt(count - 1); PRBool shouldReturn, failureFromContent; rv = LoadOverlayInternal(uri, PR_FALSE, &shouldReturn, &failureFromContent); if (failureFromContent) // The failure |rv| was the result of a problem in the content // rather than an unexpected problem in our implementation, so // just continue with the next overlay. continue; if (NS_FAILED(rv)) return rv; if (shouldReturn) return NS_OK; } // If we get here, there is nothing left for us to walk. The content // model is built and ready for layout. rv = ResolveForwardReferences(); if (NS_FAILED(rv)) return rv; rv = ApplyPersistentAttributes(); if (NS_FAILED(rv)) return rv; // XXXldb This is where we should really be setting the chromehidden // attribute. if (!mDocumentLoaded) { // Make sure we don't reenter here from StartLayout(). Note that // setting mDocumentLoaded to true here means that if StartLayout() // causes ResumeWalk() to be reentered, we'll take the other branch of // the |if (!mDocumentLoaded)| check above and since // mInitialLayoutComplete will be false will follow the else branch // there too. See the big comment there for how such reentry can // happen. mDocumentLoaded = PR_TRUE; - nsAutoString title; - mRootContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::title, title); - SetTitle(title); + if (mRootContent) { + nsAutoString title; + mRootContent->GetAttr(kNameSpaceID_None, nsHTMLAtoms::title, title); + SetTitle(title); + } StartLayout(); if (mIsWritingFastLoad && IsChromeURI(mDocumentURI)) gXULCache->WritePrototype(mMasterPrototype); NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad, (this)); DispatchContentLoadedEvents(); // Undo the onload-blocking we did in PrepareToWalk(). Note that we do // the unblocking sync because that's what the old code did. UnblockOnload(PR_TRUE); mInitialLayoutComplete = PR_TRUE; // Walk the set of pending load notifications and notify any observers. // See below for detail. if (mPendingOverlayLoadNotifications.IsInitialized()) mPendingOverlayLoadNotifications.Enumerate(FirePendingMergeNotification, (void*)&mOverlayLoadObservers); } else { if (mOverlayLoadObservers.IsInitialized()) { nsCOMPtr overlayURI; mCurrentPrototype->GetURI(getter_AddRefs(overlayURI)); nsCOMPtr obs; if (mInitialLayoutComplete) { // We have completed initial layout, so just send the notification. mOverlayLoadObservers.Get(overlayURI, getter_AddRefs(obs)); if (obs) obs->Observe(overlayURI, "xul-overlay-merged", EmptyString().get()); mOverlayLoadObservers.Remove(overlayURI); } else { // If we have not yet displayed the document for the first time // (i.e. we came in here as the result of a dynamic overlay load // which was spawned by a binding-attached event caused by // StartLayout() on the master prototype - we must remember that // this overlay has been merged and tell the listeners after // StartLayout() is completely finished rather than doing so // immediately - otherwise we may be executing code that needs to // access XBL Binding implementations on nodes for which frames // have not yet been constructed because their bindings have not // yet been attached. This can be a race condition because dynamic // overlay loading can take varying amounts of time depending on // whether or not the overlay prototype is in the XUL cache. The // most likely effect of this bug is odd UI initialization due to // methods and properties that do not work. // XXXbz really, we shouldn't be firing binding constructors // until after StartLayout returns! NS_ENSURE_TRUE(mPendingOverlayLoadNotifications.IsInitialized() || mPendingOverlayLoadNotifications.Init(), NS_ERROR_OUT_OF_MEMORY); mPendingOverlayLoadNotifications.Get(overlayURI, getter_AddRefs(obs)); if (!obs) { mOverlayLoadObservers.Get(overlayURI, getter_AddRefs(obs)); NS_ASSERTION(obs, "null overlay load observer?"); mPendingOverlayLoadNotifications.Put(overlayURI, obs); } } } } return rv; } void nsXULDocument::ReportMissingOverlay(nsIURI* aURI) { NS_PRECONDITION(aURI, "Must have a URI"); nsCAutoString spec; aURI->GetSpec(spec); NS_ConvertUTF8toUTF16 utfSpec(spec); const PRUnichar* params[] = { utfSpec.get() }; nsContentUtils::ReportToConsole(nsContentUtils::eXUL_PROPERTIES, "MissingOverlay", params, NS_ARRAY_LENGTH(params), mDocumentURI, EmptyString(), /* source line */ 0, /* line number */ 0, /* column number */ nsIScriptError::warningFlag, "XUL Document"); } nsresult nsXULDocument::LoadScript(nsXULPrototypeScript* aScriptProto, PRBool* aBlock) { // Load a transcluded script nsresult rv; if (aScriptProto->mJSObject) { rv = ExecuteScript(aScriptProto->mJSObject); // Ignore return value from execution, and don't block *aBlock = PR_FALSE; return NS_OK; } // Try the XUL script cache, in case two XUL documents source the same // .js file (e.g., strres.js from navigator.xul and utilityOverlay.xul). // XXXbe the cache relies on aScriptProto's GC root! PRBool useXULCache; gXULCache->GetEnabled(&useXULCache); if (useXULCache) { gXULCache->GetScript(aScriptProto->mSrcURI, NS_REINTERPRET_CAST(void**, &aScriptProto->mJSObject)); if (aScriptProto->mJSObject) { rv = ExecuteScript(aScriptProto->mJSObject); // Ignore return value from execution, and don't block *aBlock = PR_FALSE; return NS_OK; } } // Set the current script prototype so that OnStreamComplete can report // the right file if there are errors in the script. NS_ASSERTION(!mCurrentScriptProto, "still loading a script when starting another load?"); mCurrentScriptProto = aScriptProto; if (aScriptProto->mSrcLoading) { // Another XULDocument load has started, which is still in progress. // Remember to ResumeWalk this document when the load completes. mNextSrcLoadWaiter = aScriptProto->mSrcLoadWaiters; aScriptProto->mSrcLoadWaiters = this; NS_ADDREF_THIS(); } else { nsCOMPtr group = do_QueryReferent(mDocumentLoadGroup); // Note: the loader will keep itself alive while it's loading. nsCOMPtr loader; rv = NS_NewStreamLoader(getter_AddRefs(loader), aScriptProto->mSrcURI, this, nsnull, group); if (NS_FAILED(rv)) return rv; aScriptProto->mSrcLoading = PR_TRUE; } // Block until OnStreamComplete resumes us. *aBlock = PR_TRUE; return NS_OK; } NS_IMETHODIMP nsXULDocument::OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* context, nsresult aStatus, PRUint32 stringLen, const PRUint8* string) { nsCOMPtr request; aLoader->GetRequest(getter_AddRefs(request)); nsCOMPtr channel = do_QueryInterface(request); #ifdef DEBUG // print a load error on bad status if (NS_FAILED(aStatus)) { if (channel) { nsCOMPtr uri; channel->GetURI(getter_AddRefs(uri)); if (uri) { nsCAutoString uriSpec; uri->GetSpec(uriSpec); printf("Failed to load %s\n", uriSpec.get()); } } } #endif // This is the completion routine that will be called when a // transcluded script completes. Compile and execute the script // if the load was successful, then continue building content // from the prototype. nsresult rv; NS_ASSERTION(mCurrentScriptProto && mCurrentScriptProto->mSrcLoading, "script source not loading on unichar stream complete?"); // Clear mCurrentScriptProto now, but save it first for use below in // the compile/execute code, and in the while loop that resumes walks // of other documents that raced to load this script nsXULPrototypeScript* scriptProto = mCurrentScriptProto; mCurrentScriptProto = nsnull; // Clear the prototype's loading flag before executing the script or // resuming document walks, in case any of those control flows starts a // new script load. scriptProto->mSrcLoading = PR_FALSE; if (NS_SUCCEEDED(aStatus)) { // If the including XUL document is a FastLoad document, and we're // compiling an out-of-line script (one with src=...), then we must // be writing a new FastLoad file. If we were reading this script // from the FastLoad file, XULContentSinkImpl::OpenScript (over in // nsXULContentSink.cpp) would have already deserialized a non-null // script->mJSObject, causing control flow at the top of LoadScript // not to reach here. nsCOMPtr uri = scriptProto->mSrcURI; // XXX should also check nsIHttpChannel::requestSucceeded nsString stringStr; rv = nsScriptLoader::ConvertToUTF16(channel, string, stringLen, EmptyString(), this, stringStr); if (NS_SUCCEEDED(rv)) rv = scriptProto->Compile(stringStr.get(), stringStr.Length(), uri, 1, this, mCurrentPrototype); aStatus = rv; if (NS_SUCCEEDED(rv) && scriptProto->mJSObject) { rv = ExecuteScript(scriptProto->mJSObject); // If the XUL cache is enabled, save the script object there in // case different XUL documents source the same script. // // But don't save the script in the cache unless the master XUL // document URL is a chrome: URL. It is valid for a URL such as // about:config to translate into a master document URL, whose // prototype document nodes -- including prototype scripts that // hold GC roots protecting their mJSObject pointers -- are not // cached in the XUL prototype cache. See StartDocumentLoad, // the fillXULCache logic. // // A document such as about:config is free to load a script via // a URL such as chrome://global/content/config.js, and we must // not cache that script object without a prototype cache entry // containing a companion nsXULPrototypeScript node that owns a // GC root protecting the script object. Otherwise, the script // cache entry will dangle once the uncached prototype document // is released when its owning nsXULDocument is unloaded. // // (See http://bugzilla.mozilla.org/show_bug.cgi?id=98207 for // the true crime story.) PRBool useXULCache; gXULCache->GetEnabled(&useXULCache); if (useXULCache && IsChromeURI(mDocumentURI)) { gXULCache->PutScript(scriptProto->mSrcURI, NS_REINTERPRET_CAST(void*, scriptProto->mJSObject)); } if (mIsWritingFastLoad && mCurrentPrototype != mMasterPrototype) { // If we are loading an overlay script, try to serialize // it to the FastLoad file here. Master scripts will be // serialized when the master prototype document gets // written, at the bottom of ResumeWalk. That way, master // out-of-line scripts are serialized in the same order that // they'll be read, in the FastLoad file, which reduces the // number of seeks that dump the underlying stream's buffer. // // Ignore the return value, as we don't need to propagate // a failure to write to the FastLoad file, because this // method aborts that whole process on error. nsCOMPtr globalOwner = do_QueryInterface(mCurrentPrototype); nsIScriptGlobalObject* global = globalOwner->GetScriptGlobalObject(); NS_ASSERTION(global != nsnull, "master prototype w/o global?!"); if (global) { nsIScriptContext *scriptContext = global->GetContext(); if (scriptContext) scriptProto->SerializeOutOfLine(nsnull, scriptContext); } } } // ignore any evaluation errors } rv = ResumeWalk(); // Load a pointer to the prototype-script's list of nsXULDocuments who // raced to load the same script nsXULDocument** docp = &scriptProto->mSrcLoadWaiters; // Resume walking other documents that waited for this one's load, first // executing the script we just compiled, in each doc's script context nsXULDocument* doc; while ((doc = *docp) != nsnull) { NS_ASSERTION(doc->mCurrentScriptProto == scriptProto, "waiting for wrong script to load?"); doc->mCurrentScriptProto = nsnull; // Unlink doc from scriptProto's list before executing and resuming *docp = doc->mNextSrcLoadWaiter; doc->mNextSrcLoadWaiter = nsnull; // Execute only if we loaded and compiled successfully, then resume if (NS_SUCCEEDED(aStatus) && scriptProto->mJSObject) { doc->ExecuteScript(scriptProto->mJSObject); } doc->ResumeWalk(); NS_RELEASE(doc); } return rv; } nsresult nsXULDocument::ExecuteScript(JSObject* aScriptObject) { NS_PRECONDITION(aScriptObject != nsnull, "null ptr"); if (! aScriptObject) return NS_ERROR_NULL_POINTER; // Execute the precompiled script with the given version nsresult rv = NS_ERROR_UNEXPECTED; NS_ASSERTION(mScriptGlobalObject != nsnull, "no script global object"); nsCOMPtr context; if (mScriptGlobalObject && (context = mScriptGlobalObject->GetContext())) rv = context->ExecuteScript(aScriptObject, mScriptGlobalObject->GetGlobalJSObject(), nsnull, nsnull); return rv; } nsresult nsXULDocument::CreateElementFromPrototype(nsXULPrototypeElement* aPrototype, nsIContent** aResult) { // Create a content model element from a prototype element. NS_PRECONDITION(aPrototype != nsnull, "null ptr"); if (! aPrototype) return NS_ERROR_NULL_POINTER; *aResult = nsnull; nsresult rv = NS_OK; #ifdef PR_LOGGING if (PR_LOG_TEST(gXULLog, PR_LOG_NOTICE)) { nsAutoString tagstr; aPrototype->mNodeInfo->GetQualifiedName(tagstr); nsCAutoString tagstrC; tagstrC.AssignWithConversion(tagstr); PR_LOG(gXULLog, PR_LOG_NOTICE, ("xul: creating <%s> from prototype", tagstrC.get())); } #endif nsCOMPtr result; if (aPrototype->mNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) { // If it's a XUL element, it'll be lightweight until somebody // monkeys with it. rv = nsXULElement::Create(aPrototype, this, PR_TRUE, getter_AddRefs(result)); if (NS_FAILED(rv)) return rv; } else { // If it's not a XUL element, it's gonna be heavyweight no matter // what. So we need to copy everything out of the prototype // into the element. Get a nodeinfo from our nodeinfo manager // for this node. nsCOMPtr newNodeInfo; rv = mNodeInfoManager->GetNodeInfo(aPrototype->mNodeInfo->NameAtom(), aPrototype->mNodeInfo->GetPrefixAtom(), aPrototype->mNodeInfo->NamespaceID(), getter_AddRefs(newNodeInfo)); if (NS_FAILED(rv)) return rv; rv = NS_NewElement(getter_AddRefs(result), newNodeInfo->NamespaceID(), newNodeInfo); if (NS_FAILED(rv)) return rv; #ifdef MOZ_XTF if (result && newNodeInfo->NamespaceID() > kNameSpaceID_LastBuiltin) { result->BeginAddingChildren(); } #endif rv = AddAttributes(aPrototype, result); if (NS_FAILED(rv)) return rv; } result.swap(*aResult); return NS_OK; } nsresult nsXULDocument::CreateOverlayElement(nsXULPrototypeElement* aPrototype, nsIContent** aResult) { nsresult rv; // This doesn't really do anything except create a placeholder // element. I'd use an XML element, but it gets its knickers in a // knot with DOM ranges when you try to remove its children. nsCOMPtr element; rv = nsXULElement::Create(aPrototype, this, PR_FALSE, getter_AddRefs(element)); if (NS_FAILED(rv)) return rv; OverlayForwardReference* fwdref = new OverlayForwardReference(this, element); if (! fwdref) return NS_ERROR_OUT_OF_MEMORY; // transferring ownership to ya... rv = AddForwardReference(fwdref); if (NS_FAILED(rv)) return rv; *aResult = element; NS_ADDREF(*aResult); return NS_OK; } nsresult nsXULDocument::AddAttributes(nsXULPrototypeElement* aPrototype, nsIContent* aElement) { nsresult rv; for (PRUint32 i = 0; i < aPrototype->mNumAttributes; ++i) { nsXULPrototypeAttribute* protoattr = &(aPrototype->mAttributes[i]); nsAutoString valueStr; protoattr->mValue.ToString(valueStr); rv = aElement->SetAttr(protoattr->mName.NamespaceID(), protoattr->mName.LocalName(), protoattr->mName.GetPrefix(), valueStr, PR_FALSE); if (NS_FAILED(rv)) return rv; } return NS_OK; } nsresult nsXULDocument::CheckTemplateBuilderHookup(nsIContent* aElement, PRBool* aNeedsHookup) { // See if the element already has a `database' attribute. If it // does, then the template builder has already been created. // // XXX This approach will crash and burn (well, maybe not _that_ // bad) if aElement is not a XUL element. // // XXXvarga Do we still want to support non XUL content? nsCOMPtr xulElement = do_QueryInterface(aElement); if (xulElement) { nsCOMPtr ds; xulElement->GetDatabase(getter_AddRefs(ds)); if (ds) { *aNeedsHookup = PR_FALSE; return NS_OK; } } // Check aElement for a 'datasources' attribute, if it has // one a XUL template builder needs to be hooked up. *aNeedsHookup = aElement->HasAttr(kNameSpaceID_None, nsXULAtoms::datasources); return NS_OK; } nsresult nsXULDocument::CreateTemplateBuilder(nsIContent* aElement) { // Check if need to construct a tree builder or content builder. PRBool isTreeBuilder = PR_FALSE; PRInt32 nameSpaceID; nsCOMPtr baseTag; nsCOMPtr xblService = do_GetService("@mozilla.org/xbl;1"); if (xblService) { xblService->ResolveTag(aElement, &nameSpaceID, getter_AddRefs(baseTag)); } else { nsINodeInfo *ni = aElement->NodeInfo(); nameSpaceID = ni->NamespaceID(); baseTag = ni->NameAtom(); } if ((nameSpaceID == kNameSpaceID_XUL) && (baseTag == nsXULAtoms::tree)) { // By default, we build content for a tree and then we attach // the tree content view. However, if the `dont-build-content' // flag is set, then we we'll attach a tree builder which // directly implements the tree view. nsAutoString flags; aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::flags, flags); if (flags.Find(NS_LITERAL_STRING("dont-build-content")) >= 0) { isTreeBuilder = PR_TRUE; } } if (isTreeBuilder) { // Create and initialize a tree builder. nsCOMPtr builder = do_CreateInstance("@mozilla.org/xul/xul-tree-builder;1"); if (! builder) return NS_ERROR_FAILURE; builder->Init(aElement); // Create a if one isn't there already. // XXXvarga what about attributes? nsCOMPtr bodyContent; nsXULContentUtils::FindChildByTag(aElement, kNameSpaceID_XUL, nsXULAtoms::treechildren, getter_AddRefs(bodyContent)); if (! bodyContent) { // Get the document. nsIDocument *document = aElement->GetDocument(); NS_ASSERTION(document, "no document"); if (! document) return NS_ERROR_UNEXPECTED; nsresult rv = document->CreateElem(nsXULAtoms::treechildren, nsnull, kNameSpaceID_XUL, PR_FALSE, getter_AddRefs(bodyContent)); NS_ENSURE_SUCCESS(rv, rv); aElement->AppendChildTo(bodyContent, PR_FALSE); } } else { // Create and initialize a content builder. nsCOMPtr builder = do_CreateInstance("@mozilla.org/xul/xul-template-builder;1"); if (! builder) return NS_ERROR_FAILURE; builder->Init(aElement); nsXULElement *xulContent = nsXULElement::FromContent(aElement); if (xulContent) { // Mark the XUL element as being lazy, so the template builder // will run when layout first asks for these nodes. xulContent->SetLazyState(nsXULElement::eChildrenMustBeRebuilt); } else { // Force construction of immediate template sub-content _now_. builder->CreateContents(aElement); } } return NS_OK; } nsresult nsXULDocument::AddPrototypeSheets() { // Add mCurrentPrototype's style sheets to the document. nsresult rv; nsCOMPtr sheets; rv = mCurrentPrototype->GetStyleSheetReferences(getter_AddRefs(sheets)); if (NS_FAILED(rv)) return rv; PRUint32 count; sheets->Count(&count); for (PRUint32 i = 0; i < count; ++i) { nsISupports* isupports = sheets->ElementAt(i); nsCOMPtr uri = do_QueryInterface(isupports); NS_IF_RELEASE(isupports); NS_ASSERTION(uri, "not a URI!!!"); if (! uri) return NS_ERROR_UNEXPECTED; nsCAutoString spec; uri->GetAsciiSpec(spec); if (!IsChromeURI(uri)) { // These don't get to be in the prototype cache anyway... // and we can't load non-chrome sheets synchronously continue; } nsCOMPtr sheet; // If the sheet is a chrome URL, then we can refetch the sheet // synchronously, since we know the sheet is local. It's not // too late! :) If we're lucky, the loader will just pull it // from the prototype cache anyway. // Otherwise we just bail. It shouldn't currently // be possible to get into this situation for any reason // other than a skin switch anyway (since skin switching is the // only system that partially invalidates the XUL cache). // - dwh //XXXbz we hit this code from fastload all the time. Bug 183505. rv = CSSLoader()->LoadSheetSync(uri, getter_AddRefs(sheet)); // XXXldb We need to prevent bogus sheets from being held in the // prototype's list, but until then, don't propagate the failure // from LoadSheetSync (and thus exit the loop). if (NS_SUCCEEDED(rv)) { AddStyleSheet(sheet); } } return NS_OK; } //---------------------------------------------------------------------- // // nsXULDocument::OverlayForwardReference // nsForwardReference::Result nsXULDocument::OverlayForwardReference::Resolve() { + NS_ASSERTION(mDocument->mRootContent, "I just want to know if this can happen"); // Resolve a forward reference from an overlay element; attempt to // hook it up into the main document. nsresult rv; PRBool notify = PR_FALSE; nsIPresShell *shell = mDocument->GetShellAt(0); if (shell) shell->GetDidInitialReflow(¬ify); nsAutoString id; mOverlay->GetAttr(kNameSpaceID_None, nsXULAtoms::id, id); if (id.IsEmpty()) { // overlay had no id, use the root element mDocument->InsertElement(mDocument->mRootContent, mOverlay, notify); mResolved = PR_TRUE; return eResolve_Succeeded; } nsCOMPtr domtarget; rv = mDocument->GetElementById(id, getter_AddRefs(domtarget)); if (NS_FAILED(rv)) return eResolve_Error; // If we can't find the element in the document, defer the hookup // until later. if (! domtarget) return eResolve_Later; nsCOMPtr target = do_QueryInterface(domtarget); NS_ASSERTION(target != nsnull, "not an nsIContent"); if (! target) return eResolve_Error; rv = Merge(target, mOverlay, notify); if (NS_FAILED(rv)) return eResolve_Error; // Add child and any descendants to the element map rv = mDocument->AddSubtreeToDocument(target); if (NS_FAILED(rv)) return eResolve_Error; #ifdef PR_LOGGING if (PR_LOG_TEST(gXULLog, PR_LOG_NOTICE)) { nsCAutoString idC; idC.AssignWithConversion(id); PR_LOG(gXULLog, PR_LOG_NOTICE, ("xul: overlay resolved '%s'", idC.get())); } #endif mResolved = PR_TRUE; return eResolve_Succeeded; } nsresult nsXULDocument::OverlayForwardReference::Merge(nsIContent* aTargetNode, nsIContent* aOverlayNode, PRBool aNotify) { // This function is given: // aTargetNode: the node in the document whose 'id' attribute // matches a toplevel node in our overlay. // aOverlayNode: the node in the overlay document that matches // a node in the actual document. // aNotify: whether or not content manipulation methods should // use the aNotify parameter. After the initial // reflow (i.e. in the dynamic overlay merge case), // we want all the content manipulation methods we // call to notify so that frames are constructed // etc. Otherwise do not, since that's during initial // document construction before StartLayout has been // called which will do everything for us. // // This function merges the tree from the overlay into the tree in // the document, overwriting attributes and appending child content // nodes appropriately. (See XUL overlay reference for details) nsresult rv; // Merge attributes from the overlay content node to that of the // actual document. PRUint32 i; const nsAttrName* name; for (i = 0; (name = aOverlayNode->GetAttrNameAt(i)); ++i) { // We don't want to swap IDs, they should be the same. if (name->Equals(nsXULAtoms::id)) continue; PRInt32 nameSpaceID = name->NamespaceID(); nsIAtom* attr = name->LocalName(); nsIAtom* prefix = name->GetPrefix(); nsAutoString value; aOverlayNode->GetAttr(nameSpaceID, attr, value); // Element in the overlay has the 'removeelement' attribute set // so remove it from the actual document. if (attr == nsXULAtoms::removeelement && value.EqualsLiteral("true")) { rv = RemoveElement(aTargetNode->GetParent(), aTargetNode); if (NS_FAILED(rv)) return rv; return NS_OK; } rv = aTargetNode->SetAttr(nameSpaceID, attr, prefix, value, aNotify); if (NS_FAILED(rv)) return rv; } // Walk our child nodes, looking for elements that have the 'id' // attribute set. If we find any, we must do a parent check in the // actual document to ensure that the structure matches that of // the actual document. If it does, we can call ourselves and attempt // to merge inside that subtree. If not, we just append the tree to // the parent like any other. PRUint32 childCount = aOverlayNode->GetChildCount(); // This must be a strong reference since it will be the only // reference to a content object during part of this loop. nsCOMPtr currContent; for (i = 0; i < childCount; ++i) { currContent = aOverlayNode->GetChildAt(0); nsAutoString id; currContent->GetAttr(kNameSpaceID_None, nsXULAtoms::id, id); nsCOMPtr nodeInDocument; if (!id.IsEmpty()) { nsCOMPtr domDocument( do_QueryInterface(aTargetNode->GetDocument())); if (!domDocument) return NS_ERROR_FAILURE; rv = domDocument->GetElementById(id, getter_AddRefs(nodeInDocument)); if (NS_FAILED(rv)) return rv; } // The item has an 'id' attribute set, and we need to check with // the actual document to see if an item with this id exists at // this locale. If so, we want to merge the subtree under that // node. Otherwise, we just do an append as if the element had // no id attribute. if (nodeInDocument) { // Given two parents, aTargetNode and aOverlayNode, we want // to call merge on currContent if we find an associated // node in the document with the same id as currContent that // also has aTargetNode as its parent. nsCOMPtr nodeParent; rv = nodeInDocument->GetParentNode(getter_AddRefs(nodeParent)); if (NS_FAILED(rv)) return rv; nsCOMPtr elementParent(do_QueryInterface(nodeParent)); nsAutoString parentID; elementParent->GetAttribute(NS_LITERAL_STRING("id"), parentID); if (aTargetNode->AttrValueIs(kNameSpaceID_None, nsXULAtoms::id, parentID, eCaseMatters)) { // The element matches. "Go Deep!" nsCOMPtr childDocumentContent(do_QueryInterface(nodeInDocument)); rv = Merge(childDocumentContent, currContent, aNotify); if (NS_FAILED(rv)) return rv; rv = aOverlayNode->RemoveChildAt(0, PR_FALSE); if (NS_FAILED(rv)) return rv; continue; } } rv = aOverlayNode->RemoveChildAt(0, PR_FALSE); if (NS_FAILED(rv)) return rv; rv = InsertElement(aTargetNode, currContent, aNotify); if (NS_FAILED(rv)) return rv; } return NS_OK; } nsXULDocument::OverlayForwardReference::~OverlayForwardReference() { #ifdef PR_LOGGING if (PR_LOG_TEST(gXULLog, PR_LOG_WARNING) && !mResolved) { nsAutoString id; mOverlay->GetAttr(kNameSpaceID_None, nsXULAtoms::id, id); nsCAutoString idC; idC.AssignWithConversion(id); PR_LOG(gXULLog, PR_LOG_WARNING, ("xul: overlay failed to resolve '%s'", idC.get())); } #endif } //---------------------------------------------------------------------- // // nsXULDocument::BroadcasterHookup // nsForwardReference::Result nsXULDocument::BroadcasterHookup::Resolve() { nsresult rv; PRBool listener; rv = mDocument->CheckBroadcasterHookup(mObservesElement, &listener, &mResolved); if (NS_FAILED(rv)) return eResolve_Error; return mResolved ? eResolve_Succeeded : eResolve_Later; } nsXULDocument::BroadcasterHookup::~BroadcasterHookup() { #ifdef PR_LOGGING if (PR_LOG_TEST(gXULLog, PR_LOG_WARNING) && !mResolved) { // Tell the world we failed nsresult rv; nsIAtom *tag = mObservesElement->Tag(); nsAutoString broadcasterID; nsAutoString attribute; if (tag == nsXULAtoms::observes) { mObservesElement->GetAttr(kNameSpaceID_None, nsXULAtoms::element, broadcasterID); mObservesElement->GetAttr(kNameSpaceID_None, nsXULAtoms::attribute, attribute); } else { mObservesElement->GetAttr(kNameSpaceID_None, nsXULAtoms::observes, broadcasterID); attribute.AssignLiteral("*"); } nsAutoString tagStr; rv = tag->ToString(tagStr); if (NS_FAILED(rv)) return; nsCAutoString tagstrC, attributeC,broadcasteridC; tagstrC.AssignWithConversion(tagStr); attributeC.AssignWithConversion(attribute); broadcasteridC.AssignWithConversion(broadcasterID); PR_LOG(gXULLog, PR_LOG_WARNING, ("xul: broadcaster hookup failed <%s attribute='%s'> to %s", tagstrC.get(), attributeC.get(), broadcasteridC.get())); } #endif } //---------------------------------------------------------------------- // // nsXULDocument::TemplateBuilderHookup // nsForwardReference::Result nsXULDocument::TemplateBuilderHookup::Resolve() { PRBool needsHookup; nsresult rv = CheckTemplateBuilderHookup(mElement, &needsHookup); if (NS_FAILED(rv)) return eResolve_Error; if (needsHookup) { rv = CreateTemplateBuilder(mElement); if (NS_FAILED(rv)) return eResolve_Error; } return eResolve_Succeeded; } //---------------------------------------------------------------------- nsresult nsXULDocument::FindBroadcaster(nsIContent* aElement, nsIDOMElement** aListener, nsString& aBroadcasterID, nsString& aAttribute, nsIDOMElement** aBroadcaster) { NS_ASSERTION(aElement->IsNodeOfType(nsINode::eELEMENT), "Only pass elements into FindBroadcaster!"); nsresult rv; nsINodeInfo *ni = aElement->NodeInfo(); *aListener = nsnull; *aBroadcaster = nsnull; if (ni->Equals(nsXULAtoms::observes, kNameSpaceID_XUL)) { // It's an element, which means that the actual // listener is the _parent_ node. This element should have an // 'element' attribute that specifies the ID of the // broadcaster element, and an 'attribute' element, which // specifies the name of the attribute to observe. nsIContent* parent = aElement->GetParent(); // If we're still parented by an 'overlay' tag, then we haven't // made it into the real document yet. Defer hookup. if (parent->NodeInfo()->Equals(nsXULAtoms::overlay, kNameSpaceID_XUL)) { return NS_FINDBROADCASTER_AWAIT_OVERLAYS; } if (NS_FAILED(CallQueryInterface(parent, aListener))) *aListener = nsnull; aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::element, aBroadcasterID); aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::attribute, aAttribute); } else { // It's a generic element, which means that we'll use the // value of the 'observes' attribute to determine the ID of // the broadcaster element, and we'll watch _all_ of its // values. aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::observes, aBroadcasterID); // Bail if there's no aBroadcasterID if (aBroadcasterID.IsEmpty()) { // Try the command attribute next. aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::command, aBroadcasterID); if (!aBroadcasterID.IsEmpty()) { // We've got something in the command attribute. We // only treat this as a normal broadcaster if we are // not a menuitem or a key. if (ni->Equals(nsXULAtoms::menuitem, kNameSpaceID_XUL) || ni->Equals(nsXULAtoms::key, kNameSpaceID_XUL)) { return NS_FINDBROADCASTER_NOT_FOUND; } } else { return NS_FINDBROADCASTER_NOT_FOUND; } } if (NS_FAILED(CallQueryInterface(aElement, aListener))) *aListener = nsnull; aAttribute.AssignLiteral("*"); } // Make sure we got a valid listener. NS_ENSURE_TRUE(*aListener, NS_ERROR_UNEXPECTED); // Try to find the broadcaster element in the document. rv = GetElementById(aBroadcasterID, aBroadcaster); if (NS_FAILED(rv)) return rv; // If we can't find the broadcaster, then we'll need to defer the // hookup. We may need to resolve some of the other overlays // first. if (! *aBroadcaster) { return NS_FINDBROADCASTER_AWAIT_OVERLAYS; } return NS_FINDBROADCASTER_FOUND; } nsresult nsXULDocument::CheckBroadcasterHookup(nsIContent* aElement, PRBool* aNeedsHookup, PRBool* aDidResolve) { // Resolve a broadcaster hookup. Look at the element that we're // trying to resolve: it could be an '' element, or just // a vanilla element with an 'observes' attribute on it. nsresult rv; *aDidResolve = PR_FALSE; nsCOMPtr listener; nsAutoString broadcasterID; nsAutoString attribute; nsCOMPtr broadcaster; rv = FindBroadcaster(aElement, getter_AddRefs(listener), broadcasterID, attribute, getter_AddRefs(broadcaster)); switch (rv) { case NS_FINDBROADCASTER_NOT_FOUND: *aNeedsHookup = PR_FALSE; return NS_OK; case NS_FINDBROADCASTER_AWAIT_OVERLAYS: *aNeedsHookup = PR_TRUE; return NS_OK; case NS_FINDBROADCASTER_FOUND: break; default: return rv; } rv = AddBroadcastListenerFor(broadcaster, listener, attribute); if (NS_FAILED(rv)) return rv; #ifdef PR_LOGGING // Tell the world we succeeded if (PR_LOG_TEST(gXULLog, PR_LOG_NOTICE)) { nsCOMPtr content = do_QueryInterface(listener); NS_ASSERTION(content != nsnull, "not an nsIContent"); if (! content) return rv; nsAutoString tagStr; rv = content->Tag()->ToString(tagStr); if (NS_FAILED(rv)) return rv; nsCAutoString tagstrC, attributeC,broadcasteridC; tagstrC.AssignWithConversion(tagStr); attributeC.AssignWithConversion(attribute); broadcasteridC.AssignWithConversion(broadcasterID); PR_LOG(gXULLog, PR_LOG_NOTICE, ("xul: broadcaster hookup <%s attribute='%s'> to %s", tagstrC.get(), attributeC.get(), broadcasteridC.get())); } #endif *aNeedsHookup = PR_FALSE; *aDidResolve = PR_TRUE; return NS_OK; } nsresult nsXULDocument::InsertElement(nsIContent* aParent, nsIContent* aChild, PRBool aNotify) { // Insert aChild appropriately into aParent, accounting for a // 'pos' attribute set on aChild. nsresult rv; nsAutoString posStr; PRBool wasInserted = PR_FALSE; // insert after an element of a given id aChild->GetAttr(kNameSpaceID_None, nsXULAtoms::insertafter, posStr); PRBool isInsertAfter = PR_TRUE; if (posStr.IsEmpty()) { aChild->GetAttr(kNameSpaceID_None, nsXULAtoms::insertbefore, posStr); isInsertAfter = PR_FALSE; } if (!posStr.IsEmpty()) { nsCOMPtr domDocument( do_QueryInterface(aParent->GetDocument())); nsCOMPtr domElement; char* str = ToNewCString(posStr); char* rest; char* token = nsCRT::strtok(str, ", ", &rest); while (token) { rv = domDocument->GetElementById(NS_ConvertASCIItoUTF16(token), getter_AddRefs(domElement)); if (domElement) break; token = nsCRT::strtok(rest, ", ", &rest); } nsMemory::Free(str); if (NS_FAILED(rv)) return rv; if (domElement) { nsCOMPtr content(do_QueryInterface(domElement)); NS_ASSERTION(content != nsnull, "null ptr"); if (!content) return NS_ERROR_UNEXPECTED; PRInt32 pos = aParent->IndexOf(content); if (pos != -1) { pos = isInsertAfter ? pos + 1 : pos; rv = aParent->InsertChildAt(aChild, pos, aNotify); if (NS_FAILED(rv)) return rv; wasInserted = PR_TRUE; } } } if (!wasInserted) { aChild->GetAttr(kNameSpaceID_None, nsXULAtoms::position, posStr); if (!posStr.IsEmpty()) { // Positions are one-indexed. PRInt32 pos = posStr.ToInteger(NS_REINTERPRET_CAST(PRInt32*, &rv)); // Note: if the insertion index (which is |pos - 1|) would be less