diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /dom/xul | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/xul')
398 files changed, 53747 insertions, 0 deletions
diff --git a/dom/xul/XULDocument.cpp b/dom/xul/XULDocument.cpp new file mode 100644 index 000000000..ae3cdb7eb --- /dev/null +++ b/dom/xul/XULDocument.cpp @@ -0,0 +1,4591 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=4 sw=4 et tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + + An implementation for the XUL document. This implementation serves + as the basis for generating an NGLayout content model. + + Notes + ----- + + 1. We do some monkey business in the document observer methods to + keep the element map in sync for HTML elements. Why don't we just + do it for _all_ elements? Well, in the case of XUL elements, + which may be lazily created during frame construction, the + document observer methods will never be called because we'll be + adding the XUL nodes into the content model "quietly". + +*/ + +#include "mozilla/ArrayUtils.h" + +#include "XULDocument.h" + +#include "nsError.h" +#include "nsIBoxObject.h" +#include "nsIChromeRegistry.h" +#include "nsView.h" +#include "nsViewManager.h" +#include "nsIContentViewer.h" +#include "nsIDOMXULElement.h" +#include "nsIStreamListener.h" +#include "nsITimer.h" +#include "nsDocShell.h" +#include "nsGkAtoms.h" +#include "nsXMLContentSink.h" +#include "nsXULContentSink.h" +#include "nsXULContentUtils.h" +#include "nsIXULOverlayProvider.h" +#include "nsIStringEnumerator.h" +#include "nsNetUtil.h" +#include "nsParserCIID.h" +#include "nsPIBoxObject.h" +#include "mozilla/dom/BoxObject.h" +#include "nsXPIDLString.h" +#include "nsPIDOMWindow.h" +#include "nsPIWindowRoot.h" +#include "nsXULCommandDispatcher.h" +#include "nsXULElement.h" +#include "mozilla/Logging.h" +#include "rdf.h" +#include "nsIFrame.h" +#include "nsXBLService.h" +#include "nsCExternalHandlerService.h" +#include "nsMimeTypes.h" +#include "nsIObjectInputStream.h" +#include "nsIObjectOutputStream.h" +#include "nsContentList.h" +#include "nsIScriptGlobalObject.h" +#include "nsIScriptSecurityManager.h" +#include "nsNodeInfoManager.h" +#include "nsContentCreatorFunctions.h" +#include "nsContentUtils.h" +#include "nsIParser.h" +#include "nsCharsetSource.h" +#include "nsIParserService.h" +#include "mozilla/StyleSheetInlines.h" +#include "mozilla/css/Loader.h" +#include "nsIScriptError.h" +#include "nsIStyleSheetLinkingElement.h" +#include "nsIObserverService.h" +#include "nsNodeUtils.h" +#include "nsIDocShellTreeOwner.h" +#include "nsIXULWindow.h" +#include "nsXULPopupManager.h" +#include "nsCCUncollectableMarker.h" +#include "nsURILoader.h" +#include "mozilla/AddonPathService.h" +#include "mozilla/BasicEvents.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/NodeInfoInlines.h" +#include "mozilla/dom/ProcessingInstruction.h" +#include "mozilla/dom/ScriptSettings.h" +#include "mozilla/dom/XULDocumentBinding.h" +#include "mozilla/EventDispatcher.h" +#include "mozilla/LoadInfo.h" +#include "mozilla/Preferences.h" +#include "nsTextNode.h" +#include "nsJSUtils.h" +#include "mozilla/dom/URL.h" +#include "nsIContentPolicy.h" +#include "mozAutoDocUpdate.h" +#include "xpcpublic.h" +#include "mozilla/StyleSheet.h" +#include "mozilla/StyleSheetInlines.h" + +using namespace mozilla; +using namespace mozilla::dom; + +//---------------------------------------------------------------------- +// +// CIDs +// + +static NS_DEFINE_CID(kParserCID, NS_PARSER_CID); + +static bool IsOverlayAllowed(nsIURI* aURI) +{ + bool canOverlay = false; + if (NS_SUCCEEDED(aURI->SchemeIs("about", &canOverlay)) && canOverlay) + return true; + if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &canOverlay)) && canOverlay) + return true; + return false; +} + +//---------------------------------------------------------------------- +// +// Miscellaneous Constants +// + +const nsForwardReference::Phase nsForwardReference::kPasses[] = { + nsForwardReference::eConstruction, + nsForwardReference::eHookup, + nsForwardReference::eDone +}; + +//---------------------------------------------------------------------- +// +// Statics +// + +int32_t XULDocument::gRefCnt = 0; + +LazyLogModule XULDocument::gXULLog("XULDocument"); + +//---------------------------------------------------------------------- + +struct BroadcastListener { + nsWeakPtr mListener; + nsCOMPtr<nsIAtom> mAttribute; +}; + +struct BroadcasterMapEntry : public PLDHashEntryHdr +{ + Element* mBroadcaster; // [WEAK] + nsTArray<BroadcastListener*> mListeners; // [OWNING] of BroadcastListener objects +}; + +Element* +nsRefMapEntry::GetFirstElement() +{ + return mRefContentList.SafeElementAt(0); +} + +void +nsRefMapEntry::AppendAll(nsCOMArray<nsIContent>* aElements) +{ + for (size_t i = 0; i < mRefContentList.Length(); ++i) { + aElements->AppendObject(mRefContentList[i]); + } +} + +bool +nsRefMapEntry::AddElement(Element* aElement) +{ + if (mRefContentList.Contains(aElement)) { + return true; + } + return mRefContentList.AppendElement(aElement); +} + +bool +nsRefMapEntry::RemoveElement(Element* aElement) +{ + mRefContentList.RemoveElement(aElement); + return mRefContentList.IsEmpty(); +} + +//---------------------------------------------------------------------- +// +// ctors & dtors +// + +namespace mozilla { +namespace dom { + +XULDocument::XULDocument(void) + : XMLDocument("application/vnd.mozilla.xul+xml"), + mDocLWTheme(Doc_Theme_Uninitialized), + mState(eState_Master), + mResolutionPhase(nsForwardReference::eStart) +{ + // NOTE! nsDocument::operator new() zeroes out all members, so don't + // bother initializing members to 0. + + // Override the default in nsDocument + mCharacterSet.AssignLiteral("UTF-8"); + + mDefaultElementType = kNameSpaceID_XUL; + mType = eXUL; + + mDelayFrameLoaderInitialization = true; + + mAllowXULXBL = eTriTrue; +} + +XULDocument::~XULDocument() +{ + NS_ASSERTION(mNextSrcLoadWaiter == nullptr, + "unreferenced document still waiting for script source to load?"); + + // In case we failed somewhere early on and the forward observer + // decls never got resolved. + mForwardReferences.Clear(); + // Likewise for any references we have to IDs where we might + // look for persisted data: + mPersistenceIds.Clear(); + + // Destroy our broadcaster map. + delete mBroadcasterMap; + + delete mTemplateBuilderTable; + + Preferences::UnregisterCallback(XULDocument::DirectionChanged, + "intl.uidirection.", this); + + if (mOffThreadCompileStringBuf) { + js_free(mOffThreadCompileStringBuf); + } +} + +} // namespace dom +} // namespace mozilla + +nsresult +NS_NewXULDocument(nsIXULDocument** result) +{ + NS_PRECONDITION(result != nullptr, "null ptr"); + if (! result) + return NS_ERROR_NULL_POINTER; + + RefPtr<XULDocument> doc = new XULDocument(); + + nsresult rv; + if (NS_FAILED(rv = doc->Init())) { + return rv; + } + + doc.forget(result); + return NS_OK; +} + + +namespace mozilla { +namespace dom { + +//---------------------------------------------------------------------- +// +// nsISupports interface +// + +NS_IMPL_CYCLE_COLLECTION_CLASS(XULDocument) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XULDocument, XMLDocument) + NS_ASSERTION(!nsCCUncollectableMarker::InGeneration(cb, tmp->GetMarkedCCGeneration()), + "Shouldn't traverse XULDocument!"); + // XXX tmp->mForwardReferences? + // XXX tmp->mContextStack? + + // An element will only have a template builder as long as it's in the + // document, so we'll traverse the table here instead of from the element. + if (tmp->mTemplateBuilderTable) { + for (auto iter = tmp->mTemplateBuilderTable->Iter(); + !iter.Done(); + iter.Next()) { + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mTemplateBuilderTable key"); + cb.NoteXPCOMChild(iter.Key()); + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mTemplateBuilderTable value"); + cb.NoteXPCOMChild(iter.UserData()); + } + } + + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCurrentPrototype) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMasterPrototype) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCommandDispatcher) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrototypes) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStore) + + if (tmp->mOverlayLoadObservers) { + for (auto iter = tmp->mOverlayLoadObservers->Iter(); + !iter.Done(); + iter.Next()) { + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mOverlayLoadObservers value"); + cb.NoteXPCOMChild(iter.Data()); + } + } + if (tmp->mPendingOverlayLoadNotifications) { + for (auto iter = tmp->mPendingOverlayLoadNotifications->Iter(); + !iter.Done(); + iter.Next()) { + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mPendingOverlayLoadNotifications value"); + cb.NoteXPCOMChild(iter.Data()); + } + } +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XULDocument, XMLDocument) + delete tmp->mTemplateBuilderTable; + tmp->mTemplateBuilderTable = nullptr; + + NS_IMPL_CYCLE_COLLECTION_UNLINK(mCommandDispatcher) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStore) + //XXX We should probably unlink all the objects we traverse. +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_ADDREF_INHERITED(XULDocument, XMLDocument) +NS_IMPL_RELEASE_INHERITED(XULDocument, XMLDocument) + + +// QueryInterface implementation for XULDocument +NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(XULDocument) + NS_INTERFACE_TABLE_INHERITED(XULDocument, nsIXULDocument, + nsIDOMXULDocument, nsIStreamLoaderObserver, + nsICSSLoaderObserver, nsIOffThreadScriptReceiver) +NS_INTERFACE_TABLE_TAIL_INHERITING(XMLDocument) + + +//---------------------------------------------------------------------- +// +// nsIDocument interface +// + +void +XULDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup) +{ + NS_NOTREACHED("Reset"); +} + +void +XULDocument::ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup, + nsIPrincipal* aPrincipal) +{ + NS_NOTREACHED("ResetToURI"); +} + +void +XULDocument::SetContentType(const nsAString& aContentType) +{ + NS_ASSERTION(aContentType.EqualsLiteral("application/vnd.mozilla.xul+xml"), + "xul-documents always has content-type application/vnd.mozilla.xul+xml"); + // Don't do anything, xul always has the mimetype + // application/vnd.mozilla.xul+xml +} + +// This is called when the master document begins loading, whether it's +// being cached or not. +nsresult +XULDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel, + nsILoadGroup* aLoadGroup, + nsISupports* aContainer, + nsIStreamListener **aDocListener, + bool aReset, nsIContentSink* aSink) +{ + if (MOZ_LOG_TEST(gXULLog, LogLevel::Warning)) { + + nsCOMPtr<nsIURI> uri; + nsresult rv = aChannel->GetOriginalURI(getter_AddRefs(uri)); + if (NS_SUCCEEDED(rv)) { + nsAutoCString urlspec; + rv = uri->GetSpec(urlspec); + if (NS_SUCCEEDED(rv)) { + MOZ_LOG(gXULLog, LogLevel::Warning, + ("xul: load document '%s'", urlspec.get())); + } + } + } + // NOTE: If this ever starts calling nsDocument::StartDocumentLoad + // we'll possibly need to reset our content type afterwards. + mStillWalking = true; + mMayStartLayout = false; + mDocumentLoadGroup = do_GetWeakReference(aLoadGroup); + + mChannel = aChannel; + + // Get the URI. Note that this should match nsDocShell::OnLoadingSite + nsresult rv = + NS_GetFinalChannelURI(aChannel, getter_AddRefs(mDocumentURI)); + NS_ENSURE_SUCCESS(rv, rv); + + ResetStylesheetsToURI(mDocumentURI); + + RetrieveRelevantHeaders(aChannel); + + // Look in the chrome cache: we've got this puppy loaded + // already. + nsXULPrototypeDocument* proto = IsChromeURI(mDocumentURI) ? + nsXULPrototypeCache::GetInstance()->GetPrototype(mDocumentURI) : + nullptr; + + // Same comment as nsChromeProtocolHandler::NewChannel and + // XULDocument::ResumeWalk + // - 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 cannot be found in the startup cache, + // parse from disk + // other: the startup cache file could not be found, probably + // due to being accessed before a profile has been selected (e.g. + // loading chrome for the profile manager itself). This must be + // parsed from disk. + + if (proto) { + // If we're racing with another document to load proto, wait till the + // load has finished loading before trying to add cloned style sheets. + // XULDocument::EndLoad will call proto->NotifyLoadDone, which will + // find all racing documents and notify them via OnPrototypeLoadDone, + // which will add style sheet clones to each document. + bool loaded; + rv = proto->AwaitLoadDone(this, &loaded); + if (NS_FAILED(rv)) return rv; + + mMasterPrototype = mCurrentPrototype = proto; + + // Set up the right principal on ourselves. + SetPrincipal(proto->DocumentPrincipal()); + + // We need a listener, even if proto is not yet loaded, in which + // event the listener's OnStopRequest method does nothing, and all + // the interesting work happens below XULDocument::EndLoad, from + // the call there to mCurrentPrototype->NotifyLoadDone(). + *aDocListener = new CachedChromeStreamListener(this, loaded); + } + else { + bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled(); + bool fillXULCache = (useXULCache && IsChromeURI(mDocumentURI)); + + + // It's just a vanilla document load. Create a parser to deal + // with the stream n' stuff. + + nsCOMPtr<nsIParser> parser; + rv = PrepareToLoad(aContainer, aCommand, aChannel, aLoadGroup, + 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<nsIStreamListener> listener = do_QueryInterface(parser, &rv); + NS_ASSERTION(NS_SUCCEEDED(rv), "parser doesn't support nsIStreamListener"); + if (NS_FAILED(rv)) return rv; + + *aDocListener = listener; + + parser->Parse(mDocumentURI); + + // Put the current prototype, created under PrepareToLoad, into the + // XUL prototype cache now. We can't do this under PrepareToLoad or + // overlay loading will break; search for PutPrototype in ResumeWalk + // and see the comment there. + if (fillXULCache) { + nsXULPrototypeCache::GetInstance()->PutPrototype(mCurrentPrototype); + } + } + + NS_IF_ADDREF(*aDocListener); + return NS_OK; +} + +// This gets invoked after a prototype for this document or one of +// its overlays is fully built in the content sink. +void +XULDocument::EndLoad() +{ + // This can happen if an overlay fails to load + if (!mCurrentPrototype) + return; + + nsresult rv; + + // Whack the prototype document into the cache so that the next + // time somebody asks for it, they don't need to load it by hand. + + nsCOMPtr<nsIURI> uri = mCurrentPrototype->GetURI(); + bool isChrome = IsChromeURI(uri); + + // Remember if the XUL cache is on + bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled(); + + // If the current prototype is an overlay document (non-master prototype) + // and we're filling the FastLoad disk cache, tell the cache we're done + // loading it, and write the prototype. The master prototype is put into + // the cache earlier in XULDocument::StartDocumentLoad. + if (useXULCache && mIsWritingFastLoad && isChrome && + mMasterPrototype != mCurrentPrototype) { + nsXULPrototypeCache::GetInstance()->WritePrototype(mCurrentPrototype); + } + + if (IsOverlayAllowed(uri)) { + nsCOMPtr<nsIXULOverlayProvider> reg = + mozilla::services::GetXULOverlayProviderService(); + + if (reg) { + nsCOMPtr<nsISimpleEnumerator> overlays; + rv = reg->GetStyleOverlays(uri, getter_AddRefs(overlays)); + if (NS_FAILED(rv)) return; + + bool moreSheets; + nsCOMPtr<nsISupports> next; + nsCOMPtr<nsIURI> sheetURI; + + while (NS_SUCCEEDED(rv = overlays->HasMoreElements(&moreSheets)) && + moreSheets) { + overlays->GetNext(getter_AddRefs(next)); + + sheetURI = do_QueryInterface(next); + if (!sheetURI) { + NS_ERROR("Chrome registry handed me a non-nsIURI object!"); + continue; + } + + if (IsChromeURI(sheetURI)) { + mCurrentPrototype->AddStyleSheetReference(sheetURI); + } + } + } + + if (isChrome && useXULCache) { + // If it's a chrome prototype document, then notify any + // documents that raced to load the prototype, and awaited + // its load completion via proto->AwaitLoadDone(). + rv = mCurrentPrototype->NotifyLoadDone(); + if (NS_FAILED(rv)) return; + } + } + + OnPrototypeLoadDone(true); + if (MOZ_LOG_TEST(gXULLog, LogLevel::Warning)) { + nsAutoCString urlspec; + rv = uri->GetSpec(urlspec); + if (NS_SUCCEEDED(rv)) { + MOZ_LOG(gXULLog, LogLevel::Warning, + ("xul: Finished loading document '%s'", urlspec.get())); + } + } +} + +NS_IMETHODIMP +XULDocument::OnPrototypeLoadDone(bool aResumeWalk) +{ + nsresult rv; + + // Add the style overlays from chrome registry, if any. + rv = AddPrototypeSheets(); + if (NS_FAILED(rv)) return rv; + + rv = PrepareToWalk(); + NS_ASSERTION(NS_SUCCEEDED(rv), "unable to prepare for walk"); + if (NS_FAILED(rv)) return rv; + + if (aResumeWalk) { + rv = ResumeWalk(); + } + return rv; +} + +// called when an error occurs parsing a document +bool +XULDocument::OnDocumentParserError() +{ + // don't report errors that are from overlays + if (mCurrentPrototype && mMasterPrototype != mCurrentPrototype) { + nsCOMPtr<nsIURI> uri = mCurrentPrototype->GetURI(); + if (IsChromeURI(uri)) { + nsCOMPtr<nsIObserverService> os = + mozilla::services::GetObserverService(); + if (os) + os->NotifyObservers(uri, "xul-overlay-parsererror", + EmptyString().get()); + } + + return false; + } + + return true; +} + +static void +ClearBroadcasterMapEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry) +{ + BroadcasterMapEntry* entry = + static_cast<BroadcasterMapEntry*>(aEntry); + for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) { + delete entry->mListeners[i]; + } + entry->mListeners.Clear(); + + // N.B. that we need to manually run the dtor because we + // constructed the nsTArray object in-place. + entry->mListeners.~nsTArray<BroadcastListener*>(); +} + +static bool +CanBroadcast(int32_t aNameSpaceID, nsIAtom* aAttribute) +{ + // Don't push changes to the |id|, |ref|, |persist|, |command| or + // |observes| attribute. + if (aNameSpaceID == kNameSpaceID_None) { + if ((aAttribute == nsGkAtoms::id) || + (aAttribute == nsGkAtoms::ref) || + (aAttribute == nsGkAtoms::persist) || + (aAttribute == nsGkAtoms::command) || + (aAttribute == nsGkAtoms::observes)) { + return false; + } + } + return true; +} + +struct nsAttrNameInfo +{ + nsAttrNameInfo(int32_t aNamespaceID, nsIAtom* aName, nsIAtom* aPrefix) : + mNamespaceID(aNamespaceID), mName(aName), mPrefix(aPrefix) {} + nsAttrNameInfo(const nsAttrNameInfo& aOther) : + mNamespaceID(aOther.mNamespaceID), mName(aOther.mName), + mPrefix(aOther.mPrefix) {} + int32_t mNamespaceID; + nsCOMPtr<nsIAtom> mName; + nsCOMPtr<nsIAtom> mPrefix; +}; + +void +XULDocument::SynchronizeBroadcastListener(Element *aBroadcaster, + Element *aListener, + const nsAString &aAttr) +{ + if (!nsContentUtils::IsSafeToRunScript()) { + nsDelayedBroadcastUpdate delayedUpdate(aBroadcaster, aListener, + aAttr); + mDelayedBroadcasters.AppendElement(delayedUpdate); + MaybeBroadcast(); + return; + } + bool notify = mDocumentLoaded || mHandlingDelayedBroadcasters; + + if (aAttr.EqualsLiteral("*")) { + uint32_t count = aBroadcaster->GetAttrCount(); + nsTArray<nsAttrNameInfo> attributes(count); + for (uint32_t i = 0; i < count; ++i) { + const nsAttrName* attrName = aBroadcaster->GetAttrNameAt(i); + int32_t nameSpaceID = attrName->NamespaceID(); + nsIAtom* name = attrName->LocalName(); + + // _Don't_ push the |id|, |ref|, or |persist| attribute's value! + if (! CanBroadcast(nameSpaceID, name)) + continue; + + attributes.AppendElement(nsAttrNameInfo(nameSpaceID, name, + attrName->GetPrefix())); + } + + count = attributes.Length(); + while (count-- > 0) { + int32_t nameSpaceID = attributes[count].mNamespaceID; + nsIAtom* name = attributes[count].mName; + nsAutoString value; + if (aBroadcaster->GetAttr(nameSpaceID, name, value)) { + aListener->SetAttr(nameSpaceID, name, attributes[count].mPrefix, + value, notify); + } + +#if 0 + // XXX we don't fire the |onbroadcast| handler during + // initial hookup: doing so would potentially run the + // |onbroadcast| handler before the |onload| handler, + // which could define JS properties that mask XBL + // properties, etc. + ExecuteOnBroadcastHandlerFor(aBroadcaster, aListener, name); +#endif + } + } + else { + // Find out if the attribute is even present at all. + nsCOMPtr<nsIAtom> name = NS_Atomize(aAttr); + + nsAutoString value; + if (aBroadcaster->GetAttr(kNameSpaceID_None, name, value)) { + aListener->SetAttr(kNameSpaceID_None, name, value, notify); + } else { + aListener->UnsetAttr(kNameSpaceID_None, name, notify); + } + +#if 0 + // XXX we don't fire the |onbroadcast| handler during initial + // hookup: doing so would potentially run the |onbroadcast| + // handler before the |onload| handler, which could define JS + // properties that mask XBL properties, etc. + ExecuteOnBroadcastHandlerFor(aBroadcaster, aListener, name); +#endif + } +} + +NS_IMETHODIMP +XULDocument::AddBroadcastListenerFor(nsIDOMElement* aBroadcaster, + nsIDOMElement* aListener, + const nsAString& aAttr) +{ + ErrorResult rv; + nsCOMPtr<Element> broadcaster = do_QueryInterface(aBroadcaster); + nsCOMPtr<Element> listener = do_QueryInterface(aListener); + NS_ENSURE_ARG(broadcaster && listener); + AddBroadcastListenerFor(*broadcaster, *listener, aAttr, rv); + return rv.StealNSResult(); +} + +void +XULDocument::AddBroadcastListenerFor(Element& aBroadcaster, Element& aListener, + const nsAString& aAttr, ErrorResult& aRv) +{ + nsresult rv = + nsContentUtils::CheckSameOrigin(this, &aBroadcaster); + + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return; + } + + rv = nsContentUtils::CheckSameOrigin(this, &aListener); + + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return; + } + + static const PLDHashTableOps gOps = { + PLDHashTable::HashVoidPtrKeyStub, + PLDHashTable::MatchEntryStub, + PLDHashTable::MoveEntryStub, + ClearBroadcasterMapEntry, + nullptr + }; + + if (! mBroadcasterMap) { + mBroadcasterMap = new PLDHashTable(&gOps, sizeof(BroadcasterMapEntry)); + } + + auto entry = static_cast<BroadcasterMapEntry*> + (mBroadcasterMap->Search(&aBroadcaster)); + if (!entry) { + entry = static_cast<BroadcasterMapEntry*> + (mBroadcasterMap->Add(&aBroadcaster, fallible)); + + if (! entry) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + + entry->mBroadcaster = &aBroadcaster; + + // N.B. placement new to construct the nsTArray object in-place + new (&entry->mListeners) nsTArray<BroadcastListener*>(); + } + + // Only add the listener if it's not there already! + nsCOMPtr<nsIAtom> attr = NS_Atomize(aAttr); + + for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) { + BroadcastListener* bl = entry->mListeners[i]; + nsCOMPtr<Element> blListener = do_QueryReferent(bl->mListener); + + if (blListener == &aListener && bl->mAttribute == attr) + return; + } + + BroadcastListener* bl = new BroadcastListener; + bl->mListener = do_GetWeakReference(&aListener); + bl->mAttribute = attr; + + entry->mListeners.AppendElement(bl); + + SynchronizeBroadcastListener(&aBroadcaster, &aListener, aAttr); +} + +NS_IMETHODIMP +XULDocument::RemoveBroadcastListenerFor(nsIDOMElement* aBroadcaster, + nsIDOMElement* aListener, + const nsAString& aAttr) +{ + nsCOMPtr<Element> broadcaster = do_QueryInterface(aBroadcaster); + nsCOMPtr<Element> listener = do_QueryInterface(aListener); + NS_ENSURE_ARG(broadcaster && listener); + RemoveBroadcastListenerFor(*broadcaster, *listener, aAttr); + return NS_OK; +} + +void +XULDocument::RemoveBroadcastListenerFor(Element& aBroadcaster, + Element& aListener, + const nsAString& aAttr) +{ + // If we haven't added any broadcast listeners, then there sure + // aren't any to remove. + if (! mBroadcasterMap) + return; + + auto entry = static_cast<BroadcasterMapEntry*> + (mBroadcasterMap->Search(&aBroadcaster)); + if (entry) { + nsCOMPtr<nsIAtom> attr = NS_Atomize(aAttr); + for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) { + BroadcastListener* bl = entry->mListeners[i]; + nsCOMPtr<Element> blListener = do_QueryReferent(bl->mListener); + + if (blListener == &aListener && bl->mAttribute == attr) { + entry->mListeners.RemoveElementAt(i); + delete bl; + + if (entry->mListeners.IsEmpty()) + mBroadcasterMap->RemoveEntry(entry); + + break; + } + } + } +} + +nsresult +XULDocument::ExecuteOnBroadcastHandlerFor(Element* aBroadcaster, + Element* aListener, + nsIAtom* aAttr) +{ + // Now we execute the onchange handler in the context of the + // observer. We need to find the observer in order to + // execute the handler. + + for (nsIContent* child = aListener->GetFirstChild(); + child; + child = child->GetNextSibling()) { + + // Look for an <observes> element beneath the listener. This + // ought to have an |element| attribute that refers to + // aBroadcaster, and an |attribute| element that tells us what + // attriubtes we're listening for. + if (!child->NodeInfo()->Equals(nsGkAtoms::observes, kNameSpaceID_XUL)) + continue; + + // Is this the element that was listening to us? + nsAutoString listeningToID; + child->GetAttr(kNameSpaceID_None, nsGkAtoms::element, listeningToID); + + nsAutoString broadcasterID; + aBroadcaster->GetAttr(kNameSpaceID_None, nsGkAtoms::id, broadcasterID); + + if (listeningToID != broadcasterID) + continue; + + // We are observing the broadcaster, but is this the right + // attribute? + nsAutoString listeningToAttribute; + child->GetAttr(kNameSpaceID_None, nsGkAtoms::attribute, + listeningToAttribute); + + if (!aAttr->Equals(listeningToAttribute) && + !listeningToAttribute.EqualsLiteral("*")) { + continue; + } + + // This is the right <observes> element. Execute the + // |onbroadcast| event handler + WidgetEvent event(true, eXULBroadcast); + + nsCOMPtr<nsIPresShell> shell = GetShell(); + if (shell) { + RefPtr<nsPresContext> aPresContext = shell->GetPresContext(); + + // Handle the DOM event + nsEventStatus status = nsEventStatus_eIgnore; + EventDispatcher::Dispatch(child, aPresContext, &event, nullptr, + &status); + } + } + + return NS_OK; +} + +void +XULDocument::AttributeWillChange(nsIDocument* aDocument, + Element* aElement, int32_t aNameSpaceID, + nsIAtom* aAttribute, int32_t aModType, + const nsAttrValue* aNewValue) +{ + MOZ_ASSERT(aElement, "Null content!"); + NS_PRECONDITION(aAttribute, "Must have an attribute that's changing!"); + + // XXXbz check aNameSpaceID, dammit! + // See if we need to update our ref map. + if (aAttribute == nsGkAtoms::ref) { + // Might not need this, but be safe for now. + nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); + RemoveElementFromRefMap(aElement); + } +} + +static bool +ShouldPersistAttribute(Element* aElement, nsIAtom* aAttribute) +{ + if (aElement->IsXULElement(nsGkAtoms::window)) { + // This is not an element of the top document, its owner is + // not an nsXULWindow. Persist it. + if (aElement->OwnerDoc()->GetParentDocument()) { + return true; + } + // The following attributes of xul:window should be handled in + // nsXULWindow::SavePersistentAttributes instead of here. + if (aAttribute == nsGkAtoms::screenX || + aAttribute == nsGkAtoms::screenY || + aAttribute == nsGkAtoms::width || + aAttribute == nsGkAtoms::height || + aAttribute == nsGkAtoms::sizemode) { + return false; + } + } + return true; +} + +void +XULDocument::AttributeChanged(nsIDocument* aDocument, + Element* aElement, int32_t aNameSpaceID, + nsIAtom* aAttribute, int32_t aModType, + const nsAttrValue* aOldValue) +{ + NS_ASSERTION(aDocument == this, "unexpected doc"); + + // Might not need this, but be safe for now. + nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); + + // XXXbz check aNameSpaceID, dammit! + // See if we need to update our ref map. + if (aAttribute == nsGkAtoms::ref) { + AddElementToRefMap(aElement); + } + + // Synchronize broadcast listeners + if (mBroadcasterMap && + CanBroadcast(aNameSpaceID, aAttribute)) { + auto entry = static_cast<BroadcasterMapEntry*> + (mBroadcasterMap->Search(aElement)); + + if (entry) { + // We've got listeners: push the value. + nsAutoString value; + bool attrSet = aElement->GetAttr(kNameSpaceID_None, aAttribute, value); + + for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) { + BroadcastListener* bl = entry->mListeners[i]; + if ((bl->mAttribute == aAttribute) || + (bl->mAttribute == nsGkAtoms::_asterisk)) { + nsCOMPtr<Element> listenerEl + = do_QueryReferent(bl->mListener); + if (listenerEl) { + nsAutoString currentValue; + bool hasAttr = listenerEl->GetAttr(kNameSpaceID_None, + aAttribute, + currentValue); + // We need to update listener only if we're + // (1) removing an existing attribute, + // (2) adding a new attribute or + // (3) changing the value of an attribute. + bool needsAttrChange = + attrSet != hasAttr || !value.Equals(currentValue); + nsDelayedBroadcastUpdate delayedUpdate(aElement, + listenerEl, + aAttribute, + value, + attrSet, + needsAttrChange); + + size_t index = + mDelayedAttrChangeBroadcasts.IndexOf(delayedUpdate, + 0, nsDelayedBroadcastUpdate::Comparator()); + if (index != mDelayedAttrChangeBroadcasts.NoIndex) { + if (mHandlingDelayedAttrChange) { + NS_WARNING("Broadcasting loop!"); + continue; + } + mDelayedAttrChangeBroadcasts.RemoveElementAt(index); + } + + mDelayedAttrChangeBroadcasts.AppendElement(delayedUpdate); + } + } + } + } + } + + // checks for modifications in broadcasters + bool listener, resolved; + CheckBroadcasterHookup(aElement, &listener, &resolved); + + // See if there is anything we need to persist in the localstore. + // + // XXX Namespace handling broken :-( + nsAutoString persist; + aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::persist, persist); + // Persistence of attributes of xul:window is handled in nsXULWindow. + if (ShouldPersistAttribute(aElement, aAttribute) && !persist.IsEmpty() && + // XXXldb This should check that it's a token, not just a substring. + persist.Find(nsDependentAtomString(aAttribute)) >= 0) { + nsContentUtils::AddScriptRunner(NewRunnableMethod + <nsIContent*, int32_t, nsIAtom*> + (this, &XULDocument::DoPersist, aElement, kNameSpaceID_None, + aAttribute)); + } +} + +void +XULDocument::ContentAppended(nsIDocument* aDocument, + nsIContent* aContainer, + nsIContent* aFirstNewContent, + int32_t aNewIndexInContainer) +{ + NS_ASSERTION(aDocument == this, "unexpected doc"); + + // Might not need this, but be safe for now. + nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); + + // Update our element map + nsresult rv = NS_OK; + for (nsIContent* cur = aFirstNewContent; cur && NS_SUCCEEDED(rv); + cur = cur->GetNextSibling()) { + rv = AddSubtreeToDocument(cur); + } +} + +void +XULDocument::ContentInserted(nsIDocument* aDocument, + nsIContent* aContainer, + nsIContent* aChild, + int32_t aIndexInContainer) +{ + NS_ASSERTION(aDocument == this, "unexpected doc"); + + // Might not need this, but be safe for now. + nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); + + AddSubtreeToDocument(aChild); +} + +void +XULDocument::ContentRemoved(nsIDocument* aDocument, + nsIContent* aContainer, + nsIContent* aChild, + int32_t aIndexInContainer, + nsIContent* aPreviousSibling) +{ + NS_ASSERTION(aDocument == this, "unexpected doc"); + + // Might not need this, but be safe for now. + nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); + + RemoveSubtreeFromDocument(aChild); +} + +//---------------------------------------------------------------------- +// +// nsIXULDocument interface +// + +void +XULDocument::GetElementsForID(const nsAString& aID, + nsCOMArray<nsIContent>& aElements) +{ + aElements.Clear(); + + nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aID); + if (entry) { + entry->AppendAllIdContent(&aElements); + } + nsRefMapEntry *refEntry = mRefMap.GetEntry(aID); + if (refEntry) { + refEntry->AppendAll(&aElements); + } +} + +nsresult +XULDocument::AddForwardReference(nsForwardReference* aRef) +{ + if (mResolutionPhase < aRef->GetPhase()) { + if (!mForwardReferences.AppendElement(aRef)) { + delete aRef; + return NS_ERROR_OUT_OF_MEMORY; + } + } + else { + NS_ERROR("forward references have already been resolved"); + delete aRef; + } + + return NS_OK; +} + +nsresult +XULDocument::ResolveForwardReferences() +{ + if (mResolutionPhase == nsForwardReference::eDone) + return NS_OK; + + NS_ASSERTION(mResolutionPhase == nsForwardReference::eStart, + "nested ResolveForwardReferences()"); + + // 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) { + uint32_t previous = 0; + while (mForwardReferences.Length() && + mForwardReferences.Length() != previous) { + previous = mForwardReferences.Length(); + + for (uint32_t i = 0; i < mForwardReferences.Length(); ++i) { + nsForwardReference* fwdref = mForwardReferences[i]; + + if (fwdref->GetPhase() == *pass) { + nsForwardReference::Result result = fwdref->Resolve(); + + switch (result) { + case nsForwardReference::eResolve_Succeeded: + case nsForwardReference::eResolve_Error: + mForwardReferences.RemoveElementAt(i); + + // fixup because we removed from list + --i; + break; + + case nsForwardReference::eResolve_Later: + // do nothing. we'll try again later + ; + } + + if (mResolutionPhase == nsForwardReference::eStart) { + // Resolve() loaded a dynamic overlay, + // (see XULDocument::LoadOverlayInternal()). + // Return for now, we will be called again. + return NS_OK; + } + } + } + } + + ++pass; + } + + mForwardReferences.Clear(); + return NS_OK; +} + +//---------------------------------------------------------------------- +// +// nsIDOMDocument interface +// + +NS_IMETHODIMP +XULDocument::GetElementsByAttribute(const nsAString& aAttribute, + const nsAString& aValue, + nsIDOMNodeList** aReturn) +{ + *aReturn = GetElementsByAttribute(aAttribute, aValue).take(); + return NS_OK; +} + +already_AddRefed<nsINodeList> +XULDocument::GetElementsByAttribute(const nsAString& aAttribute, + const nsAString& aValue) +{ + nsCOMPtr<nsIAtom> attrAtom(NS_Atomize(aAttribute)); + void* attrValue = new nsString(aValue); + RefPtr<nsContentList> list = new nsContentList(this, + MatchAttribute, + nsContentUtils::DestroyMatchString, + attrValue, + true, + attrAtom, + kNameSpaceID_Unknown); + + return list.forget(); +} + +NS_IMETHODIMP +XULDocument::GetElementsByAttributeNS(const nsAString& aNamespaceURI, + const nsAString& aAttribute, + const nsAString& aValue, + nsIDOMNodeList** aReturn) +{ + ErrorResult rv; + *aReturn = GetElementsByAttributeNS(aNamespaceURI, aAttribute, + aValue, rv).take(); + return rv.StealNSResult(); +} + +already_AddRefed<nsINodeList> +XULDocument::GetElementsByAttributeNS(const nsAString& aNamespaceURI, + const nsAString& aAttribute, + const nsAString& aValue, + ErrorResult& aRv) +{ + nsCOMPtr<nsIAtom> attrAtom(NS_Atomize(aAttribute)); + void* attrValue = new nsString(aValue); + + int32_t nameSpaceId = kNameSpaceID_Wildcard; + if (!aNamespaceURI.EqualsLiteral("*")) { + nsresult rv = + nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI, + nameSpaceId); + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return nullptr; + } + } + + RefPtr<nsContentList> list = new nsContentList(this, + MatchAttribute, + nsContentUtils::DestroyMatchString, + attrValue, + true, + attrAtom, + nameSpaceId); + return list.forget(); +} + +NS_IMETHODIMP +XULDocument::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; + + Element* element = nsDocument::GetElementById(aID); + if (!element) + return NS_OK; + + nsCOMPtr<nsIAtom> tag; + int32_t nameSpaceID; + + RefPtr<mozilla::dom::NodeInfo> ni = element->GetExistingAttrNameFromQName(aAttr); + nsresult rv; + if (ni) { + tag = ni->NameAtom(); + nameSpaceID = ni->NamespaceID(); + } + else { + // Make sure that this QName is going to be valid. + const char16_t *colon; + rv = nsContentUtils::CheckQName(PromiseFlatString(aAttr), 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 = NS_Atomize(aAttr); + NS_ENSURE_TRUE(tag, NS_ERROR_OUT_OF_MEMORY); + + nameSpaceID = kNameSpaceID_None; + } + + return Persist(element, nameSpaceID, tag); +} + +nsresult +XULDocument::Persist(nsIContent* aElement, int32_t aNameSpaceID, + nsIAtom* aAttribute) +{ + // For non-chrome documents, persistance is simply broken + if (!nsContentUtils::IsSystemPrincipal(NodePrincipal())) + return NS_ERROR_NOT_AVAILABLE; + + if (!mLocalStore) { + mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1"); + if (NS_WARN_IF(!mLocalStore)) { + return NS_ERROR_NOT_INITIALIZED; + } + } + + nsAutoString id; + + aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id); + nsAtomString attrstr(aAttribute); + + nsAutoString valuestr; + aElement->GetAttr(kNameSpaceID_None, aAttribute, valuestr); + + nsAutoCString utf8uri; + nsresult rv = mDocumentURI->GetSpec(utf8uri); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + NS_ConvertUTF8toUTF16 uri(utf8uri); + + bool hasAttr; + rv = mLocalStore->HasValue(uri, id, attrstr, &hasAttr); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (hasAttr && valuestr.IsEmpty()) { + return mLocalStore->RemoveValue(uri, id, attrstr); + } else { + return mLocalStore->SetValue(uri, id, attrstr, valuestr); + } +} + + +nsresult +XULDocument::GetViewportSize(int32_t* aWidth, + int32_t* aHeight) +{ + *aWidth = *aHeight = 0; + + FlushPendingNotifications(Flush_Layout); + + nsIPresShell *shell = GetShell(); + NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE); + + nsIFrame* frame = shell->GetRootFrame(); + NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE); + + nsSize size = frame->GetSize(); + + *aWidth = nsPresContext::AppUnitsToIntCSSPixels(size.width); + *aHeight = nsPresContext::AppUnitsToIntCSSPixels(size.height); + + return NS_OK; +} + +NS_IMETHODIMP +XULDocument::GetWidth(int32_t* aWidth) +{ + NS_ENSURE_ARG_POINTER(aWidth); + + int32_t height; + return GetViewportSize(aWidth, &height); +} + +int32_t +XULDocument::GetWidth(ErrorResult& aRv) +{ + int32_t width; + aRv = GetWidth(&width); + return width; +} + +NS_IMETHODIMP +XULDocument::GetHeight(int32_t* aHeight) +{ + NS_ENSURE_ARG_POINTER(aHeight); + + int32_t width; + return GetViewportSize(&width, aHeight); +} + +int32_t +XULDocument::GetHeight(ErrorResult& aRv) +{ + int32_t height; + aRv = GetHeight(&height); + return height; +} + +JSObject* +GetScopeObjectOfNode(nsIDOMNode* node) +{ + MOZ_ASSERT(node, "Must not be called with null."); + + // Window root occasionally keeps alive a node of a document whose + // window is already dead. If in this brief period someone calls + // GetPopupNode and we return that node, nsNodeSH::PreCreate will throw, + // because it will not know which scope this node belongs to. Returning + // an orphan node like that to JS would be a bug anyway, so to avoid + // this, let's do the same check as nsNodeSH::PreCreate does to + // determine the scope and if it fails let's just return null in + // XULDocument::GetPopupNode. + nsCOMPtr<nsINode> inode = do_QueryInterface(node); + MOZ_ASSERT(inode, "How can this happen?"); + + nsIDocument* doc = inode->OwnerDoc(); + MOZ_ASSERT(inode, "This should never happen."); + + nsIGlobalObject* global = doc->GetScopeObject(); + return global ? global->GetGlobalJSObject() : nullptr; +} + +//---------------------------------------------------------------------- +// +// nsIDOMXULDocument interface +// + +NS_IMETHODIMP +XULDocument::GetPopupNode(nsIDOMNode** aNode) +{ + *aNode = nullptr; + + nsCOMPtr<nsIDOMNode> node; + nsCOMPtr<nsPIWindowRoot> rootWin = GetWindowRoot(); + if (rootWin) + node = rootWin->GetPopupNode(); // addref happens here + + if (!node) { + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm) { + node = pm->GetLastTriggerPopupNode(this); + } + } + + if (node && nsContentUtils::CanCallerAccess(node) + && GetScopeObjectOfNode(node)) { + node.forget(aNode); + } + + return NS_OK; +} + +already_AddRefed<nsINode> +XULDocument::GetPopupNode() +{ + nsCOMPtr<nsIDOMNode> node; + DebugOnly<nsresult> rv = GetPopupNode(getter_AddRefs(node)); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + nsCOMPtr<nsINode> retval(do_QueryInterface(node)); + return retval.forget(); +} + +NS_IMETHODIMP +XULDocument::SetPopupNode(nsIDOMNode* aNode) +{ + if (aNode) { + // only allow real node objects + nsCOMPtr<nsINode> node = do_QueryInterface(aNode); + NS_ENSURE_ARG(node); + } + + nsCOMPtr<nsPIWindowRoot> rootWin = GetWindowRoot(); + if (rootWin) + rootWin->SetPopupNode(aNode); // addref happens here + + return NS_OK; +} + +void +XULDocument::SetPopupNode(nsINode* aNode) +{ + nsCOMPtr<nsIDOMNode> node(do_QueryInterface(aNode)); + DebugOnly<nsresult> rv = SetPopupNode(node); + MOZ_ASSERT(NS_SUCCEEDED(rv)); +} + +// Returns the rangeOffset element from the XUL Popup Manager. This is for +// chrome callers only. +NS_IMETHODIMP +XULDocument::GetPopupRangeParent(nsIDOMNode** aRangeParent) +{ + NS_ENSURE_ARG_POINTER(aRangeParent); + *aRangeParent = nullptr; + + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (!pm) + return NS_ERROR_FAILURE; + + int32_t offset; + pm->GetMouseLocation(aRangeParent, &offset); + + if (*aRangeParent && !nsContentUtils::CanCallerAccess(*aRangeParent)) { + NS_RELEASE(*aRangeParent); + return NS_ERROR_DOM_SECURITY_ERR; + } + + return NS_OK; +} + +already_AddRefed<nsINode> +XULDocument::GetPopupRangeParent(ErrorResult& aRv) +{ + nsCOMPtr<nsIDOMNode> node; + aRv = GetPopupRangeParent(getter_AddRefs(node)); + nsCOMPtr<nsINode> retval(do_QueryInterface(node)); + return retval.forget(); +} + + +// Returns the rangeOffset element from the XUL Popup Manager. We check the +// rangeParent to determine if the caller has rights to access to the data. +NS_IMETHODIMP +XULDocument::GetPopupRangeOffset(int32_t* aRangeOffset) +{ + ErrorResult rv; + *aRangeOffset = GetPopupRangeOffset(rv); + return rv.StealNSResult(); +} + +int32_t +XULDocument::GetPopupRangeOffset(ErrorResult& aRv) +{ + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (!pm) { + aRv.Throw(NS_ERROR_FAILURE); + return 0; + } + + int32_t offset; + nsCOMPtr<nsIDOMNode> parent; + pm->GetMouseLocation(getter_AddRefs(parent), &offset); + + if (parent && !nsContentUtils::CanCallerAccess(parent)) { + aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); + return 0; + } + return offset; +} + +NS_IMETHODIMP +XULDocument::GetTooltipNode(nsIDOMNode** aNode) +{ + *aNode = nullptr; + + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm) { + nsCOMPtr<nsIDOMNode> node = pm->GetLastTriggerTooltipNode(this); + if (node && nsContentUtils::CanCallerAccess(node)) + node.forget(aNode); + } + + return NS_OK; +} + +already_AddRefed<nsINode> +XULDocument::GetTooltipNode() +{ + nsCOMPtr<nsIDOMNode> node; + DebugOnly<nsresult> rv = GetTooltipNode(getter_AddRefs(node)); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + nsCOMPtr<nsINode> retval(do_QueryInterface(node)); + return retval.forget(); +} + +NS_IMETHODIMP +XULDocument::SetTooltipNode(nsIDOMNode* aNode) +{ + // do nothing + return NS_OK; +} + + +NS_IMETHODIMP +XULDocument::GetCommandDispatcher(nsIDOMXULCommandDispatcher** aTracker) +{ + *aTracker = mCommandDispatcher; + NS_IF_ADDREF(*aTracker); + return NS_OK; +} + +Element* +XULDocument::GetElementById(const nsAString& aId) +{ + if (!CheckGetElementByIdArg(aId)) + return nullptr; + + nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aId); + if (entry) { + Element* element = entry->GetIdElement(); + if (element) + return element; + } + + nsRefMapEntry* refEntry = mRefMap.GetEntry(aId); + if (refEntry) { + NS_ASSERTION(refEntry->GetFirstElement(), + "nsRefMapEntries should have nonempty content lists"); + return refEntry->GetFirstElement(); + } + return nullptr; +} + +nsresult +XULDocument::AddElementToDocumentPre(Element* 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. Also add it to + // the id map, since it seems this can be called when creating + // elements from prototypes. + nsIAtom* id = aElement->GetID(); + if (id) { + // FIXME: Shouldn't BindToTree take care of this? + nsAutoScriptBlocker scriptBlocker; + AddToIdTable(aElement, id); + } + rv = AddElementToRefMap(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, nsGkAtoms::commandupdater, + nsGkAtoms::_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. + bool 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); + rv = AddForwardReference(hookup); + if (NS_FAILED(rv)) return rv; + } + + return NS_OK; +} + +nsresult +XULDocument::AddElementToDocumentPost(Element* aElement) +{ + // We need to pay special attention to the keyset tag to set up a listener + if (aElement->NodeInfo()->Equals(nsGkAtoms::keyset, kNameSpaceID_XUL)) { + // Create our XUL key listener and hook it up. + nsXBLService::AttachGlobalKeyHandler(aElement); + } + + // See if we need to attach a XUL template to this node + bool 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); + rv = AddForwardReference(hookup); + if (NS_FAILED(rv)) + return rv; + } + } + + return NS_OK; +} + +NS_IMETHODIMP +XULDocument::AddSubtreeToDocument(nsIContent* aContent) +{ + NS_ASSERTION(aContent->GetUncomposedDoc() == this, "Element not in doc!"); + // From here on we only care about elements. + if (!aContent->IsElement()) { + return NS_OK; + } + + Element* aElement = aContent->AsElement(); + + // Do pre-order addition magic + nsresult rv = AddElementToDocumentPre(aElement); + if (NS_FAILED(rv)) return rv; + + // Recurse to children + for (nsIContent* child = aElement->GetLastChild(); + child; + child = child->GetPreviousSibling()) { + + rv = AddSubtreeToDocument(child); + if (NS_FAILED(rv)) + return rv; + } + + // Do post-order addition magic + return AddElementToDocumentPost(aElement); +} + +NS_IMETHODIMP +XULDocument::RemoveSubtreeFromDocument(nsIContent* aContent) +{ + // From here on we only care about elements. + if (!aContent->IsElement()) { + return NS_OK; + } + + Element* aElement = aContent->AsElement(); + + // Do a bunch of cleanup to remove an element from the XUL + // document. + nsresult rv; + + if (aElement->NodeInfo()->Equals(nsGkAtoms::keyset, kNameSpaceID_XUL)) { + nsXBLService::DetachGlobalKeyHandler(aElement); + } + + // 1. Remove any children from the document. + for (nsIContent* child = aElement->GetLastChild(); + child; + child = child->GetPreviousSibling()) { + + rv = RemoveSubtreeFromDocument(child); + if (NS_FAILED(rv)) + return rv; + } + + // 2. Remove the element from the resource-to-element map. + // Also remove it from the id map, since we added it in + // AddElementToDocumentPre(). + RemoveElementFromRefMap(aElement); + nsIAtom* id = aElement->GetID(); + if (id) { + // FIXME: Shouldn't UnbindFromTree take care of this? + nsAutoScriptBlocker scriptBlocker; + RemoveFromIdTable(aElement, id); + } + + // 3. If the element is a 'command updater', then remove the + // element from the document's command dispatcher. + if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::commandupdater, + nsGkAtoms::_true, eCaseMatters)) { + nsCOMPtr<nsIDOMElement> domelement = do_QueryInterface(aElement); + NS_ASSERTION(domelement != nullptr, "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<Element> 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 +XULDocument::SetTemplateBuilderFor(nsIContent* aContent, + nsIXULTemplateBuilder* aBuilder) +{ + if (! mTemplateBuilderTable) { + if (!aBuilder) { + return NS_OK; + } + mTemplateBuilderTable = new BuilderTable; + } + + if (aBuilder) { + mTemplateBuilderTable->Put(aContent, aBuilder); + } + else { + mTemplateBuilderTable->Remove(aContent); + } + + return NS_OK; +} + +NS_IMETHODIMP +XULDocument::GetTemplateBuilderFor(nsIContent* aContent, + nsIXULTemplateBuilder** aResult) +{ + if (mTemplateBuilderTable) { + mTemplateBuilderTable->Get(aContent, aResult); + } + else + *aResult = nullptr; + + return NS_OK; +} + +static void +GetRefMapAttribute(Element* aElement, nsAutoString* aValue) +{ + aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::ref, *aValue); +} + +nsresult +XULDocument::AddElementToRefMap(Element* aElement) +{ + // Look at the element's 'ref' attribute, and if set, + // add an entry in the resource-to-element map to the element. + nsAutoString value; + GetRefMapAttribute(aElement, &value); + if (!value.IsEmpty()) { + nsRefMapEntry *entry = mRefMap.PutEntry(value); + if (!entry) + return NS_ERROR_OUT_OF_MEMORY; + if (!entry->AddElement(aElement)) + return NS_ERROR_OUT_OF_MEMORY; + } + + return NS_OK; +} + +void +XULDocument::RemoveElementFromRefMap(Element* aElement) +{ + // Remove the element from the resource-to-element map. + nsAutoString value; + GetRefMapAttribute(aElement, &value); + if (!value.IsEmpty()) { + nsRefMapEntry *entry = mRefMap.GetEntry(value); + if (!entry) + return; + if (entry->RemoveElement(aElement)) { + mRefMap.RemoveEntry(entry); + } + } +} + +//---------------------------------------------------------------------- +// +// nsIDOMNode interface +// + +nsresult +XULDocument::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const +{ + // We don't allow cloning of a XUL document + *aResult = nullptr; + return NS_ERROR_DOM_NOT_SUPPORTED_ERR; +} + + +//---------------------------------------------------------------------- +// +// Implementation methods +// + +nsresult +XULDocument::Init() +{ + nsresult rv = XMLDocument::Init(); + NS_ENSURE_SUCCESS(rv, rv); + + // Create our command dispatcher and hook it up. + mCommandDispatcher = new nsXULCommandDispatcher(this); + + if (gRefCnt++ == 0) { + // ensure that the XUL prototype cache is instantiated successfully, + // so that we can use nsXULPrototypeCache::GetInstance() without + // null-checks in the rest of the class. + nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance(); + if (!cache) { + NS_ERROR("Could not instantiate nsXULPrototypeCache"); + return NS_ERROR_FAILURE; + } + } + + Preferences::RegisterCallback(XULDocument::DirectionChanged, + "intl.uidirection.", this); + + return NS_OK; +} + + +nsresult +XULDocument::StartLayout(void) +{ + mMayStartLayout = true; + nsCOMPtr<nsIPresShell> shell = GetShell(); + if (shell) { + // Resize-reflow this time + nsPresContext *cx = shell->GetPresContext(); + NS_ASSERTION(cx != nullptr, "no pres context"); + if (! cx) + return NS_ERROR_UNEXPECTED; + + nsCOMPtr<nsIDocShell> docShell = cx->GetDocShell(); + NS_ASSERTION(docShell != nullptr, "container is not a docshell"); + if (! docShell) + return NS_ERROR_UNEXPECTED; + + nsresult rv = NS_OK; + nsRect r = cx->GetVisibleArea(); + rv = shell->Initialize(r.width, r.height); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +/* static */ +bool +XULDocument::MatchAttribute(nsIContent* aContent, + int32_t aNamespaceID, + nsIAtom* aAttrName, + void* aData) +{ + NS_PRECONDITION(aContent, "Must have content node to work with!"); + nsString* attrValue = static_cast<nsString*>(aData); + if (aNamespaceID != kNameSpaceID_Unknown && + aNamespaceID != kNameSpaceID_Wildcard) { + return attrValue->EqualsLiteral("*") ? + aContent->HasAttr(aNamespaceID, aAttrName) : + aContent->AttrValueIs(aNamespaceID, aAttrName, *attrValue, + eCaseMatters); + } + + // Qualified name match. This takes more work. + + uint32_t count = aContent->GetAttrCount(); + for (uint32_t i = 0; i < count; ++i) { + const nsAttrName* name = aContent->GetAttrNameAt(i); + bool nameMatch; + if (name->IsAtom()) { + nameMatch = name->Atom() == aAttrName; + } else if (aNamespaceID == kNameSpaceID_Wildcard) { + nameMatch = name->NodeInfo()->Equals(aAttrName); + } else { + nameMatch = name->NodeInfo()->QualifiedNameEquals(aAttrName); + } + + if (nameMatch) { + return attrValue->EqualsLiteral("*") || + aContent->AttrValueIs(name->NamespaceID(), name->LocalName(), + *attrValue, eCaseMatters); + } + } + + return false; +} + +nsresult +XULDocument::PrepareToLoad(nsISupports* aContainer, + const char* aCommand, + nsIChannel* aChannel, + nsILoadGroup* aLoadGroup, + nsIParser** aResult) +{ + // Get the document's principal + nsCOMPtr<nsIPrincipal> principal; + nsContentUtils::GetSecurityManager()-> + GetChannelResultPrincipal(aChannel, getter_AddRefs(principal)); + return PrepareToLoadPrototype(mDocumentURI, aCommand, principal, aResult); +} + + +nsresult +XULDocument::PrepareToLoadPrototype(nsIURI* aURI, const char* aCommand, + nsIPrincipal* aDocumentPrincipal, + nsIParser** aResult) +{ + nsresult rv; + + // Create a new prototype document. + rv = NS_NewXULPrototypeDocument(getter_AddRefs(mCurrentPrototype)); + if (NS_FAILED(rv)) return rv; + + rv = mCurrentPrototype->InitPrincipal(aURI, aDocumentPrincipal); + if (NS_FAILED(rv)) { + mCurrentPrototype = nullptr; + return rv; + } + + // Bootstrap the master document prototype. + if (! mMasterPrototype) { + mMasterPrototype = mCurrentPrototype; + // Set our principal based on the master proto. + SetPrincipal(aDocumentPrincipal); + } + + // Create a XUL content sink, a parser, and kick off a load for + // the overlay. + RefPtr<XULContentSinkImpl> sink = new XULContentSinkImpl(); + + rv = sink->Init(this, mCurrentPrototype); + NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to initialize datasource sink"); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr<nsIParser> parser = do_CreateInstance(kParserCID, &rv); + NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create parser"); + if (NS_FAILED(rv)) return rv; + + parser->SetCommand(nsCRT::strcmp(aCommand, "view-source") ? eViewNormal : + eViewSource); + + parser->SetDocumentCharset(NS_LITERAL_CSTRING("UTF-8"), + kCharsetFromDocTypeDefault); + parser->SetContentSink(sink); // grabs a reference to the parser + + parser.forget(aResult); + return NS_OK; +} + + +nsresult +XULDocument::ApplyPersistentAttributes() +{ + // For non-chrome documents, persistance is simply broken + if (!nsContentUtils::IsSystemPrincipal(NodePrincipal())) + return NS_ERROR_NOT_AVAILABLE; + + // Add all of the 'persisted' attributes into the content + // model. + if (!mLocalStore) { + mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1"); + if (NS_WARN_IF(!mLocalStore)) { + return NS_ERROR_NOT_INITIALIZED; + } + } + + mApplyingPersistedAttrs = true; + ApplyPersistentAttributesInternal(); + mApplyingPersistedAttrs = false; + + // After we've applied persistence once, we should only reapply + // it to nodes created by overlays + mRestrictPersistence = true; + mPersistenceIds.Clear(); + + return NS_OK; +} + + +nsresult +XULDocument::ApplyPersistentAttributesInternal() +{ + nsCOMArray<nsIContent> elements; + + nsAutoCString utf8uri; + nsresult rv = mDocumentURI->GetSpec(utf8uri); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + NS_ConvertUTF8toUTF16 uri(utf8uri); + + // Get a list of element IDs for which persisted values are available + nsCOMPtr<nsIStringEnumerator> ids; + rv = mLocalStore->GetIDsEnumerator(uri, getter_AddRefs(ids)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + while (1) { + bool hasmore = false; + ids->HasMore(&hasmore); + if (!hasmore) { + break; + } + + nsAutoString id; + ids->GetNext(id); + + if (mRestrictPersistence && !mPersistenceIds.Contains(id)) { + continue; + } + + // This will clear the array if there are no elements. + GetElementsForID(id, elements); + if (!elements.Count()) { + continue; + } + + rv = ApplyPersistentAttributesToElements(id, elements); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + return NS_OK; +} + + +nsresult +XULDocument::ApplyPersistentAttributesToElements(const nsAString &aID, + nsCOMArray<nsIContent>& aElements) +{ + nsAutoCString utf8uri; + nsresult rv = mDocumentURI->GetSpec(utf8uri); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + NS_ConvertUTF8toUTF16 uri(utf8uri); + + // Get a list of attributes for which persisted values are available + nsCOMPtr<nsIStringEnumerator> attrs; + rv = mLocalStore->GetAttributeEnumerator(uri, aID, getter_AddRefs(attrs)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + while (1) { + bool hasmore = PR_FALSE; + attrs->HasMore(&hasmore); + if (!hasmore) { + break; + } + + nsAutoString attrstr; + attrs->GetNext(attrstr); + + nsAutoString value; + rv = mLocalStore->GetValue(uri, aID, attrstr, value); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCOMPtr<nsIAtom> attr = NS_Atomize(attrstr); + if (NS_WARN_IF(!attr)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + uint32_t cnt = aElements.Count(); + + for (int32_t i = int32_t(cnt) - 1; i >= 0; --i) { + nsCOMPtr<nsIContent> element = aElements.SafeObjectAt(i); + if (!element) { + continue; + } + + rv = element->SetAttr(kNameSpaceID_None, attr, value, PR_TRUE); + } + } + + return NS_OK; +} + +void +XULDocument::TraceProtos(JSTracer* aTrc, uint32_t aGCNumber) +{ + uint32_t i, count = mPrototypes.Length(); + for (i = 0; i < count; ++i) { + mPrototypes[i]->TraceProtos(aTrc, aGCNumber); + } + + if (mCurrentPrototype) { + mCurrentPrototype->TraceProtos(aTrc, aGCNumber); + } +} + +//---------------------------------------------------------------------- +// +// XULDocument::ContextStack +// + +XULDocument::ContextStack::ContextStack() + : mTop(nullptr), mDepth(0) +{ +} + +XULDocument::ContextStack::~ContextStack() +{ + while (mTop) { + Entry* doomed = mTop; + mTop = mTop->mNext; + NS_IF_RELEASE(doomed->mElement); + delete doomed; + } +} + +nsresult +XULDocument::ContextStack::Push(nsXULPrototypeElement* aPrototype, + nsIContent* aElement) +{ + Entry* entry = new Entry; + entry->mPrototype = aPrototype; + entry->mElement = aElement; + NS_IF_ADDREF(entry->mElement); + entry->mIndex = 0; + + entry->mNext = mTop; + mTop = entry; + + ++mDepth; + return NS_OK; +} + +nsresult +XULDocument::ContextStack::Pop() +{ + if (mDepth == 0) + return NS_ERROR_UNEXPECTED; + + Entry* doomed = mTop; + mTop = mTop->mNext; + --mDepth; + + NS_IF_RELEASE(doomed->mElement); + delete doomed; + return NS_OK; +} + +nsresult +XULDocument::ContextStack::Peek(nsXULPrototypeElement** aPrototype, + nsIContent** aElement, + int32_t* aIndex) +{ + if (mDepth == 0) + return NS_ERROR_UNEXPECTED; + + *aPrototype = mTop->mPrototype; + *aElement = mTop->mElement; + NS_IF_ADDREF(*aElement); + *aIndex = mTop->mIndex; + + return NS_OK; +} + + +nsresult +XULDocument::ContextStack::SetTopIndex(int32_t aIndex) +{ + if (mDepth == 0) + return NS_ERROR_UNEXPECTED; + + mTop->mIndex = aIndex; + return NS_OK; +} + + +//---------------------------------------------------------------------- +// +// Content model walking routines +// + +nsresult +XULDocument::PrepareToWalk() +{ + // Prepare to walk the mCurrentPrototype + nsresult rv; + + // Keep an owning reference to the prototype document so that its + // elements aren't yanked from beneath us. + mPrototypes.AppendElement(mCurrentPrototype); + + // Get the prototype's root element and initialize the context + // stack for the prototype walk. + nsXULPrototypeElement* proto = mCurrentPrototype->GetRootElement(); + + if (! proto) { + if (MOZ_LOG_TEST(gXULLog, LogLevel::Error)) { + nsCOMPtr<nsIURI> url = mCurrentPrototype->GetURI(); + + nsAutoCString urlspec; + rv = url->GetSpec(urlspec); + if (NS_FAILED(rv)) return rv; + + MOZ_LOG(gXULLog, LogLevel::Error, + ("xul: error parsing '%s'", urlspec.get())); + } + + return NS_OK; + } + + uint32_t piInsertionPoint = 0; + if (mState != eState_Master) { + int32_t indexOfRoot = IndexOf(GetRootElement()); + NS_ASSERTION(indexOfRoot >= 0, + "No root content when preparing to walk overlay!"); + piInsertionPoint = indexOfRoot; + } + + const nsTArray<RefPtr<nsXULPrototypePI> >& processingInstructions = + mCurrentPrototype->GetProcessingInstructions(); + + uint32_t total = processingInstructions.Length(); + for (uint32_t i = 0; i < total; ++i) { + rv = CreateAndInsertPI(processingInstructions[i], + this, piInsertionPoint + i); + if (NS_FAILED(rv)) return rv; + } + + // Now check the chrome registry for any additional overlays. + rv = AddChromeOverlays(); + if (NS_FAILED(rv)) return rv; + + // Do one-time initialization if we're preparing to walk the + // master document's prototype. + RefPtr<Element> root; + + if (mState == eState_Master) { + // Add the root element + rv = CreateElementFromPrototype(proto, getter_AddRefs(root), true); + if (NS_FAILED(rv)) return rv; + + rv = AppendChildTo(root, false); + if (NS_FAILED(rv)) return rv; + + rv = AddElementToRefMap(root); + if (NS_FAILED(rv)) return rv; + + // 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 +XULDocument::CreateAndInsertPI(const nsXULPrototypePI* aProtoPI, + nsINode* aParent, uint32_t aIndex) +{ + NS_PRECONDITION(aProtoPI, "null ptr"); + NS_PRECONDITION(aParent, "null ptr"); + + RefPtr<ProcessingInstruction> node = + NS_NewXMLProcessingInstruction(mNodeInfoManager, aProtoPI->mTarget, + aProtoPI->mData); + + nsresult rv; + if (aProtoPI->mTarget.EqualsLiteral("xml-stylesheet")) { + rv = InsertXMLStylesheetPI(aProtoPI, aParent, aIndex, node); + } else if (aProtoPI->mTarget.EqualsLiteral("xul-overlay")) { + rv = InsertXULOverlayPI(aProtoPI, aParent, aIndex, node); + } else { + // No special processing, just add the PI to the document. + rv = aParent->InsertChildAt(node, aIndex, false); + } + + return rv; +} + +nsresult +XULDocument::InsertXMLStylesheetPI(const nsXULPrototypePI* aProtoPI, + nsINode* aParent, + uint32_t aIndex, + nsIContent* aPINode) +{ + nsCOMPtr<nsIStyleSheetLinkingElement> ssle(do_QueryInterface(aPINode)); + NS_ASSERTION(ssle, "passed XML Stylesheet node does not " + "implement nsIStyleSheetLinkingElement!"); + + nsresult rv; + + ssle->InitStyleLinkElement(false); + // We want to be notified when the style sheet finishes loading, so + // disable style sheet loading for now. + ssle->SetEnableUpdates(false); + ssle->OverrideBaseURI(mCurrentPrototype->GetURI()); + + rv = aParent->InsertChildAt(aPINode, aIndex, false); + if (NS_FAILED(rv)) return rv; + + ssle->SetEnableUpdates(true); + + // load the stylesheet if necessary, passing ourselves as + // nsICSSObserver + bool willNotify; + bool isAlternate; + rv = ssle->UpdateStyleSheet(this, &willNotify, &isAlternate); + if (NS_SUCCEEDED(rv) && willNotify && !isAlternate) { + ++mPendingSheets; + } + + // Ignore errors from UpdateStyleSheet; we don't want failure to + // do that to break the XUL document load. But do propagate out + // NS_ERROR_OUT_OF_MEMORY. + if (rv == NS_ERROR_OUT_OF_MEMORY) { + return rv; + } + + return NS_OK; +} + +nsresult +XULDocument::InsertXULOverlayPI(const nsXULPrototypePI* aProtoPI, + nsINode* aParent, + uint32_t aIndex, + nsIContent* aPINode) +{ + nsresult rv; + + rv = aParent->InsertChildAt(aPINode, aIndex, false); + if (NS_FAILED(rv)) return rv; + + // xul-overlay PI is special only in prolog + if (!nsContentUtils::InProlog(aPINode)) { + return NS_OK; + } + + nsAutoString href; + nsContentUtils::GetPseudoAttributeValue(aProtoPI->mData, + nsGkAtoms::href, + href); + + // If there was no href, we can't do anything with this PI + if (href.IsEmpty()) { + return NS_OK; + } + + // Add the overlay to our list of overlays that need to be processed. + nsCOMPtr<nsIURI> uri; + + rv = NS_NewURI(getter_AddRefs(uri), href, nullptr, + mCurrentPrototype->GetURI()); + if (NS_SUCCEEDED(rv)) { + // We insert overlays into mUnloadedOverlays at the same index in + // document order, so they end up in the reverse of the document + // order in mUnloadedOverlays. + // This is needed because the code in ResumeWalk loads the overlays + // by processing the last item of mUnloadedOverlays and removing it + // from the array. + mUnloadedOverlays.InsertElementAt(0, uri); + rv = NS_OK; + } else if (rv == NS_ERROR_MALFORMED_URI) { + // The URL is bad, move along. Don't propagate for now. + // XXX report this to the Error Console (bug 359846) + rv = NS_OK; + } + + return rv; +} + +nsresult +XULDocument::AddChromeOverlays() +{ + nsresult rv; + + nsCOMPtr<nsIURI> docUri = mCurrentPrototype->GetURI(); + + /* overlays only apply to chrome or about URIs */ + if (!IsOverlayAllowed(docUri)) return NS_OK; + + nsCOMPtr<nsIXULOverlayProvider> chromeReg = + mozilla::services::GetXULOverlayProviderService(); + // 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<nsISimpleEnumerator> overlays; + rv = chromeReg->GetXULOverlays(docUri, getter_AddRefs(overlays)); + NS_ENSURE_SUCCESS(rv, rv); + + bool moreOverlays; + nsCOMPtr<nsISupports> next; + nsCOMPtr<nsIURI> uri; + + while (NS_SUCCEEDED(rv = overlays->HasMoreElements(&moreOverlays)) && + moreOverlays) { + + rv = overlays->GetNext(getter_AddRefs(next)); + if (NS_FAILED(rv) || !next) break; + + uri = do_QueryInterface(next); + if (!uri) { + NS_ERROR("Chrome registry handed me a non-nsIURI object!"); + continue; + } + + // Same comment as in XULDocument::InsertXULOverlayPI + mUnloadedOverlays.InsertElementAt(0, uri); + } + + return rv; +} + +NS_IMETHODIMP +XULDocument::LoadOverlay(const nsAString& aURL, nsIObserver* aObserver) +{ + nsresult rv; + + nsCOMPtr<nsIURI> uri; + rv = NS_NewURI(getter_AddRefs(uri), aURL, nullptr); + if (NS_FAILED(rv)) return rv; + + if (aObserver) { + nsIObserver* obs = nullptr; + if (!mOverlayLoadObservers) { + mOverlayLoadObservers = new nsInterfaceHashtable<nsURIHashKey,nsIObserver>; + } + 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); + } + bool shouldReturn, failureFromContent; + rv = LoadOverlayInternal(uri, true, &shouldReturn, &failureFromContent); + if (NS_FAILED(rv) && mOverlayLoadObservers) + mOverlayLoadObservers->Remove(uri); // remove the observer if LoadOverlayInternal generated an error + return rv; +} + +nsresult +XULDocument::LoadOverlayInternal(nsIURI* aURI, bool aIsDynamic, + bool* aShouldReturn, + bool* aFailureFromContent) +{ + nsresult rv; + + *aShouldReturn = false; + *aFailureFromContent = false; + + if (MOZ_LOG_TEST(gXULLog, LogLevel::Debug)) { + nsCOMPtr<nsIURI> uri; + mChannel->GetOriginalURI(getter_AddRefs(uri)); + + MOZ_LOG(gXULLog, LogLevel::Debug, + ("xul: %s loading overlay %s", + uri ? uri->GetSpecOrDefault().get() : "", + aURI->GetSpecOrDefault().get())); + } + + if (aIsDynamic) + mResolutionPhase = nsForwardReference::eStart; + + // Look in the prototype cache for the prototype document with + // the specified overlay URI. Only use the cache if the containing + // document is chrome otherwise it may not have a system principal and + // the cached document will, see bug 565610. + bool overlayIsChrome = IsChromeURI(aURI); + bool documentIsChrome = IsChromeURI(mDocumentURI); + mCurrentPrototype = overlayIsChrome && documentIsChrome ? + nsXULPrototypeCache::GetInstance()->GetPrototype(aURI) : nullptr; + + // Same comment as nsChromeProtocolHandler::NewChannel and + // XULDocument::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. + + bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled(); + if (useXULCache && mCurrentPrototype) { + bool 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 = true; + return NS_OK; + } + + MOZ_LOG(gXULLog, LogLevel::Debug, ("xul: overlay was cached")); + + // Found the overlay's prototype in the cache, fully loaded. If + // this is a dynamic overlay, this will call ResumeWalk. + // Otherwise, we'll return to ResumeWalk, which called us. + return OnPrototypeLoadDone(aIsDynamic); + } + else { + // Not there. Initiate a load. + MOZ_LOG(gXULLog, LogLevel::Debug, ("xul: overlay was not cached")); + + if (mIsGoingAway) { + MOZ_LOG(gXULLog, LogLevel::Debug, ("xul: ...and document already destroyed")); + return NS_ERROR_NOT_AVAILABLE; + } + + // We'll set the right principal on the proto doc when we get + // OnStartRequest from the parser, so just pass in a null principal for + // now. + nsCOMPtr<nsIParser> parser; + rv = PrepareToLoadPrototype(aURI, "view", nullptr, 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<nsIStreamListener> 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. + RefPtr<ParserObserver> parserObserver = + new ParserObserver(this, mCurrentPrototype); + parser->Parse(aURI, parserObserver); + parserObserver = nullptr; + + nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup); + nsCOMPtr<nsIChannel> channel; + // Set the owner of the channel to be our principal so + // that the overlay's JSObjects etc end up being created + // with the right principal and in the correct + // compartment. + rv = NS_NewChannel(getter_AddRefs(channel), + aURI, + NodePrincipal(), + nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS | + nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL, + nsIContentPolicy::TYPE_OTHER, + group); + + if (NS_SUCCEEDED(rv)) { + rv = channel->AsyncOpen2(listener); + } + + if (NS_FAILED(rv)) { + // Abandon this prototype + mCurrentPrototype = nullptr; + + // The parser won't get an OnStartRequest and + // OnStopRequest, so it needs a Terminate. + parser->Terminate(); + + // Just move on to the next overlay. + ReportMissingOverlay(aURI); + + // XXX the error could indicate an internal error as well... + *aFailureFromContent = 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 AsyncOpen, + // or chrome code will wrongly create a cached chrome channel + // instead of a real one. Prototypes are only cached when the + // document to be overlayed is chrome to avoid caching overlay + // scripts with incorrect principals, see bug 565610. + if (useXULCache && overlayIsChrome && documentIsChrome) { + nsXULPrototypeCache::GetInstance()->PutPrototype(mCurrentPrototype); + } + + // 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 = true; + } + return NS_OK; +} + +nsresult +XULDocument::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., + // <html:script src="..." />) can be properly re-loaded if the + // cached copy of the document becomes stale. + nsresult rv; + nsCOMPtr<nsIURI> overlayURI = + mCurrentPrototype ? mCurrentPrototype->GetURI() : nullptr; + + 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. + // This will always be a node already constructed and + // inserted to the actual document. + nsXULPrototypeElement* proto; + nsCOMPtr<nsIContent> element; + int32_t indx; // all children of proto before indx (not + // inclusive) have already been constructed + rv = mContextStack.Peek(&proto, getter_AddRefs(element), &indx); + if (NS_FAILED(rv)) return rv; + + if (indx >= (int32_t)proto->mChildren.Length()) { + 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->AsElement()); + + if (element->NodeInfo()->Equals(nsGkAtoms::style, + kNameSpaceID_XHTML) || + element->NodeInfo()->Equals(nsGkAtoms::style, + kNameSpaceID_SVG)) { + // XXX sucks that we have to do this - + // see bug 370111 + nsCOMPtr<nsIStyleSheetLinkingElement> ssle = + do_QueryInterface(element); + NS_ASSERTION(ssle, "<html:style> doesn't implement " + "nsIStyleSheetLinkingElement?"); + bool willNotify; + bool isAlternate; + ssle->UpdateStyleSheet(nullptr, &willNotify, + &isAlternate); + } + } + } + // 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); + + // Whether we're in the "first ply" of an overlay: + // the "hookup" nodes. In the case !processingOverlayHookupNodes, + // 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. + bool processingOverlayHookupNodes = (mState == eState_Overlay) && + (mContextStack.Depth() == 1); + + NS_ASSERTION(element || processingOverlayHookupNodes, + "no element on context stack"); + + switch (childproto->mType) { + case nsXULPrototypeNode::eType_Element: { + // An 'element', which may contain more content. + nsXULPrototypeElement* protoele = + static_cast<nsXULPrototypeElement*>(childproto); + + RefPtr<Element> child; + + if (!processingOverlayHookupNodes) { + rv = CreateElementFromPrototype(protoele, + getter_AddRefs(child), + false); + if (NS_FAILED(rv)) return rv; + + // ...and append it to the content model. + rv = element->AppendChildTo(child, false); + if (NS_FAILED(rv)) return rv; + + // If we're only restoring persisted things on + // some elements, store the ID here to do that. + if (mRestrictPersistence) { + nsIAtom* id = child->GetID(); + if (id) { + mPersistenceIds.PutEntry(nsDependentAtomString(id)); + } + } + + // 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->mChildren.Length() > 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); + } + } + } + break; + + case nsXULPrototypeNode::eType_Script: { + // A script reference. Execute the script immediately; + // this may have side effects in the content model. + nsXULPrototypeScript* scriptproto = + static_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. + bool 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->HasScriptObject()) { + // An inline script + rv = ExecuteScript(scriptproto); + if (NS_FAILED(rv)) return rv; + } + } + break; + + case nsXULPrototypeNode::eType_Text: { + // A simple text node. + + if (!processingOverlayHookupNodes) { + // This does mean that text nodes that are direct children + // of <overlay> get ignored. + + RefPtr<nsTextNode> text = + new nsTextNode(mNodeInfoManager); + + nsXULPrototypeText* textproto = + static_cast<nsXULPrototypeText*>(childproto); + text->SetText(textproto->mValue, false); + + rv = element->AppendChildTo(text, false); + NS_ENSURE_SUCCESS(rv, rv); + } + } + break; + + case nsXULPrototypeNode::eType_PI: { + nsXULPrototypePI* piProto = + static_cast<nsXULPrototypePI*>(childproto); + + // <?xul-overlay?> and <?xml-stylesheet?> don't have effect + // outside the prolog, like they used to. Issue a warning. + + if (piProto->mTarget.EqualsLiteral("xml-stylesheet") || + piProto->mTarget.EqualsLiteral("xul-overlay")) { + + const char16_t* params[] = { piProto->mTarget.get() }; + + nsContentUtils::ReportToConsole( + nsIScriptError::warningFlag, + NS_LITERAL_CSTRING("XUL Document"), nullptr, + nsContentUtils::eXUL_PROPERTIES, + "PINotInProlog", + params, ArrayLength(params), + overlayURI); + } + + nsIContent* parent = processingOverlayHookupNodes ? + GetRootElement() : element.get(); + + if (parent) { + // an inline script could have removed the root element + rv = CreateAndInsertPI(piProto, parent, + parent->GetChildCount()); + NS_ENSURE_SUCCESS(rv, rv); + } + } + break; + + default: + NS_NOTREACHED("Unexpected nsXULPrototypeNode::Type value"); + } + } + + // 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; + + // If there are no overlay URIs, then we're done. + uint32_t count = mUnloadedOverlays.Length(); + if (! count) + break; + + nsCOMPtr<nsIURI> uri = mUnloadedOverlays[count-1]; + mUnloadedOverlays.RemoveElementAt(count - 1); + + bool shouldReturn, failureFromContent; + rv = LoadOverlayInternal(uri, 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 (mOverlayLoadObservers) { + nsIObserver *obs = mOverlayLoadObservers->GetWeak(overlayURI); + if (obs) { + // This overlay has an unloaded overlay, so it will never + // notify. The best we can do is to notify for the unloaded + // overlay instead, assuming nobody is already notifiable + // for it. Note that this will confuse the observer. + if (!mOverlayLoadObservers->GetWeak(uri)) + mOverlayLoadObservers->Put(uri, obs); + mOverlayLoadObservers->Remove(overlayURI); + } + } + if (shouldReturn) + return NS_OK; + overlayURI.swap(uri); + } + + // 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; + + ApplyPersistentAttributes(); + + mStillWalking = false; + if (mPendingSheets == 0) { + rv = DoneWalking(); + } + return rv; +} + +nsresult +XULDocument::DoneWalking() +{ + NS_PRECONDITION(mPendingSheets == 0, "there are sheets to be loaded"); + NS_PRECONDITION(!mStillWalking, "walk not done"); + + // XXXldb This is where we should really be setting the chromehidden + // attribute. + + { + mozAutoDocUpdate updateBatch(this, UPDATE_STYLE, true); + uint32_t count = mOverlaySheets.Length(); + for (uint32_t i = 0; i < count; ++i) { + AddStyleSheet(mOverlaySheets[i]); + } + } + + mOverlaySheets.Clear(); + + 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 = true; + + NotifyPossibleTitleChange(false); + + // Before starting layout, check whether we're a toplevel chrome + // window. If we are, set our chrome flags now, so that we don't have + // to restyle the whole frame tree after StartLayout. + nsCOMPtr<nsIDocShellTreeItem> item = GetDocShell(); + if (item) { + nsCOMPtr<nsIDocShellTreeOwner> owner; + item->GetTreeOwner(getter_AddRefs(owner)); + nsCOMPtr<nsIXULWindow> xulWin = do_GetInterface(owner); + if (xulWin) { + nsCOMPtr<nsIDocShell> xulWinShell; + xulWin->GetDocShell(getter_AddRefs(xulWinShell)); + if (SameCOMIdentity(xulWinShell, item)) { + // We're the chrome document! Apply our chrome flags now. + xulWin->ApplyChromeFlags(); + } + } + } + + StartLayout(); + + if (mIsWritingFastLoad && IsChromeURI(mDocumentURI)) + nsXULPrototypeCache::GetInstance()->WritePrototype(mMasterPrototype); + + NS_ASSERTION(mDelayFrameLoaderInitialization, + "mDelayFrameLoaderInitialization should be true!"); + mDelayFrameLoaderInitialization = false; + NS_WARNING_ASSERTION( + mUpdateNestLevel == 0, + "Constructing XUL document in middle of an update?"); + if (mUpdateNestLevel == 0) { + MaybeInitializeFinalizeFrameLoaders(); + } + + NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad, (this)); + + // DispatchContentLoadedEvents undoes the onload-blocking we + // did in PrepareToWalk(). + DispatchContentLoadedEvents(); + + mInitialLayoutComplete = true; + + // Walk the set of pending load notifications and notify any observers. + // See below for detail. + if (mPendingOverlayLoadNotifications) { + nsInterfaceHashtable<nsURIHashKey,nsIObserver>* observers = + mOverlayLoadObservers.get(); + for (auto iter = mPendingOverlayLoadNotifications->Iter(); + !iter.Done(); + iter.Next()) { + nsIURI* aKey = iter.Key(); + iter.Data()->Observe(aKey, "xul-overlay-merged", + EmptyString().get()); + + if (observers) { + observers->Remove(aKey); + } + + iter.Remove(); + } + } + } + else { + if (mOverlayLoadObservers) { + nsCOMPtr<nsIURI> overlayURI = mCurrentPrototype->GetURI(); + nsCOMPtr<nsIObserver> 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! + + if (!mPendingOverlayLoadNotifications) { + mPendingOverlayLoadNotifications = + new nsInterfaceHashtable<nsURIHashKey,nsIObserver>; + } + + 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 NS_OK; +} + +NS_IMETHODIMP +XULDocument::StyleSheetLoaded(StyleSheet* aSheet, + bool aWasAlternate, + nsresult aStatus) +{ + if (!aWasAlternate) { + // Don't care about when alternate sheets finish loading + + NS_ASSERTION(mPendingSheets > 0, + "Unexpected StyleSheetLoaded notification"); + + --mPendingSheets; + + if (!mStillWalking && mPendingSheets == 0) { + return DoneWalking(); + } + } + + return NS_OK; +} + +void +XULDocument::MaybeBroadcast() +{ + // Only broadcast when not in an update and when safe to run scripts. + if (mUpdateNestLevel == 0 && + (mDelayedAttrChangeBroadcasts.Length() || + mDelayedBroadcasters.Length())) { + if (!nsContentUtils::IsSafeToRunScript()) { + if (!mInDestructor) { + nsContentUtils::AddScriptRunner( + NewRunnableMethod(this, &XULDocument::MaybeBroadcast)); + } + return; + } + if (!mHandlingDelayedAttrChange) { + mHandlingDelayedAttrChange = true; + for (uint32_t i = 0; i < mDelayedAttrChangeBroadcasts.Length(); ++i) { + nsIAtom* attrName = mDelayedAttrChangeBroadcasts[i].mAttrName; + if (mDelayedAttrChangeBroadcasts[i].mNeedsAttrChange) { + nsCOMPtr<nsIContent> listener = + do_QueryInterface(mDelayedAttrChangeBroadcasts[i].mListener); + const nsString& value = mDelayedAttrChangeBroadcasts[i].mAttr; + if (mDelayedAttrChangeBroadcasts[i].mSetAttr) { + listener->SetAttr(kNameSpaceID_None, attrName, value, + true); + } else { + listener->UnsetAttr(kNameSpaceID_None, attrName, + true); + } + } + ExecuteOnBroadcastHandlerFor(mDelayedAttrChangeBroadcasts[i].mBroadcaster, + mDelayedAttrChangeBroadcasts[i].mListener, + attrName); + } + mDelayedAttrChangeBroadcasts.Clear(); + mHandlingDelayedAttrChange = false; + } + + uint32_t length = mDelayedBroadcasters.Length(); + if (length) { + bool oldValue = mHandlingDelayedBroadcasters; + mHandlingDelayedBroadcasters = true; + nsTArray<nsDelayedBroadcastUpdate> delayedBroadcasters; + mDelayedBroadcasters.SwapElements(delayedBroadcasters); + for (uint32_t i = 0; i < length; ++i) { + SynchronizeBroadcastListener(delayedBroadcasters[i].mBroadcaster, + delayedBroadcasters[i].mListener, + delayedBroadcasters[i].mAttr); + } + mHandlingDelayedBroadcasters = oldValue; + } + } +} + +void +XULDocument::EndUpdate(nsUpdateType aUpdateType) +{ + XMLDocument::EndUpdate(aUpdateType); + + MaybeBroadcast(); +} + +void +XULDocument::ReportMissingOverlay(nsIURI* aURI) +{ + NS_PRECONDITION(aURI, "Must have a URI"); + + NS_ConvertUTF8toUTF16 utfSpec(aURI->GetSpecOrDefault()); + const char16_t* params[] = { utfSpec.get() }; + nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, + NS_LITERAL_CSTRING("XUL Document"), this, + nsContentUtils::eXUL_PROPERTIES, + "MissingOverlay", + params, ArrayLength(params)); +} + +nsresult +XULDocument::LoadScript(nsXULPrototypeScript* aScriptProto, bool* aBlock) +{ + // Load a transcluded script + nsresult rv; + + bool isChromeDoc = IsChromeURI(mDocumentURI); + + if (isChromeDoc && aScriptProto->HasScriptObject()) { + rv = ExecuteScript(aScriptProto); + + // Ignore return value from execution, and don't block + *aBlock = 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! + bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled(); + + if (isChromeDoc && useXULCache) { + JSScript* newScriptObject = + nsXULPrototypeCache::GetInstance()->GetScript( + aScriptProto->mSrcURI); + if (newScriptObject) { + // The script language for a proto must remain constant - we + // can't just change it for this unexpected language. + aScriptProto->Set(newScriptObject); + } + + if (aScriptProto->HasScriptObject()) { + rv = ExecuteScript(aScriptProto); + + // Ignore return value from execution, and don't block + *aBlock = false; + return NS_OK; + } + } + + // Release script objects from FastLoad since we decided against using them + aScriptProto->UnlinkJSObjects(); + + // 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 (isChromeDoc && 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<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup); + + // Note: the loader will keep itself alive while it's loading. + nsCOMPtr<nsIStreamLoader> loader; + rv = NS_NewStreamLoader(getter_AddRefs(loader), + aScriptProto->mSrcURI, + this, // aObserver + this, // aRequestingContext + nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS, + nsIContentPolicy::TYPE_INTERNAL_SCRIPT, + group); + + if (NS_FAILED(rv)) { + mCurrentScriptProto = nullptr; + return rv; + } + + aScriptProto->mSrcLoading = true; + } + + // Block until OnStreamComplete resumes us. + *aBlock = true; + return NS_OK; +} + +NS_IMETHODIMP +XULDocument::OnStreamComplete(nsIStreamLoader* aLoader, + nsISupports* context, + nsresult aStatus, + uint32_t stringLen, + const uint8_t* string) +{ + nsCOMPtr<nsIRequest> request; + aLoader->GetRequest(getter_AddRefs(request)); + nsCOMPtr<nsIChannel> channel = do_QueryInterface(request); + +#ifdef DEBUG + // print a load error on bad status + if (NS_FAILED(aStatus)) { + if (channel) { + nsCOMPtr<nsIURI> uri; + channel->GetURI(getter_AddRefs(uri)); + if (uri) { + printf("Failed to load %s\n", uri->GetSpecOrDefault().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 = aStatus; + + NS_ASSERTION(mCurrentScriptProto && mCurrentScriptProto->mSrcLoading, + "script source not loading on unichar stream complete?"); + if (!mCurrentScriptProto) { + // XXX Wallpaper for bug 270042 + return NS_OK; + } + + 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->mScriptObject, causing control flow at the top of LoadScript + // not to reach here. + nsCOMPtr<nsIURI> uri = mCurrentScriptProto->mSrcURI; + + // XXX should also check nsIHttpChannel::requestSucceeded + + MOZ_ASSERT(!mOffThreadCompiling && (mOffThreadCompileStringLength == 0 && + !mOffThreadCompileStringBuf), + "XULDocument can't load multiple scripts at once"); + + rv = nsScriptLoader::ConvertToUTF16(channel, string, stringLen, + EmptyString(), this, + mOffThreadCompileStringBuf, + mOffThreadCompileStringLength); + if (NS_SUCCEEDED(rv)) { + // Attempt to give ownership of the buffer to the JS engine. If + // we hit offthread compilation, however, we will have to take it + // back below in order to keep the memory alive until compilation + // completes. + JS::SourceBufferHolder srcBuf(mOffThreadCompileStringBuf, + mOffThreadCompileStringLength, + JS::SourceBufferHolder::GiveOwnership); + mOffThreadCompileStringBuf = nullptr; + mOffThreadCompileStringLength = 0; + + rv = mCurrentScriptProto->Compile(srcBuf, uri, 1, this, this); + if (NS_SUCCEEDED(rv) && !mCurrentScriptProto->HasScriptObject()) { + // We will be notified via OnOffThreadCompileComplete when the + // compile finishes. Keep the contents of the compiled script + // alive until the compilation finishes. + mOffThreadCompiling = true; + // If the JS engine did not take the source buffer, then take + // it back here to ensure it remains alive. + mOffThreadCompileStringBuf = srcBuf.take(); + if (mOffThreadCompileStringBuf) { + mOffThreadCompileStringLength = srcBuf.length(); + } + BlockOnload(); + return NS_OK; + } + } + } + + return OnScriptCompileComplete(mCurrentScriptProto->GetScriptObject(), rv); +} + +NS_IMETHODIMP +XULDocument::OnScriptCompileComplete(JSScript* aScript, nsresult aStatus) +{ + // When compiling off thread the script will not have been attached to the + // script proto yet. + if (aScript && !mCurrentScriptProto->HasScriptObject()) + mCurrentScriptProto->Set(aScript); + + // Allow load events to be fired once off thread compilation finishes. + if (mOffThreadCompiling) { + mOffThreadCompiling = false; + UnblockOnload(false); + } + + // After compilation finishes the script's characters are no longer needed. + if (mOffThreadCompileStringBuf) { + js_free(mOffThreadCompileStringBuf); + mOffThreadCompileStringBuf = nullptr; + mOffThreadCompileStringLength = 0; + } + + // Clear mCurrentScriptProto now, but save it first for use below in + // the execute code, and in the while loop that resumes walks of other + // documents that raced to load this script. + nsXULPrototypeScript* scriptProto = mCurrentScriptProto; + mCurrentScriptProto = nullptr; + + // 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 = false; + + nsresult rv = aStatus; + if (NS_SUCCEEDED(rv)) { + rv = ExecuteScript(scriptProto); + + // 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 XULDocument is unloaded. + // + // (See http://bugzilla.mozilla.org/show_bug.cgi?id=98207 for + // the true crime story.) + bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled(); + + if (useXULCache && IsChromeURI(mDocumentURI) && scriptProto->HasScriptObject()) { + JS::Rooted<JSScript*> script(RootingCx(), scriptProto->GetScriptObject()); + nsXULPrototypeCache::GetInstance()->PutScript( + scriptProto->mSrcURI, script); + } + + 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. + scriptProto->SerializeOutOfLine(nullptr, mCurrentPrototype); + } + // ignore any evaluation errors + } + + rv = ResumeWalk(); + + // Load a pointer to the prototype-script's list of XULDocuments who + // raced to load the same script + XULDocument** 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 + XULDocument* doc; + while ((doc = *docp) != nullptr) { + NS_ASSERTION(doc->mCurrentScriptProto == scriptProto, + "waiting for wrong script to load?"); + doc->mCurrentScriptProto = nullptr; + + // Unlink doc from scriptProto's list before executing and resuming + *docp = doc->mNextSrcLoadWaiter; + doc->mNextSrcLoadWaiter = nullptr; + + // Execute only if we loaded and compiled successfully, then resume + if (NS_SUCCEEDED(aStatus) && scriptProto->HasScriptObject()) { + doc->ExecuteScript(scriptProto); + } + doc->ResumeWalk(); + NS_RELEASE(doc); + } + + return rv; +} + +nsresult +XULDocument::ExecuteScript(nsXULPrototypeScript *aScript) +{ + NS_PRECONDITION(aScript != nullptr, "null ptr"); + NS_ENSURE_TRUE(aScript, NS_ERROR_NULL_POINTER); + NS_ENSURE_TRUE(mScriptGlobalObject, NS_ERROR_NOT_INITIALIZED); + + nsresult rv; + rv = mScriptGlobalObject->EnsureScriptEnvironment(); + NS_ENSURE_SUCCESS(rv, rv); + + // Execute the precompiled script with the given version + nsAutoMicroTask mt; + + // We're about to run script via JS::CloneAndExecuteScript, so we need an + // AutoEntryScript. This is Gecko specific and not in any spec. + AutoEntryScript aes(mScriptGlobalObject, "precompiled XUL <script> element"); + JSContext* cx = aes.cx(); + + JS::Rooted<JSScript*> scriptObject(cx, aScript->GetScriptObject()); + NS_ENSURE_TRUE(scriptObject, NS_ERROR_UNEXPECTED); + + JS::Rooted<JSObject*> baseGlobal(cx, JS::CurrentGlobalOrNull(cx)); + NS_ENSURE_TRUE(xpc::Scriptability::Get(baseGlobal).Allowed(), NS_OK); + + JSAddonId* addonId = mCurrentPrototype ? MapURIToAddonID(mCurrentPrototype->GetURI()) : nullptr; + JS::Rooted<JSObject*> global(cx, xpc::GetAddonScope(cx, baseGlobal, addonId)); + NS_ENSURE_TRUE(global, NS_ERROR_FAILURE); + + JS::ExposeObjectToActiveJS(global); + JSAutoCompartment ac(cx, global); + + // The script is in the compilation scope. Clone it into the target scope + // and execute it. On failure, ~AutoScriptEntry will handle exceptions, so + // there is no need to manually check the return value. + JS::RootedValue rval(cx); + JS::CloneAndExecuteScript(cx, scriptObject, &rval); + + return NS_OK; +} + + +nsresult +XULDocument::CreateElementFromPrototype(nsXULPrototypeElement* aPrototype, + Element** aResult, + bool aIsRoot) +{ + // Create a content model element from a prototype element. + NS_PRECONDITION(aPrototype != nullptr, "null ptr"); + if (! aPrototype) + return NS_ERROR_NULL_POINTER; + + *aResult = nullptr; + nsresult rv = NS_OK; + + if (MOZ_LOG_TEST(gXULLog, LogLevel::Debug)) { + MOZ_LOG(gXULLog, LogLevel::Debug, + ("xul: creating <%s> from prototype", + NS_ConvertUTF16toUTF8(aPrototype->mNodeInfo->QualifiedName()).get())); + } + + RefPtr<Element> 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, true, aIsRoot, 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. + RefPtr<mozilla::dom::NodeInfo> newNodeInfo; + newNodeInfo = mNodeInfoManager->GetNodeInfo(aPrototype->mNodeInfo->NameAtom(), + aPrototype->mNodeInfo->GetPrefixAtom(), + aPrototype->mNodeInfo->NamespaceID(), + nsIDOMNode::ELEMENT_NODE); + if (!newNodeInfo) return NS_ERROR_OUT_OF_MEMORY; + RefPtr<mozilla::dom::NodeInfo> xtfNi = newNodeInfo; + rv = NS_NewElement(getter_AddRefs(result), newNodeInfo.forget(), + NOT_FROM_PARSER); + if (NS_FAILED(rv)) + return rv; + + rv = AddAttributes(aPrototype, result); + if (NS_FAILED(rv)) return rv; + } + + result.forget(aResult); + + return NS_OK; +} + +nsresult +XULDocument::CreateOverlayElement(nsXULPrototypeElement* aPrototype, + Element** aResult) +{ + nsresult rv; + + RefPtr<Element> element; + rv = CreateElementFromPrototype(aPrototype, getter_AddRefs(element), false); + if (NS_FAILED(rv)) return rv; + + OverlayForwardReference* fwdref = + new OverlayForwardReference(this, element); + + // transferring ownership to ya... + rv = AddForwardReference(fwdref); + if (NS_FAILED(rv)) return rv; + + element.forget(aResult); + return NS_OK; +} + +nsresult +XULDocument::AddAttributes(nsXULPrototypeElement* aPrototype, + nsIContent* aElement) +{ + nsresult rv; + + for (uint32_t 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, + false); + if (NS_FAILED(rv)) return rv; + } + + return NS_OK; +} + + +nsresult +XULDocument::CheckTemplateBuilderHookup(nsIContent* aElement, + bool* 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<nsIDOMXULElement> xulElement = do_QueryInterface(aElement); + if (xulElement) { + nsCOMPtr<nsIRDFCompositeDataSource> ds; + xulElement->GetDatabase(getter_AddRefs(ds)); + if (ds) { + *aNeedsHookup = 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, + nsGkAtoms::datasources); + return NS_OK; +} + +/* static */ nsresult +XULDocument::CreateTemplateBuilder(nsIContent* aElement) +{ + // Check if need to construct a tree builder or content builder. + bool isTreeBuilder = false; + + // return successful if the element is not is a document, as an inline + // script could have removed it + nsIDocument* document = aElement->GetUncomposedDoc(); + NS_ENSURE_TRUE(document, NS_OK); + + int32_t nameSpaceID; + nsIAtom* baseTag = document->BindingManager()-> + ResolveTag(aElement, &nameSpaceID); + + if ((nameSpaceID == kNameSpaceID_XUL) && (baseTag == nsGkAtoms::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, nsGkAtoms::flags, flags); + if (flags.Find(NS_LITERAL_STRING("dont-build-content")) >= 0) { + isTreeBuilder = true; + } + } + + if (isTreeBuilder) { + // Create and initialize a tree builder. + nsCOMPtr<nsIXULTemplateBuilder> builder = + do_CreateInstance("@mozilla.org/xul/xul-tree-builder;1"); + + if (! builder) + return NS_ERROR_FAILURE; + + builder->Init(aElement); + + // Create a <treechildren> if one isn't there already. + // XXXvarga what about attributes? + nsCOMPtr<nsIContent> bodyContent; + nsXULContentUtils::FindChildByTag(aElement, kNameSpaceID_XUL, + nsGkAtoms::treechildren, + getter_AddRefs(bodyContent)); + + if (! bodyContent) { + bodyContent = + document->CreateElem(nsDependentAtomString(nsGkAtoms::treechildren), + nullptr, kNameSpaceID_XUL); + + aElement->AppendChildTo(bodyContent, false); + } + } + else { + // Create and initialize a content builder. + nsCOMPtr<nsIXULTemplateBuilder> builder + = do_CreateInstance("@mozilla.org/xul/xul-template-builder;1"); + + if (! builder) + return NS_ERROR_FAILURE; + + builder->Init(aElement); + builder->CreateContents(aElement, false); + } + + return NS_OK; +} + + +nsresult +XULDocument::AddPrototypeSheets() +{ + nsresult rv; + + const nsCOMArray<nsIURI>& sheets = mCurrentPrototype->GetStyleSheetReferences(); + + for (int32_t i = 0; i < sheets.Count(); i++) { + nsCOMPtr<nsIURI> uri = sheets[i]; + + RefPtr<StyleSheet> incompleteSheet; + rv = CSSLoader()->LoadSheet(uri, + mCurrentPrototype->DocumentPrincipal(), + EmptyCString(), this, + &incompleteSheet); + + // XXXldb We need to prevent bogus sheets from being held in the + // prototype's list, but until then, don't propagate the failure + // from LoadSheet (and thus exit the loop). + if (NS_SUCCEEDED(rv)) { + ++mPendingSheets; + if (!mOverlaySheets.AppendElement(incompleteSheet)) { + return NS_ERROR_OUT_OF_MEMORY; + } + } + } + + return NS_OK; +} + + +//---------------------------------------------------------------------- +// +// XULDocument::OverlayForwardReference +// + +nsForwardReference::Result +XULDocument::OverlayForwardReference::Resolve() +{ + // Resolve a forward reference from an overlay element; attempt to + // hook it up into the main document. + nsresult rv; + nsCOMPtr<nsIContent> target; + + nsIPresShell *shell = mDocument->GetShell(); + bool notify = shell && shell->DidInitialize(); + + nsAutoString id; + mOverlay->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id); + if (id.IsEmpty()) { + // mOverlay is a direct child of <overlay> and has no id. + // Insert it under the root element in the base document. + Element* root = mDocument->GetRootElement(); + if (!root) { + return eResolve_Error; + } + + rv = mDocument->InsertElement(root, mOverlay, notify); + if (NS_FAILED(rv)) return eResolve_Error; + + target = mOverlay; + } + else { + // The hook-up element has an id, try to match it with an element + // with the same id in the base document. + target = mDocument->GetElementById(id); + + // If we can't find the element in the document, defer the hookup + // until later. + if (!target) + return eResolve_Later; + + rv = Merge(target, mOverlay, notify); + if (NS_FAILED(rv)) return eResolve_Error; + } + + // Check if 'target' is still in our document --- it might not be! + if (!notify && target->GetUncomposedDoc() == mDocument) { + // Add child and any descendants to the element map + // XXX this is bogus, the content in 'target' might already be + // in the document + rv = mDocument->AddSubtreeToDocument(target); + if (NS_FAILED(rv)) return eResolve_Error; + } + + if (MOZ_LOG_TEST(gXULLog, LogLevel::Debug)) { + nsAutoCString idC; + idC.AssignWithConversion(id); + MOZ_LOG(gXULLog, LogLevel::Debug, + ("xul: overlay resolved '%s'", + idC.get())); + } + + mResolved = true; + return eResolve_Succeeded; +} + + + +nsresult +XULDocument::OverlayForwardReference::Merge(nsIContent* aTargetNode, + nsIContent* aOverlayNode, + bool 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. + uint32_t 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(nsGkAtoms::id)) + continue; + + // In certain cases merging command or observes is unsafe, so don't. + if (!aNotify) { + if (aTargetNode->NodeInfo()->Equals(nsGkAtoms::observes, + kNameSpaceID_XUL)) + continue; + + if (name->Equals(nsGkAtoms::observes) && + aTargetNode->HasAttr(kNameSpaceID_None, nsGkAtoms::observes)) + continue; + + if (name->Equals(nsGkAtoms::command) && + aTargetNode->HasAttr(kNameSpaceID_None, nsGkAtoms::command) && + !aTargetNode->NodeInfo()->Equals(nsGkAtoms::key, + kNameSpaceID_XUL) && + !aTargetNode->NodeInfo()->Equals(nsGkAtoms::menuitem, + kNameSpaceID_XUL)) + continue; + } + + int32_t 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 == nsGkAtoms::removeelement && + value.EqualsLiteral("true")) { + + nsCOMPtr<nsINode> parent = aTargetNode->GetParentNode(); + if (!parent) return NS_ERROR_FAILURE; + rv = RemoveElement(parent, aTargetNode); + if (NS_FAILED(rv)) return rv; + + return NS_OK; + } + + rv = aTargetNode->SetAttr(nameSpaceID, attr, prefix, value, aNotify); + if (!NS_FAILED(rv) && !aNotify) + rv = mDocument->BroadcastAttributeChangeFromOverlay(aTargetNode, + nameSpaceID, + attr, prefix, + value); + 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. + + uint32_t 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<nsIContent> currContent; + + for (i = 0; i < childCount; ++i) { + currContent = aOverlayNode->GetFirstChild(); + + nsIAtom *idAtom = currContent->GetID(); + + nsIContent *elementInDocument = nullptr; + if (idAtom) { + nsDependentAtomString id(idAtom); + + if (!id.IsEmpty()) { + nsIDocument *doc = aTargetNode->GetUncomposedDoc(); + //XXXsmaug should we use ShadowRoot::GetElementById() + // if doc is null? + if (!doc) return NS_ERROR_FAILURE; + + elementInDocument = doc->GetElementById(id); + } + } + + // 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 (elementInDocument) { + // 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. + + nsIContent *elementParent = elementInDocument->GetParent(); + + nsIAtom *parentID = elementParent->GetID(); + if (parentID && + aTargetNode->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id, + nsDependentAtomString(parentID), + eCaseMatters)) { + // The element matches. "Go Deep!" + rv = Merge(elementInDocument, currContent, aNotify); + if (NS_FAILED(rv)) return rv; + aOverlayNode->RemoveChildAt(0, false); + + continue; + } + } + + aOverlayNode->RemoveChildAt(0, false); + + rv = InsertElement(aTargetNode, currContent, aNotify); + if (NS_FAILED(rv)) return rv; + } + + return NS_OK; +} + + + +XULDocument::OverlayForwardReference::~OverlayForwardReference() +{ + if (MOZ_LOG_TEST(gXULLog, LogLevel::Warning) && !mResolved) { + nsAutoString id; + mOverlay->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id); + + nsAutoCString idC; + idC.AssignWithConversion(id); + + nsIURI *protoURI = mDocument->mCurrentPrototype->GetURI(); + + nsCOMPtr<nsIURI> docURI; + mDocument->mChannel->GetOriginalURI(getter_AddRefs(docURI)); + + MOZ_LOG(gXULLog, LogLevel::Warning, + ("xul: %s overlay failed to resolve '%s' in %s", + protoURI->GetSpecOrDefault().get(), idC.get(), + docURI ? docURI->GetSpecOrDefault().get() : "")); + } +} + + +//---------------------------------------------------------------------- +// +// XULDocument::BroadcasterHookup +// + +nsForwardReference::Result +XULDocument::BroadcasterHookup::Resolve() +{ + nsresult rv; + + bool listener; + rv = mDocument->CheckBroadcasterHookup(mObservesElement, &listener, &mResolved); + if (NS_FAILED(rv)) return eResolve_Error; + + return mResolved ? eResolve_Succeeded : eResolve_Later; +} + + +XULDocument::BroadcasterHookup::~BroadcasterHookup() +{ + if (MOZ_LOG_TEST(gXULLog, LogLevel::Warning) && !mResolved) { + // Tell the world we failed + + nsAutoString broadcasterID; + nsAutoString attribute; + + if (mObservesElement->IsXULElement(nsGkAtoms::observes)) { + mObservesElement->GetAttr(kNameSpaceID_None, nsGkAtoms::element, broadcasterID); + mObservesElement->GetAttr(kNameSpaceID_None, nsGkAtoms::attribute, attribute); + } + else { + mObservesElement->GetAttr(kNameSpaceID_None, nsGkAtoms::observes, broadcasterID); + attribute.Assign('*'); + } + + nsAutoCString attributeC,broadcasteridC; + attributeC.AssignWithConversion(attribute); + broadcasteridC.AssignWithConversion(broadcasterID); + MOZ_LOG(gXULLog, LogLevel::Warning, + ("xul: broadcaster hookup failed <%s attribute='%s'> to %s", + nsAtomCString(mObservesElement->NodeInfo()->NameAtom()).get(), + attributeC.get(), + broadcasteridC.get())); + } +} + + +//---------------------------------------------------------------------- +// +// XULDocument::TemplateBuilderHookup +// + +nsForwardReference::Result +XULDocument::TemplateBuilderHookup::Resolve() +{ + bool 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 +XULDocument::BroadcastAttributeChangeFromOverlay(nsIContent* aNode, + int32_t aNameSpaceID, + nsIAtom* aAttribute, + nsIAtom* aPrefix, + const nsAString& aValue) +{ + nsresult rv = NS_OK; + + if (!mBroadcasterMap || !CanBroadcast(aNameSpaceID, aAttribute)) + return rv; + + if (!aNode->IsElement()) + return rv; + + auto entry = static_cast<BroadcasterMapEntry*> + (mBroadcasterMap->Search(aNode->AsElement())); + if (!entry) + return rv; + + // We've got listeners: push the value. + for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) { + BroadcastListener* bl = entry->mListeners[i]; + + if ((bl->mAttribute != aAttribute) && + (bl->mAttribute != nsGkAtoms::_asterisk)) + continue; + + nsCOMPtr<nsIContent> l = do_QueryReferent(bl->mListener); + if (l) { + rv = l->SetAttr(aNameSpaceID, aAttribute, + aPrefix, aValue, false); + if (NS_FAILED(rv)) return rv; + } + } + return rv; +} + +nsresult +XULDocument::FindBroadcaster(Element* aElement, + Element** aListener, + nsString& aBroadcasterID, + nsString& aAttribute, + Element** aBroadcaster) +{ + mozilla::dom::NodeInfo *ni = aElement->NodeInfo(); + *aListener = nullptr; + *aBroadcaster = nullptr; + + if (ni->Equals(nsGkAtoms::observes, kNameSpaceID_XUL)) { + // It's an <observes> 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 (!parent) { + // <observes> is the root element + return NS_FINDBROADCASTER_NOT_FOUND; + } + + // 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(nsGkAtoms::overlay, + kNameSpaceID_XUL)) { + return NS_FINDBROADCASTER_AWAIT_OVERLAYS; + } + + *aListener = parent->IsElement() ? parent->AsElement() : nullptr; + NS_IF_ADDREF(*aListener); + + aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::element, aBroadcasterID); + if (aBroadcasterID.IsEmpty()) { + return NS_FINDBROADCASTER_NOT_FOUND; + } + aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::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, nsGkAtoms::observes, aBroadcasterID); + + // Bail if there's no aBroadcasterID + if (aBroadcasterID.IsEmpty()) { + // Try the command attribute next. + aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::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(nsGkAtoms::menuitem, kNameSpaceID_XUL) || + ni->Equals(nsGkAtoms::key, kNameSpaceID_XUL)) { + return NS_FINDBROADCASTER_NOT_FOUND; + } + } + else { + return NS_FINDBROADCASTER_NOT_FOUND; + } + } + + *aListener = aElement; + NS_ADDREF(*aListener); + + aAttribute.Assign('*'); + } + + // Make sure we got a valid listener. + NS_ENSURE_TRUE(*aListener, NS_ERROR_UNEXPECTED); + + // Try to find the broadcaster element in the document. + *aBroadcaster = GetElementById(aBroadcasterID); + + // 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; + } + + NS_ADDREF(*aBroadcaster); + + return NS_FINDBROADCASTER_FOUND; +} + +nsresult +XULDocument::CheckBroadcasterHookup(Element* aElement, + bool* aNeedsHookup, + bool* aDidResolve) +{ + // Resolve a broadcaster hookup. Look at the element that we're + // trying to resolve: it could be an '<observes>' element, or just + // a vanilla element with an 'observes' attribute on it. + nsresult rv; + + *aDidResolve = false; + + nsCOMPtr<Element> listener; + nsAutoString broadcasterID; + nsAutoString attribute; + nsCOMPtr<Element> broadcaster; + + rv = FindBroadcaster(aElement, getter_AddRefs(listener), + broadcasterID, attribute, getter_AddRefs(broadcaster)); + switch (rv) { + case NS_FINDBROADCASTER_NOT_FOUND: + *aNeedsHookup = false; + return NS_OK; + case NS_FINDBROADCASTER_AWAIT_OVERLAYS: + *aNeedsHookup = true; + return NS_OK; + case NS_FINDBROADCASTER_FOUND: + break; + default: + return rv; + } + + NS_ENSURE_ARG(broadcaster && listener); + ErrorResult domRv; + AddBroadcastListenerFor(*broadcaster, *listener, attribute, domRv); + if (domRv.Failed()) { + return domRv.StealNSResult(); + } + + // Tell the world we succeeded + if (MOZ_LOG_TEST(gXULLog, LogLevel::Debug)) { + nsCOMPtr<nsIContent> content = + do_QueryInterface(listener); + + NS_ASSERTION(content != nullptr, "not an nsIContent"); + if (! content) + return rv; + + nsAutoCString attributeC,broadcasteridC; + attributeC.AssignWithConversion(attribute); + broadcasteridC.AssignWithConversion(broadcasterID); + MOZ_LOG(gXULLog, LogLevel::Debug, + ("xul: broadcaster hookup <%s attribute='%s'> to %s", + nsAtomCString(content->NodeInfo()->NameAtom()).get(), + attributeC.get(), + broadcasteridC.get())); + } + + *aNeedsHookup = false; + *aDidResolve = true; + return NS_OK; +} + +nsresult +XULDocument::InsertElement(nsINode* aParent, nsIContent* aChild, + bool aNotify) +{ + // Insert aChild appropriately into aParent, accounting for a + // 'pos' attribute set on aChild. + + nsAutoString posStr; + bool wasInserted = false; + + // insert after an element of a given id + aChild->GetAttr(kNameSpaceID_None, nsGkAtoms::insertafter, posStr); + bool isInsertAfter = true; + + if (posStr.IsEmpty()) { + aChild->GetAttr(kNameSpaceID_None, nsGkAtoms::insertbefore, posStr); + isInsertAfter = false; + } + + if (!posStr.IsEmpty()) { + nsIDocument *document = aParent->OwnerDoc(); + + nsIContent *content = nullptr; + + char* str = ToNewCString(posStr); + char* rest; + char* token = nsCRT::strtok(str, ", ", &rest); + + while (token) { + content = document->GetElementById(NS_ConvertASCIItoUTF16(token)); + if (content) + break; + + token = nsCRT::strtok(rest, ", ", &rest); + } + free(str); + + if (content) { + int32_t pos = aParent->IndexOf(content); + + if (pos != -1) { + pos = isInsertAfter ? pos + 1 : pos; + nsresult rv = aParent->InsertChildAt(aChild, pos, aNotify); + if (NS_FAILED(rv)) + return rv; + + wasInserted = true; + } + } + } + + if (!wasInserted) { + + aChild->GetAttr(kNameSpaceID_None, nsGkAtoms::position, posStr); + if (!posStr.IsEmpty()) { + nsresult rv; + // Positions are one-indexed. + int32_t pos = posStr.ToInteger(&rv); + // Note: if the insertion index (which is |pos - 1|) would be less + // than 0 or greater than the number of children aParent has, then + // don't insert, since the position is bogus. Just skip on to + // appending. + if (NS_SUCCEEDED(rv) && pos > 0 && + uint32_t(pos - 1) <= aParent->GetChildCount()) { + rv = aParent->InsertChildAt(aChild, pos - 1, aNotify); + if (NS_SUCCEEDED(rv)) + wasInserted = true; + // If the insertion fails, then we should still + // attempt an append. Thus, rather than returning rv + // immediately, we fall through to the final + // "catch-all" case that just does an AppendChildTo. + } + } + } + + if (!wasInserted) { + return aParent->AppendChildTo(aChild, aNotify); + } + return NS_OK; +} + +nsresult +XULDocument::RemoveElement(nsINode* aParent, nsINode* aChild) +{ + int32_t nodeOffset = aParent->IndexOf(aChild); + + aParent->RemoveChildAt(nodeOffset, true); + return NS_OK; +} + +//---------------------------------------------------------------------- +// +// CachedChromeStreamListener +// + +XULDocument::CachedChromeStreamListener::CachedChromeStreamListener(XULDocument* aDocument, bool aProtoLoaded) + : mDocument(aDocument), + mProtoLoaded(aProtoLoaded) +{ +} + + +XULDocument::CachedChromeStreamListener::~CachedChromeStreamListener() +{ +} + + +NS_IMPL_ISUPPORTS(XULDocument::CachedChromeStreamListener, + nsIRequestObserver, nsIStreamListener) + +NS_IMETHODIMP +XULDocument::CachedChromeStreamListener::OnStartRequest(nsIRequest *request, + nsISupports* acontext) +{ + return NS_ERROR_PARSED_DATA_CACHED; +} + + +NS_IMETHODIMP +XULDocument::CachedChromeStreamListener::OnStopRequest(nsIRequest *request, + nsISupports* aContext, + nsresult aStatus) +{ + if (! mProtoLoaded) + return NS_OK; + + return mDocument->OnPrototypeLoadDone(true); +} + + +NS_IMETHODIMP +XULDocument::CachedChromeStreamListener::OnDataAvailable(nsIRequest *request, + nsISupports* aContext, + nsIInputStream* aInStr, + uint64_t aSourceOffset, + uint32_t aCount) +{ + NS_NOTREACHED("CachedChromeStream doesn't receive data"); + return NS_ERROR_UNEXPECTED; +} + +//---------------------------------------------------------------------- +// +// ParserObserver +// + +XULDocument::ParserObserver::ParserObserver(XULDocument* aDocument, + nsXULPrototypeDocument* aPrototype) + : mDocument(aDocument), mPrototype(aPrototype) +{ +} + +XULDocument::ParserObserver::~ParserObserver() +{ +} + +NS_IMPL_ISUPPORTS(XULDocument::ParserObserver, nsIRequestObserver) + +NS_IMETHODIMP +XULDocument::ParserObserver::OnStartRequest(nsIRequest *request, + nsISupports* aContext) +{ + // Guard against buggy channels calling OnStartRequest multiple times. + if (mPrototype) { + nsCOMPtr<nsIChannel> channel = do_QueryInterface(request); + nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); + if (channel && secMan) { + nsCOMPtr<nsIPrincipal> principal; + secMan->GetChannelResultPrincipal(channel, getter_AddRefs(principal)); + + // Failure there is ok -- it'll just set a (safe) null principal + mPrototype->SetDocumentPrincipal(principal); + } + + // Make sure to avoid cycles + mPrototype = nullptr; + } + + return NS_OK; +} + +NS_IMETHODIMP +XULDocument::ParserObserver::OnStopRequest(nsIRequest *request, + nsISupports* aContext, + nsresult aStatus) +{ + nsresult rv = NS_OK; + + if (NS_FAILED(aStatus)) { + // If an overlay load fails, we need to nudge the prototype + // walk along. + nsCOMPtr<nsIChannel> aChannel = do_QueryInterface(request); + if (aChannel) { + nsCOMPtr<nsIURI> uri; + aChannel->GetOriginalURI(getter_AddRefs(uri)); + if (uri) { + mDocument->ReportMissingOverlay(uri); + } + } + + rv = mDocument->ResumeWalk(); + } + + // Drop the reference to the document to break cycle between the + // document, the parser, the content sink, and the parser + // observer. + mDocument = nullptr; + + return rv; +} + +already_AddRefed<nsPIWindowRoot> +XULDocument::GetWindowRoot() +{ + if (!mDocumentContainer) { + return nullptr; + } + + nsCOMPtr<nsPIDOMWindowOuter> piWin = mDocumentContainer->GetWindow(); + return piWin ? piWin->GetTopWindowRoot() : nullptr; +} + +bool +XULDocument::IsDocumentRightToLeft() +{ + // setting the localedir attribute on the root element forces a + // specific direction for the document. + Element* element = GetRootElement(); + if (element) { + static nsIContent::AttrValuesArray strings[] = + {&nsGkAtoms::ltr, &nsGkAtoms::rtl, nullptr}; + switch (element->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::localedir, + strings, eCaseMatters)) { + case 0: return false; + case 1: return true; + default: break; // otherwise, not a valid value, so fall through + } + } + + // otherwise, get the locale from the chrome registry and + // look up the intl.uidirection.<locale> preference + nsCOMPtr<nsIXULChromeRegistry> reg = + mozilla::services::GetXULChromeRegistryService(); + if (!reg) + return false; + + nsAutoCString package; + bool isChrome; + if (NS_SUCCEEDED(mDocumentURI->SchemeIs("chrome", &isChrome)) && + isChrome) { + mDocumentURI->GetHostPort(package); + } + else { + // use the 'global' package for about and resource uris. + // otherwise, just default to left-to-right. + bool isAbout, isResource; + if (NS_SUCCEEDED(mDocumentURI->SchemeIs("about", &isAbout)) && + isAbout) { + package.AssignLiteral("global"); + } + else if (NS_SUCCEEDED(mDocumentURI->SchemeIs("resource", &isResource)) && + isResource) { + package.AssignLiteral("global"); + } + else { + return false; + } + } + + bool isRTL = false; + reg->IsLocaleRTL(package, &isRTL); + return isRTL; +} + +void +XULDocument::ResetDocumentDirection() +{ + DocumentStatesChanged(NS_DOCUMENT_STATE_RTL_LOCALE); +} + +void +XULDocument::DirectionChanged(const char* aPrefName, void* aData) +{ + // Reset the direction and restyle the document if necessary. + XULDocument* doc = (XULDocument *)aData; + if (doc) { + doc->ResetDocumentDirection(); + } +} + +int +XULDocument::GetDocumentLWTheme() +{ + if (mDocLWTheme == Doc_Theme_Uninitialized) { + mDocLWTheme = Doc_Theme_None; // No lightweight theme by default + + Element* element = GetRootElement(); + nsAutoString hasLWTheme; + if (element && + element->GetAttr(kNameSpaceID_None, nsGkAtoms::lwtheme, hasLWTheme) && + !(hasLWTheme.IsEmpty()) && + hasLWTheme.EqualsLiteral("true")) { + mDocLWTheme = Doc_Theme_Neutral; + nsAutoString lwTheme; + element->GetAttr(kNameSpaceID_None, nsGkAtoms::lwthemetextcolor, lwTheme); + if (!(lwTheme.IsEmpty())) { + if (lwTheme.EqualsLiteral("dark")) + mDocLWTheme = Doc_Theme_Dark; + else if (lwTheme.EqualsLiteral("bright")) + mDocLWTheme = Doc_Theme_Bright; + } + } + } + return mDocLWTheme; +} + +NS_IMETHODIMP +XULDocument::GetBoxObjectFor(nsIDOMElement* aElement, nsIBoxObject** aResult) +{ + ErrorResult rv; + nsCOMPtr<Element> el = do_QueryInterface(aElement); + *aResult = GetBoxObjectFor(el, rv).take(); + return rv.StealNSResult(); +} + +JSObject* +XULDocument::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return XULDocumentBinding::Wrap(aCx, this, aGivenProto); +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/xul/XULDocument.h b/dom/xul/XULDocument.h new file mode 100644 index 000000000..a2f82bb89 --- /dev/null +++ b/dom/xul/XULDocument.h @@ -0,0 +1,791 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_XULDocument_h +#define mozilla_dom_XULDocument_h + +#include "nsAutoPtr.h" +#include "nsCOMPtr.h" +#include "nsXULPrototypeDocument.h" +#include "nsXULPrototypeCache.h" +#include "nsTArray.h" + +#include "mozilla/dom/XMLDocument.h" +#include "mozilla/StyleSheet.h" +#include "nsForwardReference.h" +#include "nsIContent.h" +#include "nsIDOMXULCommandDispatcher.h" +#include "nsIDOMXULDocument.h" +#include "nsCOMArray.h" +#include "nsIURI.h" +#include "nsIXULDocument.h" +#include "nsScriptLoader.h" +#include "nsIStreamListener.h" +#include "nsIStreamLoader.h" +#include "nsICSSLoaderObserver.h" +#include "nsIXULStore.h" + +#include "mozilla/Attributes.h" + +#include "js/TracingAPI.h" +#include "js/TypeDecls.h" + +class nsIRDFResource; +class nsIRDFService; +class nsPIWindowRoot; +#if 0 // XXXbe save me, scc (need NSCAP_FORWARD_DECL(nsXULPrototypeScript)) +class nsIObjectInputStream; +class nsIObjectOutputStream; +#else +#include "nsIObjectInputStream.h" +#include "nsIObjectOutputStream.h" +#include "nsXULElement.h" +#endif +#include "nsURIHashKey.h" +#include "nsInterfaceHashtable.h" + +class nsRefMapEntry : public nsStringHashKey +{ +public: + explicit nsRefMapEntry(const nsAString& aKey) : + nsStringHashKey(&aKey) + { + } + explicit nsRefMapEntry(const nsAString* aKey) : + nsStringHashKey(aKey) + { + } + nsRefMapEntry(const nsRefMapEntry& aOther) : + nsStringHashKey(&aOther.GetKey()) + { + NS_ERROR("Should never be called"); + } + + mozilla::dom::Element* GetFirstElement(); + void AppendAll(nsCOMArray<nsIContent>* aElements); + /** + * @return true if aElement was added, false if we failed due to OOM + */ + bool AddElement(mozilla::dom::Element* aElement); + /** + * @return true if aElement was removed and it was the last content for + * this ref, so this entry should be removed from the map + */ + bool RemoveElement(mozilla::dom::Element* aElement); + +private: + nsTArray<mozilla::dom::Element*> mRefContentList; +}; + +/** + * The XUL document class + */ + +namespace mozilla { +namespace dom { + +class XULDocument final : public XMLDocument, + public nsIXULDocument, + public nsIDOMXULDocument, + public nsIStreamLoaderObserver, + public nsICSSLoaderObserver, + public nsIOffThreadScriptReceiver +{ +public: + XULDocument(); + + // nsISupports interface + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSISTREAMLOADEROBSERVER + + // nsIDocument interface + virtual void Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup) override; + virtual void ResetToURI(nsIURI *aURI, nsILoadGroup* aLoadGroup, + nsIPrincipal* aPrincipal) override; + + virtual nsresult StartDocumentLoad(const char* aCommand, + nsIChannel *channel, + nsILoadGroup* aLoadGroup, + nsISupports* aContainer, + nsIStreamListener **aDocListener, + bool aReset = true, + nsIContentSink* aSink = nullptr) override; + + virtual void SetContentType(const nsAString& aContentType) override; + + virtual void EndLoad() override; + + // nsIMutationObserver interface + NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED + NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED + NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED + NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED + NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE + + // nsIXULDocument interface + virtual void GetElementsForID(const nsAString& aID, + nsCOMArray<nsIContent>& aElements) override; + + NS_IMETHOD AddSubtreeToDocument(nsIContent* aContent) override; + NS_IMETHOD RemoveSubtreeFromDocument(nsIContent* aContent) override; + NS_IMETHOD SetTemplateBuilderFor(nsIContent* aContent, + nsIXULTemplateBuilder* aBuilder) override; + NS_IMETHOD GetTemplateBuilderFor(nsIContent* aContent, + nsIXULTemplateBuilder** aResult) override; + NS_IMETHOD OnPrototypeLoadDone(bool aResumeWalk) override; + bool OnDocumentParserError() override; + + // nsINode interface overrides + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + + // nsIDOMNode interface + NS_FORWARD_NSIDOMNODE_TO_NSINODE + + // nsIDOMDocument interface + using nsDocument::CreateElement; + using nsDocument::CreateElementNS; + NS_FORWARD_NSIDOMDOCUMENT(XMLDocument::) + // And explicitly import the things from nsDocument that we just shadowed + using nsDocument::GetImplementation; + using nsDocument::GetTitle; + using nsDocument::SetTitle; + using nsDocument::GetLastStyleSheetSet; + using nsDocument::MozSetImageElement; + using nsDocument::GetMozFullScreenElement; + using nsIDocument::GetLocation; + + // nsDocument interface overrides + virtual Element* GetElementById(const nsAString & elementId) override; + + // nsIDOMXULDocument interface + NS_DECL_NSIDOMXULDOCUMENT + + // nsICSSLoaderObserver + NS_IMETHOD StyleSheetLoaded(mozilla::StyleSheet* aSheet, + bool aWasAlternate, + nsresult aStatus) override; + + virtual void EndUpdate(nsUpdateType aUpdateType) override; + + virtual bool IsDocumentRightToLeft() override; + + virtual void ResetDocumentDirection() override; + + virtual int GetDocumentLWTheme() override; + + virtual void ResetDocumentLWTheme() override { mDocLWTheme = Doc_Theme_Uninitialized; } + + NS_IMETHOD OnScriptCompileComplete(JSScript* aScript, nsresult aStatus) override; + + static bool + MatchAttribute(nsIContent* aContent, + int32_t aNameSpaceID, + nsIAtom* aAttrName, + void* aData); + + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XULDocument, XMLDocument) + + void TraceProtos(JSTracer* aTrc, uint32_t aGCNumber); + + // WebIDL API + already_AddRefed<nsINode> GetPopupNode(); + void SetPopupNode(nsINode* aNode); + already_AddRefed<nsINode> GetPopupRangeParent(ErrorResult& aRv); + int32_t GetPopupRangeOffset(ErrorResult& aRv); + already_AddRefed<nsINode> GetTooltipNode(); + void SetTooltipNode(nsINode* aNode) { /* do nothing */ } + nsIDOMXULCommandDispatcher* GetCommandDispatcher() const + { + return mCommandDispatcher; + } + int32_t GetWidth(ErrorResult& aRv); + int32_t GetHeight(ErrorResult& aRv); + already_AddRefed<nsINodeList> + GetElementsByAttribute(const nsAString& aAttribute, + const nsAString& aValue); + already_AddRefed<nsINodeList> + GetElementsByAttributeNS(const nsAString& aNamespaceURI, + const nsAString& aAttribute, + const nsAString& aValue, + ErrorResult& aRv); + void AddBroadcastListenerFor(Element& aBroadcaster, Element& aListener, + const nsAString& aAttr, ErrorResult& aRv); + void RemoveBroadcastListenerFor(Element& aBroadcaster, Element& aListener, + const nsAString& aAttr); + void Persist(const nsAString& aId, const nsAString& aAttr, ErrorResult& aRv) + { + aRv = Persist(aId, aAttr); + } + using nsDocument::GetBoxObjectFor; + void LoadOverlay(const nsAString& aURL, nsIObserver* aObserver, + ErrorResult& aRv) + { + aRv = LoadOverlay(aURL, aObserver); + } + +protected: + virtual ~XULDocument(); + + // Implementation methods + friend nsresult + (::NS_NewXULDocument(nsIXULDocument** aResult)); + + nsresult Init(void) override; + nsresult StartLayout(void); + + nsresult + AddElementToRefMap(Element* aElement); + void + RemoveElementFromRefMap(Element* aElement); + + nsresult GetViewportSize(int32_t* aWidth, int32_t* aHeight); + + nsresult PrepareToLoad(nsISupports* aContainer, + const char* aCommand, + nsIChannel* aChannel, + nsILoadGroup* aLoadGroup, + nsIParser** aResult); + + nsresult + PrepareToLoadPrototype(nsIURI* aURI, + const char* aCommand, + nsIPrincipal* aDocumentPrincipal, + nsIParser** aResult); + + nsresult + LoadOverlayInternal(nsIURI* aURI, bool aIsDynamic, bool* aShouldReturn, + bool* aFailureFromContent); + + nsresult ApplyPersistentAttributes(); + nsresult ApplyPersistentAttributesInternal(); + nsresult ApplyPersistentAttributesToElements(const nsAString &aID, + nsCOMArray<nsIContent>& aElements); + + nsresult + AddElementToDocumentPre(Element* aElement); + + nsresult + AddElementToDocumentPost(Element* aElement); + + nsresult + ExecuteOnBroadcastHandlerFor(Element* aBroadcaster, + Element* aListener, + nsIAtom* aAttr); + + nsresult + BroadcastAttributeChangeFromOverlay(nsIContent* aNode, + int32_t aNameSpaceID, + nsIAtom* aAttribute, + nsIAtom* aPrefix, + const nsAString& aValue); + + already_AddRefed<nsPIWindowRoot> GetWindowRoot(); + + static void DirectionChanged(const char* aPrefName, void* aData); + + // pseudo constants + static int32_t gRefCnt; + + static nsIAtom** kIdentityAttrs[]; + + static nsIRDFService* gRDFService; + static nsIRDFResource* kNC_persist; + static nsIRDFResource* kNC_attribute; + static nsIRDFResource* kNC_value; + + static LazyLogModule gXULLog; + + nsresult + Persist(nsIContent* aElement, int32_t aNameSpaceID, nsIAtom* aAttribute); + // Just like Persist but ignores the return value so we can use it + // as a runnable method. + void DoPersist(nsIContent* aElement, int32_t aNameSpaceID, nsIAtom* aAttribute) + { + Persist(aElement, aNameSpaceID, aAttribute); + } + + virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override; + + // IMPORTANT: The ownership implicit in the following member + // variables has been explicitly checked and set using nsCOMPtr + // for owning pointers and raw COM interface pointers for weak + // (ie, non owning) references. If you add any members to this + // class, please make the ownership explicit (pinkerton, scc). + // NOTE, THIS IS STILL IN PROGRESS, TALK TO PINK OR SCC BEFORE + // CHANGING + + XULDocument* mNextSrcLoadWaiter; // [OWNER] but not COMPtr + + // Tracks elements with a 'ref' attribute, or an 'id' attribute where + // the element's namespace has no registered ID attribute name. + nsTHashtable<nsRefMapEntry> mRefMap; + nsCOMPtr<nsIXULStore> mLocalStore; + bool mApplyingPersistedAttrs; + bool mIsWritingFastLoad; + bool mDocumentLoaded; + /** + * Since ResumeWalk is interruptible, it's possible that last + * stylesheet finishes loading while the PD walk is still in + * progress (waiting for an overlay to finish loading). + * mStillWalking prevents DoneLoading (and StartLayout) from being + * called in this situation. + */ + bool mStillWalking; + + /** + * These two values control where persistent attributes get applied. + */ + bool mRestrictPersistence; + nsTHashtable<nsStringHashKey> mPersistenceIds; + + /** + * An array of style sheets, that will be added (preserving order) to the + * document after all of them are loaded (in DoneWalking). + */ + nsTArray<RefPtr<StyleSheet>> mOverlaySheets; + + nsCOMPtr<nsIDOMXULCommandDispatcher> mCommandDispatcher; // [OWNER] of the focus tracker + + // Maintains the template builders that have been attached to + // content elements + typedef nsInterfaceHashtable<nsISupportsHashKey, nsIXULTemplateBuilder> + BuilderTable; + BuilderTable* mTemplateBuilderTable; + + uint32_t mPendingSheets; + + /** + * document lightweight theme for use with :-moz-lwtheme, :-moz-lwtheme-brighttext + * and :-moz-lwtheme-darktext + */ + DocumentTheme mDocLWTheme; + + /** + * Context stack, which maintains the state of the Builder and allows + * it to be interrupted. + */ + class ContextStack { + protected: + struct Entry { + nsXULPrototypeElement* mPrototype; + nsIContent* mElement; + int32_t mIndex; + Entry* mNext; + }; + + Entry* mTop; + int32_t mDepth; + + public: + ContextStack(); + ~ContextStack(); + + int32_t Depth() { return mDepth; } + + nsresult Push(nsXULPrototypeElement* aPrototype, nsIContent* aElement); + nsresult Pop(); + nsresult Peek(nsXULPrototypeElement** aPrototype, nsIContent** aElement, int32_t* aIndex); + + nsresult SetTopIndex(int32_t aIndex); + }; + + friend class ContextStack; + ContextStack mContextStack; + + enum State { eState_Master, eState_Overlay }; + State mState; + + /** + * An array of overlay nsIURIs that have yet to be resolved. The + * order of the array is significant: overlays at the _end_ of the + * array are resolved before overlays earlier in the array (i.e., + * it is a stack). + * + * In the current implementation the order the overlays are loaded + * in is as follows: first overlays from xul-overlay PIs, in the + * same order as in the document, then the overlays from the chrome + * registry. + */ + nsTArray<nsCOMPtr<nsIURI> > mUnloadedOverlays; + + /** + * Load the transcluded script at the specified URI. If the + * prototype construction must 'block' until the load has + * completed, aBlock will be set to true. + */ + nsresult LoadScript(nsXULPrototypeScript *aScriptProto, bool* aBlock); + + /** + * Execute the precompiled script object scoped by this XUL document's + * containing window object. + */ + nsresult ExecuteScript(nsXULPrototypeScript *aScript); + + /** + * Create a delegate content model element from a prototype. + * Note that the resulting content node is not bound to any tree + */ + nsresult CreateElementFromPrototype(nsXULPrototypeElement* aPrototype, + Element** aResult, + bool aIsRoot); + + /** + * Create a hook-up element to which content nodes can be attached for + * later resolution. + */ + nsresult CreateOverlayElement(nsXULPrototypeElement* aPrototype, + Element** aResult); + + /** + * Add attributes from the prototype to the element. + */ + nsresult AddAttributes(nsXULPrototypeElement* aPrototype, nsIContent* aElement); + + /** + * The prototype-script of the current transcluded script that is being + * loaded. For document.write('<script src="nestedwrite.js"><\/script>') + * to work, these need to be in a stack element type, and we need to hold + * the top of stack here. + */ + nsXULPrototypeScript* mCurrentScriptProto; + + /** + * Whether the current transcluded script is being compiled off thread. + * The load event is blocked while this is in progress. + */ + bool mOffThreadCompiling; + + /** + * If the current transcluded script is being compiled off thread, the + * source for that script. + */ + char16_t* mOffThreadCompileStringBuf; + size_t mOffThreadCompileStringLength; + + /** + * Check if a XUL template builder has already been hooked up. + */ + static nsresult + CheckTemplateBuilderHookup(nsIContent* aElement, bool* aNeedsHookup); + + /** + * Create a XUL template builder on the specified node. + */ + static nsresult + CreateTemplateBuilder(nsIContent* aElement); + + /** + * Add the current prototype's style sheets (currently it's just + * style overlays from the chrome registry) to the document. + */ + nsresult AddPrototypeSheets(); + + +protected: + /* Declarations related to forward references. + * + * Forward references are declarations which are added to the temporary + * list (mForwardReferences) during the document (or overlay) load and + * are resolved later, when the document loading is almost complete. + */ + + /** + * The list of different types of forward references to resolve. After + * a reference is resolved, it is removed from this array (and + * automatically deleted) + */ + nsTArray<nsAutoPtr<nsForwardReference> > mForwardReferences; + + /** Indicates what kind of forward references are still to be processed. */ + nsForwardReference::Phase mResolutionPhase; + + /** + * Adds aRef to the mForwardReferences array. Takes the ownership of aRef. + */ + nsresult AddForwardReference(nsForwardReference* aRef); + + /** + * Resolve all of the document's forward references. + */ + nsresult ResolveForwardReferences(); + + /** + * Used to resolve broadcaster references + */ + class BroadcasterHookup : public nsForwardReference + { + protected: + XULDocument* mDocument; // [WEAK] + RefPtr<Element> mObservesElement; // [OWNER] + bool mResolved; + + public: + BroadcasterHookup(XULDocument* aDocument, + Element* aObservesElement) + : mDocument(aDocument), + mObservesElement(aObservesElement), + mResolved(false) + { + } + + virtual ~BroadcasterHookup(); + + virtual Phase GetPhase() override { return eHookup; } + virtual Result Resolve() override; + }; + + friend class BroadcasterHookup; + + + /** + * Used to hook up overlays + */ + class OverlayForwardReference : public nsForwardReference + { + protected: + XULDocument* mDocument; // [WEAK] + nsCOMPtr<nsIContent> mOverlay; // [OWNER] + bool mResolved; + + nsresult Merge(nsIContent* aTargetNode, nsIContent* aOverlayNode, bool aNotify); + + public: + OverlayForwardReference(XULDocument* aDocument, nsIContent* aOverlay) + : mDocument(aDocument), mOverlay(aOverlay), mResolved(false) {} + + virtual ~OverlayForwardReference(); + + virtual Phase GetPhase() override { return eConstruction; } + virtual Result Resolve() override; + }; + + friend class OverlayForwardReference; + + class TemplateBuilderHookup : public nsForwardReference + { + protected: + nsCOMPtr<nsIContent> mElement; // [OWNER] + + public: + explicit TemplateBuilderHookup(nsIContent* aElement) + : mElement(aElement) {} + + virtual Phase GetPhase() override { return eHookup; } + virtual Result Resolve() override; + }; + + friend class TemplateBuilderHookup; + + // The out params of FindBroadcaster only have values that make sense when + // the method returns NS_FINDBROADCASTER_FOUND. In all other cases, the + // values of the out params should not be relied on (though *aListener and + // *aBroadcaster do need to be released if non-null, of course). + nsresult + FindBroadcaster(Element* aElement, + Element** aListener, + nsString& aBroadcasterID, + nsString& aAttribute, + Element** aBroadcaster); + + nsresult + CheckBroadcasterHookup(Element* aElement, + bool* aNeedsHookup, + bool* aDidResolve); + + void + SynchronizeBroadcastListener(Element *aBroadcaster, + Element *aListener, + const nsAString &aAttr); + + static + nsresult + InsertElement(nsINode* aParent, nsIContent* aChild, bool aNotify); + + static + nsresult + RemoveElement(nsINode* aParent, nsINode* aChild); + + /** + * The current prototype that we are walking to construct the + * content model. + */ + RefPtr<nsXULPrototypeDocument> mCurrentPrototype; + + /** + * The master document (outermost, .xul) prototype, from which + * all subdocuments get their security principals. + */ + RefPtr<nsXULPrototypeDocument> mMasterPrototype; + + /** + * Owning references to all of the prototype documents that were + * used to construct this document. + */ + nsTArray< RefPtr<nsXULPrototypeDocument> > mPrototypes; + + /** + * Prepare to walk the current prototype. + */ + nsresult PrepareToWalk(); + + /** + * Creates a processing instruction based on aProtoPI and inserts + * it to the DOM (as the aIndex-th child of aParent). + */ + nsresult + CreateAndInsertPI(const nsXULPrototypePI* aProtoPI, + nsINode* aParent, uint32_t aIndex); + + /** + * Inserts the passed <?xml-stylesheet ?> PI at the specified + * index. Loads and applies the associated stylesheet + * asynchronously. + * The prototype document walk can happen before the stylesheets + * are loaded, but the final steps in the load process (see + * DoneWalking()) are not run before all the stylesheets are done + * loading. + */ + nsresult + InsertXMLStylesheetPI(const nsXULPrototypePI* aProtoPI, + nsINode* aParent, + uint32_t aIndex, + nsIContent* aPINode); + + /** + * Inserts the passed <?xul-overlay ?> PI at the specified index. + * Schedules the referenced overlay URI for further processing. + */ + nsresult + InsertXULOverlayPI(const nsXULPrototypePI* aProtoPI, + nsINode* aParent, + uint32_t aIndex, + nsIContent* aPINode); + + /** + * Add overlays from the chrome registry to the set of unprocessed + * overlays still to do. + */ + nsresult AddChromeOverlays(); + + /** + * Resume (or initiate) an interrupted (or newly prepared) + * prototype walk. + */ + nsresult ResumeWalk(); + + /** + * Called at the end of ResumeWalk() and from StyleSheetLoaded(). + * Expects that both the prototype document walk is complete and + * all referenced stylesheets finished loading. + */ + nsresult DoneWalking(); + + /** + * Report that an overlay failed to load + * @param aURI the URI of the overlay that failed to load + */ + void ReportMissingOverlay(nsIURI* aURI); + + class CachedChromeStreamListener : public nsIStreamListener { + protected: + RefPtr<XULDocument> mDocument; + bool mProtoLoaded; + + virtual ~CachedChromeStreamListener(); + + public: + CachedChromeStreamListener(XULDocument* aDocument, + bool aProtoLoaded); + + NS_DECL_ISUPPORTS + NS_DECL_NSIREQUESTOBSERVER + NS_DECL_NSISTREAMLISTENER + }; + + friend class CachedChromeStreamListener; + + + class ParserObserver : public nsIRequestObserver { + protected: + RefPtr<XULDocument> mDocument; + RefPtr<nsXULPrototypeDocument> mPrototype; + virtual ~ParserObserver(); + + public: + ParserObserver(XULDocument* aDocument, + nsXULPrototypeDocument* aPrototype); + + NS_DECL_ISUPPORTS + NS_DECL_NSIREQUESTOBSERVER + }; + + friend class ParserObserver; + + /** + * A map from a broadcaster element to a list of listener elements. + */ + PLDHashTable* mBroadcasterMap; + + nsAutoPtr<nsInterfaceHashtable<nsURIHashKey,nsIObserver> > mOverlayLoadObservers; + nsAutoPtr<nsInterfaceHashtable<nsURIHashKey,nsIObserver> > mPendingOverlayLoadNotifications; + + bool mInitialLayoutComplete; + + class nsDelayedBroadcastUpdate + { + public: + nsDelayedBroadcastUpdate(Element* aBroadcaster, + Element* aListener, + const nsAString &aAttr) + : mBroadcaster(aBroadcaster), mListener(aListener), mAttr(aAttr), + mSetAttr(false), mNeedsAttrChange(false) {} + + nsDelayedBroadcastUpdate(Element* aBroadcaster, + Element* aListener, + nsIAtom* aAttrName, + const nsAString &aAttr, + bool aSetAttr, + bool aNeedsAttrChange) + : mBroadcaster(aBroadcaster), mListener(aListener), mAttr(aAttr), + mAttrName(aAttrName), mSetAttr(aSetAttr), + mNeedsAttrChange(aNeedsAttrChange) {} + + nsDelayedBroadcastUpdate(const nsDelayedBroadcastUpdate& aOther) + : mBroadcaster(aOther.mBroadcaster), mListener(aOther.mListener), + mAttr(aOther.mAttr), mAttrName(aOther.mAttrName), + mSetAttr(aOther.mSetAttr), mNeedsAttrChange(aOther.mNeedsAttrChange) {} + + nsCOMPtr<Element> mBroadcaster; + nsCOMPtr<Element> mListener; + // Note if mAttrName isn't used, this is the name of the attr, otherwise + // this is the value of the attribute. + nsString mAttr; + nsCOMPtr<nsIAtom> mAttrName; + bool mSetAttr; + bool mNeedsAttrChange; + + class Comparator { + public: + static bool Equals(const nsDelayedBroadcastUpdate& a, const nsDelayedBroadcastUpdate& b) { + return a.mBroadcaster == b.mBroadcaster && a.mListener == b.mListener && a.mAttrName == b.mAttrName; + } + }; + }; + + nsTArray<nsDelayedBroadcastUpdate> mDelayedBroadcasters; + nsTArray<nsDelayedBroadcastUpdate> mDelayedAttrChangeBroadcasts; + bool mHandlingDelayedAttrChange; + bool mHandlingDelayedBroadcasters; + + void MaybeBroadcast(); +private: + // helpers + +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_XULDocument_h diff --git a/dom/xul/crashtests/107518-1.xml b/dom/xul/crashtests/107518-1.xml new file mode 100644 index 000000000..0fa41240a --- /dev/null +++ b/dom/xul/crashtests/107518-1.xml @@ -0,0 +1,52 @@ +<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<!DOCTYPE window>
+
+<window
+ id = "xulnote-main-window"
+ xmlns = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html = "http://www.w3.org/1999/xhtml"
+ onload = "init();"
+>
+ <scrollbox>
+ <vbox style="background-color:white;">
+ <text value="hahaha 0"/>
+ <text value="hahaha 1"/>
+ <text value="hahaha 2"/>
+ <text value="hahaha 3"/>
+ <text value="hahaha 4"/>
+ <text value="hahaha 5"/>
+ <text value="hahaha 6"/>
+ <text value="hahaha 7"/>
+ <text value="hahaha 8"/>
+ <text value="hahaha 9"/>
+ <text value="hahaha 10"/>
+ <text value="hahaha 11"/>
+ <text value="hahaha 12"/>
+ <text value="hahaha 13"/>
+ <text value="hahaha 14"/>
+ <text value="hahaha 15"/>
+ <text value="hahaha 16"/>
+ <text value="hahaha 17"/>
+ <text value="hahaha 18"/>
+ <text value="hahaha 19"/>
+ </vbox>
+<scrollbar
+ id="identifier"
+ align="horizontal"
+ curpos="20"
+ maxpos="100"
+ increment="1"
+ pageincrement="10"/>
+
+ </scrollbox>
+
+ <script type="application/x-javascript">
+ <![CDATA[
+ function init()
+ {
+ }
+ ]]>
+ </script>
+
+</window>
diff --git a/dom/xul/crashtests/252448-1.xul b/dom/xul/crashtests/252448-1.xul new file mode 100644 index 000000000..68813e740 --- /dev/null +++ b/dom/xul/crashtests/252448-1.xul @@ -0,0 +1,10 @@ +<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin/"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="document.documentElement.firstChild.focus();">
+ <menulist>
+ <menupopup style="-moz-binding: none;">
+ <spacer/>
+ </menupopup>
+ </menulist>
+</window>
diff --git a/dom/xul/crashtests/253479-1.xul b/dom/xul/crashtests/253479-1.xul new file mode 100644 index 000000000..5860d2200 --- /dev/null +++ b/dom/xul/crashtests/253479-1.xul @@ -0,0 +1,6 @@ +<?xml version="1.0"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+>
+<html:div style="position:fixed;"/>
+</window>
\ No newline at end of file diff --git a/dom/xul/crashtests/253479-2.xul b/dom/xul/crashtests/253479-2.xul new file mode 100644 index 000000000..5af86f1e9 --- /dev/null +++ b/dom/xul/crashtests/253479-2.xul @@ -0,0 +1,4 @@ +<?xml version="1.0"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <x style="position:fixed;"/>
+</window>
\ No newline at end of file diff --git a/dom/xul/crashtests/326204-1.xul b/dom/xul/crashtests/326204-1.xul new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/dom/xul/crashtests/326204-1.xul diff --git a/dom/xul/crashtests/326644-1-inner.xul b/dom/xul/crashtests/326644-1-inner.xul new file mode 100644 index 000000000..63ed9b721 --- /dev/null +++ b/dom/xul/crashtests/326644-1-inner.xul @@ -0,0 +1,34 @@ +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ title="Testcase bug 326644 - Crash when changing enumerated properties of objects in xul">
+
+<html:script><![CDATA[
+var timers=0;
+function doe(aObj, aNested, aCurrentTimer){
+var temp =0;
+for (var i in aObj) {
+try {
+if (typeof aObj[i] == 'object') {
+ if (aNested >= 19 || aObj[i] == window.location)
+ continue;
+ setTimeout(doe,500, aObj[i], ++aNested, timers);
+ timers++;
+}
+}
+catch(e){}
+try {
+ //if (temp == 68 && aNested == 21 && aCurrentTimer >= 116) {
+ // alert(i + '-'+ aObj[i]);
+ // return;
+ // }
+ aObj[i]= i;
+ temp+=1;
+}
+catch (e) {
+
+}
+}
+}
+var s=document.getElementsByTagName('window')[0];
+setTimeout(doe,100, s, 0);
+]]></html:script>
+</window>
diff --git a/dom/xul/crashtests/326644-1.html b/dom/xul/crashtests/326644-1.html new file mode 100644 index 000000000..41f2cc67f --- /dev/null +++ b/dom/xul/crashtests/326644-1.html @@ -0,0 +1,9 @@ +<html class="reftest-wait"> +<head> +<script> +setTimeout('document.documentElement.className = ""', 1000); +</script> +<body> +<iframe src="326644-1-inner.xul"></iframe> +</body> +</html> diff --git a/dom/xul/crashtests/326644-2-inner.xul b/dom/xul/crashtests/326644-2-inner.xul new file mode 100644 index 000000000..efbbc1c62 --- /dev/null +++ b/dom/xul/crashtests/326644-2-inner.xul @@ -0,0 +1,10 @@ +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
+ title="Testcase bug 326644 - Crash when changing enumerated properties of objects in xul">
+<html:script><![CDATA[
+function doe() {
+document.documentElement.boxObject.firstChild.hidden = true;
+document.documentElement.boxObject.firstChild.tooltip = 'test';
+}
+setTimeout(doe,100);
+]]></html:script>
+</window>
diff --git a/dom/xul/crashtests/326644-2.html b/dom/xul/crashtests/326644-2.html new file mode 100644 index 000000000..68fd49c75 --- /dev/null +++ b/dom/xul/crashtests/326644-2.html @@ -0,0 +1,9 @@ +<html class="reftest-wait"> +<head> +<script> +setTimeout('document.documentElement.className = ""', 1000); +</script> +<body> +<iframe src="326644-2-inner.xul"></iframe> +</body> +</html> diff --git a/dom/xul/crashtests/326864-1.xul b/dom/xul/crashtests/326864-1.xul new file mode 100644 index 000000000..5515ca259 --- /dev/null +++ b/dom/xul/crashtests/326864-1.xul @@ -0,0 +1,34 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + +<script> + +function getAnonymousNodes(e) { + return SpecialPowers.unwrap(SpecialPowers.wrap(document).getAnonymousNodes(e)); +} + +function init() +{ + var tt = document.getElementById("textbox"); + var hb = getAnonymousNodes(tt)[0]; // hbox + var men = getAnonymousNodes(hb)[1]; // menupopup + var menitem = men.childNodes[0]; // menuitem + var hb2 = getAnonymousNodes(menitem)[1]; // hbox + var label2 = hb2.childNodes[0]; // label + + men.menu = null; + label2.click(); +} + +window.addEventListener("load", init, false); + +</script> + + +<textbox id="textbox"/> + + +</window> diff --git a/dom/xul/crashtests/326875-1.xul b/dom/xul/crashtests/326875-1.xul new file mode 100644 index 000000000..e4c3f1d6d --- /dev/null +++ b/dom/xul/crashtests/326875-1.xul @@ -0,0 +1,27 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + + +<script> + + +function init() { + + var m = document.getElementById("m"); + m.parentNode.removeChild(m); + m.controllers; +}; + + +window.addEventListener("load", init, false); + +</script> + + + +<hbox id="m" /> + +</window> diff --git a/dom/xul/crashtests/326881-1.xul b/dom/xul/crashtests/326881-1.xul new file mode 100644 index 000000000..021da398a --- /dev/null +++ b/dom/xul/crashtests/326881-1.xul @@ -0,0 +1,13 @@ +<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
<script>
+ +function init() {
+ var n1 = document.getElementsByTagName("textbox")[0]; + n1.cloneNode(false); +} + +window.addEventListener("load", init, false); +
</script>
+ +<textbox/> +
</window>
\ No newline at end of file diff --git a/dom/xul/crashtests/329982-1.xhtml b/dom/xul/crashtests/329982-1.xhtml new file mode 100644 index 000000000..43f374a57 --- /dev/null +++ b/dom/xul/crashtests/329982-1.xhtml @@ -0,0 +1,42 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + +<script> + +function init() +{ + var A = document.getElementById("z"); + var B = A.nextSibling; + var C = B.nextSibling; + var P = A.parentNode; + + document.addEventListener("DOMNodeRemoved", fizzy, false); + P.removeChild(B); + document.removeEventListener("DOMNodeRemoved", fizzy, false); + + function fizzy() + { + document.removeEventListener("DOMNodeRemoved", fizzy, false); // avoid recursion + P.removeChild(A); + } + + document.documentElement.appendChild(C); +} + + +window.addEventListener("load", init, false); + +</script> + +</head> + +<body> + +<hbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + +<menupopup y="x">
<menuitem id="z"/>
<menuitem/>
<menuitem/>
</menupopup>
+ +</hbox> + +</body> +</html>
\ No newline at end of file diff --git a/dom/xul/crashtests/336096-1.xhtml b/dom/xul/crashtests/336096-1.xhtml new file mode 100644 index 000000000..e15691d88 --- /dev/null +++ b/dom/xul/crashtests/336096-1.xhtml @@ -0,0 +1,41 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<script> +<![CDATA[ + +function init() +{ + var targetWindow = window.frames[0]; + var targetDocument = targetWindow.document; + + targetDocument.body.appendChild(document.getElementById('rootish')); + targetDocument.designMode = 'on'; + + var r = targetDocument.createRange(); + r.setStart(targetDocument.getElementById("start"), 0); + r.setEnd (targetDocument.getElementById("end"), 0); + targetWindow.getSelection().addRange(r); + + targetDocument.execCommand('bold', false, null); +} + +]]> +</script> +</head> + +<body onload="setTimeout(init, 200);"> + +<iframe src="data:text/html," style="width: 95%; height: 500px;"></iframe> + +<div id="rootish"> + <div id="start"></div> + <hbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <hbox/> + <vbox id="end"> + <hbox/> + </vbox> + </hbox> +</div> + +</body> +</html> diff --git a/dom/xul/crashtests/344215-1.xul b/dom/xul/crashtests/344215-1.xul new file mode 100644 index 000000000..6443c22d6 --- /dev/null +++ b/dom/xul/crashtests/344215-1.xul @@ -0,0 +1,7 @@ +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + +<observes/> + +<description>You should not see any assertions in a debug build.</description> + +</window>
\ No newline at end of file diff --git a/dom/xul/crashtests/354611-1.html b/dom/xul/crashtests/354611-1.html new file mode 100644 index 000000000..fe25de366 --- /dev/null +++ b/dom/xul/crashtests/354611-1.html @@ -0,0 +1,20 @@ +<html> +<head> +<script> + +var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; + +function boom() +{ + var z = document.createElementNS(XUL_NS, "window"); + document.body.appendChild(z); + z.setAttribute("hidechrome", "true"); +} + +</script> + +<body onload="boom();"> + +</body> + +</html>
\ No newline at end of file diff --git a/dom/xul/crashtests/360078-1.xhtml b/dom/xul/crashtests/360078-1.xhtml new file mode 100644 index 000000000..a29087014 --- /dev/null +++ b/dom/xul/crashtests/360078-1.xhtml @@ -0,0 +1,42 @@ +<html xmlns="http://www.w3.org/1999/xhtml" class="reftest-wait"> +<head> + +<style> +<![CDATA[ +#baz { -moz-binding: url(360078-1xbl.xml#foo); } +]]> +</style> + +<script> +<![CDATA[ + +function stuff() +{ + var baz = document.getElementById("baz"); + var count = 0; + + setTimeout(step, 30); + + function step() + { + ++count; + if (count < 15) { + baz.cloneNode(true); + setTimeout(step, 30); + } + else { + document.documentElement.removeAttribute("class"); + } + } + +} + +]]> +</script> +</head> +<body onload="stuff()"> + +<hbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="baz"></hbox> + +</body> +</html> diff --git a/dom/xul/crashtests/360078-1xbl.xml b/dom/xul/crashtests/360078-1xbl.xml new file mode 100644 index 000000000..c0f428c05 --- /dev/null +++ b/dom/xul/crashtests/360078-1xbl.xml @@ -0,0 +1,3 @@ +<bindings xmlns="http://www.mozilla.org/xbl"><binding id="foo"><content>
+<a xmlns="http://www.w3.org/1999/xhtml" href="http://www.mozilla.org/" id="mlink">Foo<children xmlns="http://www.mozilla.org/xbl"/></a>
+</content></binding></bindings>
diff --git a/dom/xul/crashtests/363791-1.xul b/dom/xul/crashtests/363791-1.xul new file mode 100644 index 000000000..08d2c99e4 --- /dev/null +++ b/dom/xul/crashtests/363791-1.xul @@ -0,0 +1,44 @@ +<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + onload="setTimeout(boom, 30);" class="reftest-wait"> + +<script> + +function boom() +{ + var tree = document.getElementById("tree"); + var treecols = document.getElementById("treecols"); + var treechildren = document.getElementById("treechildren"); + + tree.appendChild(treechildren); // no real change + + function boom2() { + treecols.parentNode.removeChild(treecols); + document.documentElement.removeAttribute("class"); + } + + setTimeout(boom2, 30); +} + +</script> + + +<tree rows="6" id="tree"> + + <treecols id="treecols"> + <treecol id="firstname" label="First Name"/> + </treecols> + + <treechildren id="treechildren"> + <treeitem> + <treerow> + <treecell label="Bob"/> + </treerow> + </treeitem> + </treechildren> + +</tree> + + +</window> diff --git a/dom/xul/crashtests/384740-1.xul b/dom/xul/crashtests/384740-1.xul new file mode 100644 index 000000000..374f47c3d --- /dev/null +++ b/dom/xul/crashtests/384740-1.xul @@ -0,0 +1,23 @@ +<?xml version="1.0"?> + +<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" onload="boom()"> + +<script> + +function boom() +{ + var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; + + var scrollbar = document.createElementNS(XUL_NS, 'scrollbar'); + document.documentElement.appendChild(scrollbar); + var sbb = SpecialPowers.unwrap(SpecialPowers.wrap(document).getAnonymousNodes(scrollbar))[0]; + var action = document.createElementNS(XUL_NS, 'action'); + action.setAttribute('datasources', ""); + sbb.appendChild(action); +} + +</script> + +</window> diff --git a/dom/xul/crashtests/384877-1-inner.xul b/dom/xul/crashtests/384877-1-inner.xul new file mode 100644 index 000000000..9bbfc07bd --- /dev/null +++ b/dom/xul/crashtests/384877-1-inner.xul @@ -0,0 +1,15 @@ +<menupopup xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="d" popup="d"> +<script> +function doe() { +var d = document.getElementById('d'); +if (d.openPopup) { + d.openPopup(document.documentElement, 'before_start', 0, 0, false, false); +// alert(d.state); +} + +if (d.showPopup) + d.showPopup(); +} +setTimeout(doe, 200); +</script> +</menupopup> diff --git a/dom/xul/crashtests/384877-1.html b/dom/xul/crashtests/384877-1.html new file mode 100644 index 000000000..28fd2fc58 --- /dev/null +++ b/dom/xul/crashtests/384877-1.html @@ -0,0 +1,9 @@ +<html class="reftest-wait"> +<head> +<script> +setTimeout('document.documentElement.className = ""', 1000); +</script> +<body> +<iframe src="384877-1-inner.xul"></iframe> +</body> +</html> diff --git a/dom/xul/crashtests/386914-1-inner.xul b/dom/xul/crashtests/386914-1-inner.xul new file mode 100644 index 000000000..909889d8b --- /dev/null +++ b/dom/xul/crashtests/386914-1-inner.xul @@ -0,0 +1,10 @@ +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="a"> + +<box id="b" observes="a"/> + +<html:script xmlns:html="http://www.w3.org/1999/xhtml"> +document.getElementById('b').addEventListener('DOMAttrModified', function(e) {document.removeChild(document.documentElement);}, true); +setTimeout(function() {document.getElementById('a').setAttribute('tabindex', '1') ;}, 100); +</html:script> + +</window>
\ No newline at end of file diff --git a/dom/xul/crashtests/386914-1.html b/dom/xul/crashtests/386914-1.html new file mode 100644 index 000000000..a8961339b --- /dev/null +++ b/dom/xul/crashtests/386914-1.html @@ -0,0 +1,9 @@ +<html class="reftest-wait"> +<head> +<script> +setTimeout('document.documentElement.className = ""', 1000); +</script> +<body> +<iframe src="386914-1-inner.xul"></iframe> +</body> +</html> diff --git a/dom/xul/crashtests/386947-1.xul b/dom/xul/crashtests/386947-1.xul new file mode 100644 index 000000000..cf00d35f2 --- /dev/null +++ b/dom/xul/crashtests/386947-1.xul @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + style="background-color: red" onload="boom();"> + +<script><![CDATA[ + +function boom() { + document.documentElement.style.MozBinding = "url('#t')"; +} + +]]></script> +</window> diff --git a/dom/xul/crashtests/425821-1.xul b/dom/xul/crashtests/425821-1.xul new file mode 100644 index 000000000..9764e64aa --- /dev/null +++ b/dom/xul/crashtests/425821-1.xul @@ -0,0 +1,15 @@ +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" onload="boom();"> +<script type="text/javascript"> + +function boom() +{ + var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; + var wiz = document.createElementNS(XUL_NS, "wizard"); + var btn = document.createElementNS(XUL_NS, "hbox"); + btn.setAttribute("anonid", "Buttons"); + wiz.appendChild(btn); + wiz.cloneNode(true); +} + +</script> +</window> diff --git a/dom/xul/crashtests/428951-1.xul b/dom/xul/crashtests/428951-1.xul new file mode 100644 index 000000000..024bc5d5c --- /dev/null +++ b/dom/xul/crashtests/428951-1.xul @@ -0,0 +1,21 @@ +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:mathml="http://www.w3.org/1998/Math/MathML"> +<box> + <box style="background: initial;" id="f"> + <box style="margin-top: -9999999px;"/> + </box> + <mathml:divergence> + <box/> + </mathml:divergence> + <mathml:moment command="f"/> +</box> + +<script id="script" xmlns="http://www.w3.org/1999/xhtml"><![CDATA[ +function init() { + var f = document.getElementsByTagName('mathml:divergence')[0]; + window.addEventListener('DOMAttrModified',function() { f.parentNode.removeChild(f);}, true); + var x=document.getElementsByTagName('mathml:moment')[0]; + x.parentNode.removeChild(x); +} +window.addEventListener("load", init, false); +]]></script> +</window> diff --git a/dom/xul/crashtests/429085-1.xhtml b/dom/xul/crashtests/429085-1.xhtml new file mode 100644 index 000000000..96e030fd4 --- /dev/null +++ b/dom/xul/crashtests/429085-1.xhtml @@ -0,0 +1,21 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<script type="text/javascript"> +<![CDATA[ + +function boom() +{ + var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; + var prefs = document.createElementNS(XUL_NS, "preferences"); + var textbox = document.createElementNS(XUL_NS, "textbox"); + textbox.setAttribute("onchange", "1"); + prefs.appendChild(textbox); + prefs.cloneNode(true); +} + +]]> +</script> +</head> + +<body onload="boom();"></body> +</html> diff --git a/dom/xul/crashtests/431906-1-inner.xul b/dom/xul/crashtests/431906-1-inner.xul new file mode 100644 index 000000000..367f621b2 --- /dev/null +++ b/dom/xul/crashtests/431906-1-inner.xul @@ -0,0 +1,19 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<colgroup id="a" command="a"> +<box xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> +<box id="a" command="a"/> +<box id="a" command="a"/> +</box> +</colgroup> +<script><![CDATA[ +function doe(){ +document.getElementsByTagName('*')[1].setAttribute('id',''); +document.getElementsByTagName('*')[1].setAttribute('xmlns', ''); +document.getElementsByTagName('*')[3].setAttribute('id',''); + +document.getElementsByTagName('*')[4].removeAttribute('xmlns'); +document.getElementsByTagName('*')[4].setAttribute('width', '1px'); +} +setTimeout(doe,100); +]]></script> +</html>
\ No newline at end of file diff --git a/dom/xul/crashtests/431906-1.html b/dom/xul/crashtests/431906-1.html new file mode 100644 index 000000000..1639d3ea6 --- /dev/null +++ b/dom/xul/crashtests/431906-1.html @@ -0,0 +1,9 @@ +<html class="reftest-wait"> +<head> +<script> +setTimeout('document.documentElement.className = ""', 1000); +</script> +<body> +<iframe src="431906-1-inner.xul"></iframe> +</body> +</html> diff --git a/dom/xul/crashtests/451311-1.xul b/dom/xul/crashtests/451311-1.xul new file mode 100644 index 000000000..f07928b66 --- /dev/null +++ b/dom/xul/crashtests/451311-1.xul @@ -0,0 +1 @@ +<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"><iframe/></overlay> diff --git a/dom/xul/crashtests/461917-1.xhtml b/dom/xul/crashtests/461917-1.xhtml new file mode 100644 index 000000000..15792f6f0 --- /dev/null +++ b/dom/xul/crashtests/461917-1.xhtml @@ -0,0 +1,6 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<head></head> +<body> +<tabs xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" onselect="#"><wizard/></tabs> +</body> +</html> diff --git a/dom/xul/crashtests/468211-1.xul b/dom/xul/crashtests/468211-1.xul new file mode 100644 index 000000000..b6364f1bf --- /dev/null +++ b/dom/xul/crashtests/468211-1.xul @@ -0,0 +1,23 @@ +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" style="-moz-binding:url(#xbl2)" class="reftest-wait"> +<tooltip/> + +<script id="script" xmlns="http://www.w3.org/1999/xhtml"> +function doe() { +document.getElementsByTagName('tooltip')[0].setAttribute('style', '-moz-binding:url(#xbl)'); +document.getElementsByTagName('tooltip')[0].setAttribute('onDOMAttrModified', 'this.focus()'); +document.documentElement.removeAttribute("class"); +} +setTimeout(doe, 1); +</script> + +<bindings xmlns="http://www.mozilla.org/xbl" xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> +<binding id="xbl"> +<content> +<xul:box onbroadcast="document.documentElement.setAttribute('a','a')" id="g"/> +<xul:box onDOMAttrModified="document.documentElement.style.display = 'none'" observes="g"/> +</content> +</binding> + +<binding id="xbl2"></binding> +</bindings> +</window>
\ No newline at end of file diff --git a/dom/xul/crashtests/468211-2-binding.xml b/dom/xul/crashtests/468211-2-binding.xml new file mode 100644 index 000000000..6a9d14f13 --- /dev/null +++ b/dom/xul/crashtests/468211-2-binding.xml @@ -0,0 +1,12 @@ +<bindings xmlns="http://www.mozilla.org/xbl"> + +<binding id="xbl"> + +<content> +<mrow xmlns="http://www.w3.org/1998/Math/MathML" id="f"> +<box observes="f" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"/> +</mrow> +</content> + +</binding> +</bindings>
\ No newline at end of file diff --git a/dom/xul/crashtests/468211-2.xul b/dom/xul/crashtests/468211-2.xul new file mode 100644 index 000000000..ca3104875 --- /dev/null +++ b/dom/xul/crashtests/468211-2.xul @@ -0,0 +1,10 @@ +<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"> + +<html:div onDOMAttrModified="this.parentNode.removeChild(this)"> +<html:div/> +<html:style>div {-moz-binding:url(468211-2-binding.xml#xbl);</html:style> +</html:div> + +</window>
\ No newline at end of file diff --git a/dom/xul/crashtests/468211-3.xul b/dom/xul/crashtests/468211-3.xul new file mode 100644 index 000000000..4d221616e --- /dev/null +++ b/dom/xul/crashtests/468211-3.xul @@ -0,0 +1,18 @@ +<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?> +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+<box style="-moz-binding:url(#xbl)"/> + + +<bindings xmlns="http://www.mozilla.org/xbl"> +<binding id="xbl" inheritstyle="false"> +<content> +<g xmlns="http://www.w3.org/2000/svg" onDOMAttrModified="document.removeChild(document.documentElement)" id="a"/> +<box xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" observes="a"/> + +<children xmlns="http://www.mozilla.org/xbl"/> + +</content> +</binding> +</bindings> +
+</window>
\ No newline at end of file diff --git a/dom/xul/crashtests/495635-1.xul b/dom/xul/crashtests/495635-1.xul new file mode 100644 index 000000000..3d42a10bb --- /dev/null +++ b/dom/xul/crashtests/495635-1.xul @@ -0,0 +1,8 @@ +<?xml version="1.0"?>
+<?xul-overlay href="extA1.xul"?>
+<?xul-overlay href="extB1.xul"?>
+<?xul-overlay href="extA2.xul"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+<vbox id="browser-bottombox">
+</vbox>
+</window>
diff --git a/dom/xul/crashtests/509719-1-overlay.xul b/dom/xul/crashtests/509719-1-overlay.xul new file mode 100644 index 000000000..880edc1cd --- /dev/null +++ b/dom/xul/crashtests/509719-1-overlay.xul @@ -0,0 +1,3 @@ +<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <window id="win" removeelement="true"/> +</overlay> diff --git a/dom/xul/crashtests/509719-1.xul b/dom/xul/crashtests/509719-1.xul new file mode 100644 index 000000000..4d869b323 --- /dev/null +++ b/dom/xul/crashtests/509719-1.xul @@ -0,0 +1,3 @@ +<?xul-overlay href="509719-1-overlay.xul"?> +<window id="win" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> +</window> diff --git a/dom/xul/crashtests/509719-2-overlay.xul b/dom/xul/crashtests/509719-2-overlay.xul new file mode 100644 index 000000000..fefbe194a --- /dev/null +++ b/dom/xul/crashtests/509719-2-overlay.xul @@ -0,0 +1,8 @@ +<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script> + if (document.getElementById("testnode")) { + document.loadOverlay(window.location.href.substr(0,window.location.href.lastIndexOf('/')+1)+'509719-2-overlay.xul', null); + } + </script> + <box xmlns="http://www.w3.org/1999/xhtml" id="testnode" removeelement="true"/> +</overlay> diff --git a/dom/xul/crashtests/509719-2.xul b/dom/xul/crashtests/509719-2.xul new file mode 100644 index 000000000..5a64dc76e --- /dev/null +++ b/dom/xul/crashtests/509719-2.xul @@ -0,0 +1,7 @@ +<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?> +<window id="win" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <box id="testnode" onDOMAttrModified="this.parentNode.removeChild(this)"/> + <script> +document.loadOverlay(window.location.href.substr(0,window.location.href.lastIndexOf('/')+1)+'509719-2-overlay.xul', null); + </script> +</window> diff --git a/dom/xul/crashtests/583230.xul b/dom/xul/crashtests/583230.xul new file mode 100644 index 000000000..0361b619d --- /dev/null +++ b/dom/xul/crashtests/583230.xul @@ -0,0 +1,23 @@ +<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?> +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> +<richlistbox id="a" datasources="" template="d"/> +<script><![CDATA[ +function doe() { +var node = document.getElementById('a'); +var b = node.builder; +document.removeChild(document.documentElement); +b.addResult({}, node); +b.removeResult({}); +b.replaceResult({}, {}, node); +b.resultBindingChanged({}); +try { b.addResult(null, null); } catch(ex) { } +try { b.removeResult(null); } catch(ex) { } +try { b.replaceResult(null, null, null); } catch(ex) { } +try { b.resultBindingChanged(null); } catch(ex) { } +try { b.getResultForId("empty"); } catch(ex) { } +try { b.getResultForContent(node); } catch(ex) { } +try { b.hasGeneratedContent(null, null); } catch(ex) { } +} +window.addEventListener("load", doe, false); +]]></script> +</window> diff --git a/dom/xul/crashtests/crashtests.list b/dom/xul/crashtests/crashtests.list new file mode 100644 index 000000000..737a32f60 --- /dev/null +++ b/dom/xul/crashtests/crashtests.list @@ -0,0 +1,33 @@ +load 107518-1.xml +load 252448-1.xul +load 253479-1.xul +load 253479-2.xul +load 326204-1.xul +load 326644-1.html +load 326644-2.html +load 326864-1.xul +load 326875-1.xul +load 326881-1.xul +load 329982-1.xhtml +load 336096-1.xhtml +load 344215-1.xul +load 354611-1.html +load 360078-1.xhtml +load 363791-1.xul +load 384740-1.xul +load 384877-1.html +load 386914-1.html +load 386947-1.xul +load 425821-1.xul +load 428951-1.xul +load 429085-1.xhtml +load 431906-1.html +load 451311-1.xul +load 461917-1.xhtml +load 468211-1.xul +load 468211-2.xul +load 468211-3.xul +load 495635-1.xul +load 509719-1.xul +asserts(3) load 509719-2.xul # bug 909819 +load 583230.xul diff --git a/dom/xul/crashtests/extA1.xul b/dom/xul/crashtests/extA1.xul new file mode 100644 index 000000000..bfd2c0d44 --- /dev/null +++ b/dom/xul/crashtests/extA1.xul @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE overlay SYSTEM "chrome://exta/locale/exta.dtd"> +<overlay id="extA1" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <vbox id="browser-bottombox"> + <statusbar id="extAbar" /> + </vbox> +</overlay> diff --git a/dom/xul/crashtests/extA2.xul b/dom/xul/crashtests/extA2.xul new file mode 100644 index 000000000..8d77a4995 --- /dev/null +++ b/dom/xul/crashtests/extA2.xul @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE overlay SYSTEM "chrome://exta/locale/exta.dtd"> +<overlay id="extA2" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <statusbar id="extAbar"> + <statusbarpanel id="extApanel" label="panel 1"> + </statusbarpanel> + <statusbarpanel id="extApanel2" label="panel 2" /> + </statusbar> +</overlay> diff --git a/dom/xul/crashtests/extB1.xul b/dom/xul/crashtests/extB1.xul new file mode 100644 index 000000000..9e2f5c418 --- /dev/null +++ b/dom/xul/crashtests/extB1.xul @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<overlay id="extA2" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <statusbar id="extAbar" removeelement="true" /> +</overlay> diff --git a/dom/xul/moz.build b/dom/xul/moz.build new file mode 100644 index 000000000..8dff722be --- /dev/null +++ b/dom/xul/moz.build @@ -0,0 +1,59 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +MOCHITEST_MANIFESTS += ['test/mochitest.ini'] + +MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini'] + +if CONFIG['MOZ_XUL']: + DIRS += ['templates'] + + XPIDL_SOURCES += [ + 'nsIXULOverlayProvider.idl', + ] + + EXPORTS += [ + 'nsIXULDocument.h', + ] + + UNIFIED_SOURCES += [ + 'nsXULCommandDispatcher.cpp', + 'nsXULContentSink.cpp', + 'nsXULElement.cpp', + 'nsXULPopupListener.cpp', + 'nsXULPrototypeCache.cpp', + 'nsXULPrototypeDocument.cpp', + 'XULDocument.cpp', + ] + +XPIDL_SOURCES += [ + 'nsIController.idl', + 'nsIControllers.idl', +] + +XPIDL_MODULE = 'xul' + +UNIFIED_SOURCES += [ + 'nsXULControllers.cpp', +] + +LOCAL_INCLUDES += [ + '/docshell/base', + '/dom/base', + '/dom/html', + '/dom/xbl', + '/dom/xml', + '/dom/xul/templates', + '/layout/base', + '/layout/generic', + '/layout/style', + '/layout/xul', +] + +FINAL_LIBRARY = 'xul' + +if CONFIG['GNU_CXX']: + CXXFLAGS += ['-Wno-error=shadow'] diff --git a/dom/xul/nsForwardReference.h b/dom/xul/nsForwardReference.h new file mode 100644 index 000000000..2528662c3 --- /dev/null +++ b/dom/xul/nsForwardReference.h @@ -0,0 +1,76 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsForwardReference_h__ +#define nsForwardReference_h__ + +class nsForwardReference +{ +protected: + nsForwardReference() {} + +public: + virtual ~nsForwardReference() {} + + /** + * Priority codes returned from GetPhase() + */ + enum Phase { + /** A dummy marker, used to indicate unstarted resolution */ + eStart, + + /** The initial pass, after which the content model will be + fully built */ + eConstruction, + + /** A second pass, after which all 'magic attribute' hookup + will have been performed */ + eHookup, + + /** A dummy marker, used in kPasses to indicate termination */ + eDone + }; + + /** + * Forward references are categorized by 'priority', and all + * forward references in a higher priority are resolved before any + * reference in a lower priority. This variable specifies this + * ordering. The last Priority is guaranteed to be eDone. + */ + static const Phase kPasses[]; + + /** + * Get the state in which the forward reference should be resolved. + * 'eConstruction' references are all resolved before 'eHookup' references + * are resolved. + * + * @return the Phase in which the reference needs to be resolved + */ + virtual Phase GetPhase() = 0; + + /** + * Result codes returned from Resolve() + */ + enum Result { + /** Resolution succeeded, I'm done. */ + eResolve_Succeeded, + + /** Couldn't resolve, but try me later. */ + eResolve_Later, + + /** Something bad happened, don't try again. */ + eResolve_Error + }; + + /** + * Attempt to resolve the forward reference. + * + * @return a Result that tells the resolver how to treat + * the reference. + */ + virtual Result Resolve() = 0; +}; + +#endif // nsForwardReference_h__ diff --git a/dom/xul/nsIController.idl b/dom/xul/nsIController.idl new file mode 100644 index 000000000..ace5374a0 --- /dev/null +++ b/dom/xul/nsIController.idl @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +[scriptable, uuid(D5B61B82-1DA4-11d3-BF87-00105A1B0627)] +interface nsIController : nsISupports { + boolean isCommandEnabled(in string command); + boolean supportsCommand(in string command); + + void doCommand(in string command); + + void onEvent(in string eventName); +}; + + +/* + + Enhanced controller interface that allows for passing of parameters + to commands. + +*/ + +interface nsICommandParams; + +[scriptable, uuid(EEC0B435-7F53-44FE-B00A-CF3EED65C01A)] +interface nsICommandController : nsISupports +{ + + void getCommandStateWithParams( in string command, in nsICommandParams aCommandParams); + + void doCommandWithParams(in string command, in nsICommandParams aCommandParams); + + void getSupportedCommands(out unsigned long count, + [array, size_is(count), retval] out string commands); +}; + + +/* + An API for registering commands in groups, to allow for + updating via nsIDOMWindow::UpdateCommands. +*/ +interface nsISimpleEnumerator; + +[scriptable, uuid(9F82C404-1C7B-11D5-A73C-ECA43CA836FC)] +interface nsIControllerCommandGroup : nsISupports +{ + + void addCommandToGroup(in string aCommand, in string aGroup); + void removeCommandFromGroup(in string aCommand, in string aGroup); + + boolean isCommandInGroup(in string aCommand, in string aGroup); + + /* + We should expose some methods that allow for enumeration. + */ + nsISimpleEnumerator getGroupsEnumerator(); + + nsISimpleEnumerator getEnumeratorForGroup(in string aGroup); + +}; + diff --git a/dom/xul/nsIControllers.idl b/dom/xul/nsIControllers.idl new file mode 100644 index 000000000..d9505a5e8 --- /dev/null +++ b/dom/xul/nsIControllers.idl @@ -0,0 +1,34 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +interface nsIController; +interface nsIDOMXULCommandDispatcher; + +[scriptable, uuid(f36e3ec1-9197-4ad8-8d4c-d3b1927fd6df)] +interface nsIControllers : nsISupports +{ + nsIController getControllerForCommand(in string command); + + void insertControllerAt(in unsigned long index, in nsIController controller); + nsIController removeControllerAt(in unsigned long index); + nsIController getControllerAt(in unsigned long index); + + void appendController(in nsIController controller); + void removeController(in nsIController controller); + + /* + Return an ID for this controller which is unique to this + nsIControllers. + */ + unsigned long getControllerId(in nsIController controller); + /* + Get the controller specified by the given ID. + */ + nsIController getControllerById(in unsigned long controllerID); + + unsigned long getControllerCount(); +}; diff --git a/dom/xul/nsIXULDocument.h b/dom/xul/nsIXULDocument.h new file mode 100644 index 000000000..944c481d9 --- /dev/null +++ b/dom/xul/nsIXULDocument.h @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsIXULDocument_h___ +#define nsIXULDocument_h___ + +#include "nsISupports.h" +#include "nsString.h" +#include "nsCOMArray.h" + +class nsIXULTemplateBuilder; +class nsIContent; + + +// 81ba4be5-6cc5-478a-9b08-b3e7ed524455 +#define NS_IXULDOCUMENT_IID \ + {0x81ba4be5, 0x6cc5, 0x478a, {0x9b, 0x08, 0xb3, 0xe7, 0xed, 0x52, 0x44, 0x55}} + + +/* + * An XUL-specific extension to nsIDocument. Includes methods for + * setting the root resource of the document content model, a factory + * method for constructing the children of a node, etc. + */ +class nsIXULDocument : public nsISupports +{ +public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IXULDOCUMENT_IID) + + /** + * Get the elements for a particular resource --- all elements whose 'id' + * or 'ref' is aID. The nsCOMArray will be truncated and filled in with + * nsIContent pointers. + */ + virtual void GetElementsForID(const nsAString& aID, nsCOMArray<nsIContent>& aElements) = 0; + + /** + * Notify the XUL document that a subtree has been added + */ + NS_IMETHOD AddSubtreeToDocument(nsIContent* aElement) = 0; + + /** + * Notify the XUL document that a subtree has been removed + */ + NS_IMETHOD RemoveSubtreeFromDocument(nsIContent* aElement) = 0; + + /** + * Attach a XUL template builder to the specified content node. + * @param aBuilder the template builder to attach, or null if + * the builder is to be removed. + */ + NS_IMETHOD SetTemplateBuilderFor(nsIContent* aContent, nsIXULTemplateBuilder* aBuilder) = 0; + + /** + * Retrieve the XUL template builder that's attached to a content + * node. + */ + NS_IMETHOD GetTemplateBuilderFor(nsIContent* aContent, nsIXULTemplateBuilder** aResult) = 0; + + /** + * This is invoked whenever the prototype for this document is loaded + * and should be walked, regardless of whether the XUL cache is + * disabled, whether the protototype was loaded, whether the + * prototype was loaded from the cache or created by parsing the + * actual XUL source, etc. + * + * @param aResumeWalk whether this should also call ResumeWalk(). + * Sometimes the caller of OnPrototypeLoadDone resumes the walk itself + */ + NS_IMETHOD OnPrototypeLoadDone(bool aResumeWalk) = 0; + + /** + * Callback notifying when a document could not be parsed properly. + */ + virtual bool OnDocumentParserError() = 0; + + /** + * Reset the document direction so that it is recomputed. + */ + virtual void ResetDocumentDirection() = 0; + + virtual void ResetDocumentLWTheme() = 0; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIXULDocument, NS_IXULDOCUMENT_IID) + +// factory functions +nsresult NS_NewXULDocument(nsIXULDocument** result); + +#endif // nsIXULDocument_h___ diff --git a/dom/xul/nsIXULOverlayProvider.idl b/dom/xul/nsIXULOverlayProvider.idl new file mode 100644 index 000000000..f74a1717d --- /dev/null +++ b/dom/xul/nsIXULOverlayProvider.idl @@ -0,0 +1,34 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +interface nsISimpleEnumerator; +interface nsIURI; + +/** + * The chrome registry implements this interface to give overlays + * to the gecko XUL engine. + */ + +[scriptable, uuid(1d5b5b94-dc47-4050-93b7-ac092e383cad)] +interface nsIXULOverlayProvider : nsISupports +{ + /** + * Get the XUL overlays for a particular chrome URI. + * + * @param aURI The URI being loaded + * @return An enumerator of nsIURI for the overlays of this URI + */ + nsISimpleEnumerator /*nsIURI*/ getXULOverlays(in nsIURI aURI); + + /** + * Get the style overlays for a particular chrome URI. + * + * @param aURI The URI being loaded + * @return An enumerator of nsIURI for the overlays of this URI + */ + nsISimpleEnumerator /*nsIURI*/ getStyleOverlays(in nsIURI aURI); +}; diff --git a/dom/xul/nsXULCommandDispatcher.cpp b/dom/xul/nsXULCommandDispatcher.cpp new file mode 100644 index 000000000..2d222c240 --- /dev/null +++ b/dom/xul/nsXULCommandDispatcher.cpp @@ -0,0 +1,459 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 sw=2 et tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + + This file provides the implementation for the XUL Command Dispatcher. + + */ + +#include "nsIContent.h" +#include "nsFocusManager.h" +#include "nsIControllers.h" +#include "nsIDOMDocument.h" +#include "nsIDOMElement.h" +#include "nsIDOMWindow.h" +#include "nsIDOMXULElement.h" +#include "nsIDocument.h" +#include "nsPresContext.h" +#include "nsIPresShell.h" +#include "nsIScriptGlobalObject.h" +#include "nsPIDOMWindow.h" +#include "nsPIWindowRoot.h" +#include "nsRDFCID.h" +#include "nsXULCommandDispatcher.h" +#include "mozilla/Logging.h" +#include "nsContentUtils.h" +#include "nsReadableUtils.h" +#include "nsCRT.h" +#include "nsError.h" +#include "nsDOMClassInfoID.h" +#include "mozilla/BasicEvents.h" +#include "mozilla/EventDispatcher.h" +#include "mozilla/dom/Element.h" + +using namespace mozilla; + +static LazyLogModule gCommandLog("nsXULCommandDispatcher"); + +//////////////////////////////////////////////////////////////////////// + +nsXULCommandDispatcher::nsXULCommandDispatcher(nsIDocument* aDocument) + : mDocument(aDocument), mUpdaters(nullptr) +{ +} + +nsXULCommandDispatcher::~nsXULCommandDispatcher() +{ + Disconnect(); +} + +// QueryInterface implementation for nsXULCommandDispatcher + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULCommandDispatcher) + NS_INTERFACE_MAP_ENTRY(nsIDOMXULCommandDispatcher) + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMXULCommandDispatcher) + NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(XULCommandDispatcher) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULCommandDispatcher) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULCommandDispatcher) + +NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULCommandDispatcher) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULCommandDispatcher) + tmp->Disconnect(); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULCommandDispatcher) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument) + Updater* updater = tmp->mUpdaters; + while (updater) { + cb.NoteXPCOMChild(updater->mElement); + updater = updater->mNext; + } +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +void +nsXULCommandDispatcher::Disconnect() +{ + while (mUpdaters) { + Updater* doomed = mUpdaters; + mUpdaters = mUpdaters->mNext; + delete doomed; + } + mDocument = nullptr; +} + +already_AddRefed<nsPIWindowRoot> +nsXULCommandDispatcher::GetWindowRoot() +{ + if (mDocument) { + if (nsCOMPtr<nsPIDOMWindowOuter> window = mDocument->GetWindow()) { + return window->GetTopWindowRoot(); + } + } + + return nullptr; +} + +nsIContent* +nsXULCommandDispatcher::GetRootFocusedContentAndWindow(nsPIDOMWindowOuter** aWindow) +{ + *aWindow = nullptr; + + if (!mDocument) { + return nullptr; + } + + if (nsCOMPtr<nsPIDOMWindowOuter> win = mDocument->GetWindow()) { + if (nsCOMPtr<nsPIDOMWindowOuter> rootWindow = win->GetPrivateRoot()) { + return nsFocusManager::GetFocusedDescendant(rootWindow, true, aWindow); + } + } + + return nullptr; +} + +NS_IMETHODIMP +nsXULCommandDispatcher::GetFocusedElement(nsIDOMElement** aElement) +{ + *aElement = nullptr; + + nsCOMPtr<nsPIDOMWindowOuter> focusedWindow; + nsIContent* focusedContent = + GetRootFocusedContentAndWindow(getter_AddRefs(focusedWindow)); + if (focusedContent) { + CallQueryInterface(focusedContent, aElement); + + // Make sure the caller can access the focused element. + nsCOMPtr<nsINode> node = do_QueryInterface(*aElement); + if (!node || !nsContentUtils::SubjectPrincipalOrSystemIfNativeCaller()->Subsumes(node->NodePrincipal())) { + // XXX This might want to return null, but we use that return value + // to mean "there is no focused element," so to be clear, throw an + // exception. + NS_RELEASE(*aElement); + return NS_ERROR_DOM_SECURITY_ERR; + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXULCommandDispatcher::GetFocusedWindow(mozIDOMWindowProxy** aWindow) +{ + *aWindow = nullptr; + + nsCOMPtr<nsPIDOMWindowOuter> window; + GetRootFocusedContentAndWindow(getter_AddRefs(window)); + if (!window) + return NS_OK; + + // Make sure the caller can access this window. The caller can access this + // window iff it can access the document. + nsCOMPtr<nsIDocument> doc = window->GetDoc(); + + // Note: If there is no document, then this window has been cleared and + // there's nothing left to protect, so let the window pass through. + if (doc && !nsContentUtils::CanCallerAccess(doc)) + return NS_ERROR_DOM_SECURITY_ERR; + + window.forget(aWindow); + return NS_OK; +} + +NS_IMETHODIMP +nsXULCommandDispatcher::SetFocusedElement(nsIDOMElement* aElement) +{ + nsIFocusManager* fm = nsFocusManager::GetFocusManager(); + NS_ENSURE_TRUE(fm, NS_ERROR_FAILURE); + + if (aElement) + return fm->SetFocus(aElement, 0); + + // if aElement is null, clear the focus in the currently focused child window + nsCOMPtr<nsPIDOMWindowOuter> focusedWindow; + GetRootFocusedContentAndWindow(getter_AddRefs(focusedWindow)); + return fm->ClearFocus(focusedWindow); +} + +NS_IMETHODIMP +nsXULCommandDispatcher::SetFocusedWindow(mozIDOMWindowProxy* aWindow) +{ + NS_ENSURE_TRUE(aWindow, NS_OK); // do nothing if set to null + + nsCOMPtr<nsPIDOMWindowOuter> window = nsPIDOMWindowOuter::From(aWindow); + NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); + + nsIFocusManager* fm = nsFocusManager::GetFocusManager(); + NS_ENSURE_TRUE(fm, NS_ERROR_FAILURE); + + // get the containing frame for the window, and set it as focused. This will + // end up focusing whatever is currently focused inside the frame. Since + // setting the command dispatcher's focused window doesn't raise the window, + // setting it to a top-level window doesn't need to do anything. + nsCOMPtr<nsIDOMElement> frameElement = + do_QueryInterface(window->GetFrameElementInternal()); + if (frameElement) + return fm->SetFocus(frameElement, 0); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULCommandDispatcher::AdvanceFocus() +{ + return AdvanceFocusIntoSubtree(nullptr); +} + +NS_IMETHODIMP +nsXULCommandDispatcher::RewindFocus() +{ + nsCOMPtr<nsPIDOMWindowOuter> win; + GetRootFocusedContentAndWindow(getter_AddRefs(win)); + + nsCOMPtr<nsIDOMElement> result; + nsIFocusManager* fm = nsFocusManager::GetFocusManager(); + if (fm) + return fm->MoveFocus(win, nullptr, nsIFocusManager::MOVEFOCUS_BACKWARD, + 0, getter_AddRefs(result)); + return NS_OK; +} + +NS_IMETHODIMP +nsXULCommandDispatcher::AdvanceFocusIntoSubtree(nsIDOMElement* aElt) +{ + nsCOMPtr<nsPIDOMWindowOuter> win; + GetRootFocusedContentAndWindow(getter_AddRefs(win)); + + nsCOMPtr<nsIDOMElement> result; + nsIFocusManager* fm = nsFocusManager::GetFocusManager(); + if (fm) + return fm->MoveFocus(win, aElt, nsIFocusManager::MOVEFOCUS_FORWARD, + 0, getter_AddRefs(result)); + return NS_OK; +} + +NS_IMETHODIMP +nsXULCommandDispatcher::AddCommandUpdater(nsIDOMElement* aElement, + const nsAString& aEvents, + const nsAString& aTargets) +{ + NS_PRECONDITION(aElement != nullptr, "null ptr"); + if (! aElement) + return NS_ERROR_NULL_POINTER; + + NS_ENSURE_TRUE(mDocument, NS_ERROR_UNEXPECTED); + + nsresult rv = nsContentUtils::CheckSameOrigin(mDocument, aElement); + + if (NS_FAILED(rv)) { + return rv; + } + + Updater* updater = mUpdaters; + Updater** link = &mUpdaters; + + while (updater) { + if (updater->mElement == aElement) { + +#ifdef DEBUG + if (MOZ_LOG_TEST(gCommandLog, LogLevel::Debug)) { + nsAutoCString eventsC, targetsC, aeventsC, atargetsC; + eventsC.AssignWithConversion(updater->mEvents); + targetsC.AssignWithConversion(updater->mTargets); + CopyUTF16toUTF8(aEvents, aeventsC); + CopyUTF16toUTF8(aTargets, atargetsC); + MOZ_LOG(gCommandLog, LogLevel::Debug, + ("xulcmd[%p] replace %p(events=%s targets=%s) with (events=%s targets=%s)", + this, aElement, + eventsC.get(), + targetsC.get(), + aeventsC.get(), + atargetsC.get())); + } +#endif + + // If the updater was already in the list, then replace + // (?) the 'events' and 'targets' filters with the new + // specification. + updater->mEvents = aEvents; + updater->mTargets = aTargets; + return NS_OK; + } + + link = &(updater->mNext); + updater = updater->mNext; + } +#ifdef DEBUG + if (MOZ_LOG_TEST(gCommandLog, LogLevel::Debug)) { + nsAutoCString aeventsC, atargetsC; + CopyUTF16toUTF8(aEvents, aeventsC); + CopyUTF16toUTF8(aTargets, atargetsC); + + MOZ_LOG(gCommandLog, LogLevel::Debug, + ("xulcmd[%p] add %p(events=%s targets=%s)", + this, aElement, + aeventsC.get(), + atargetsC.get())); + } +#endif + + // If we get here, this is a new updater. Append it to the list. + *link = new Updater(aElement, aEvents, aTargets); + return NS_OK; +} + +NS_IMETHODIMP +nsXULCommandDispatcher::RemoveCommandUpdater(nsIDOMElement* aElement) +{ + NS_PRECONDITION(aElement != nullptr, "null ptr"); + if (! aElement) + return NS_ERROR_NULL_POINTER; + + Updater* updater = mUpdaters; + Updater** link = &mUpdaters; + + while (updater) { + if (updater->mElement == aElement) { +#ifdef DEBUG + if (MOZ_LOG_TEST(gCommandLog, LogLevel::Debug)) { + nsAutoCString eventsC, targetsC; + eventsC.AssignWithConversion(updater->mEvents); + targetsC.AssignWithConversion(updater->mTargets); + MOZ_LOG(gCommandLog, LogLevel::Debug, + ("xulcmd[%p] remove %p(events=%s targets=%s)", + this, aElement, + eventsC.get(), + targetsC.get())); + } +#endif + + *link = updater->mNext; + delete updater; + return NS_OK; + } + + link = &(updater->mNext); + updater = updater->mNext; + } + + // Hmm. Not found. Oh well. + return NS_OK; +} + +NS_IMETHODIMP +nsXULCommandDispatcher::UpdateCommands(const nsAString& aEventName) +{ + nsAutoString id; + nsCOMPtr<nsIDOMElement> element; + GetFocusedElement(getter_AddRefs(element)); + if (element) { + nsresult rv = element->GetAttribute(NS_LITERAL_STRING("id"), id); + NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get element's id"); + if (NS_FAILED(rv)) return rv; + } + + nsCOMArray<nsIContent> updaters; + + for (Updater* updater = mUpdaters; updater != nullptr; updater = updater->mNext) { + // Skip any nodes that don't match our 'events' or 'targets' + // filters. + if (! Matches(updater->mEvents, aEventName)) + continue; + + if (! Matches(updater->mTargets, id)) + continue; + + nsCOMPtr<nsIContent> content = do_QueryInterface(updater->mElement); + NS_ASSERTION(content != nullptr, "not an nsIContent"); + if (! content) + return NS_ERROR_UNEXPECTED; + + updaters.AppendObject(content); + } + + for (int32_t u = 0; u < updaters.Count(); u++) { + nsIContent* content = updaters[u]; + +#ifdef DEBUG + if (MOZ_LOG_TEST(gCommandLog, LogLevel::Debug)) { + nsAutoCString aeventnameC; + CopyUTF16toUTF8(aEventName, aeventnameC); + MOZ_LOG(gCommandLog, LogLevel::Debug, + ("xulcmd[%p] update %p event=%s", + this, content, + aeventnameC.get())); + } +#endif + + WidgetEvent event(true, eXULCommandUpdate); + EventDispatcher::Dispatch(content, nullptr, &event); + } + return NS_OK; +} + +bool +nsXULCommandDispatcher::Matches(const nsString& aList, + const nsAString& aElement) +{ + if (aList.EqualsLiteral("*")) + return true; // match _everything_! + + int32_t indx = aList.Find(PromiseFlatString(aElement)); + if (indx == -1) + return false; // not in the list at all + + // okay, now make sure it's not a substring snafu; e.g., 'ur' + // found inside of 'blur'. + if (indx > 0) { + char16_t ch = aList[indx - 1]; + if (! nsCRT::IsAsciiSpace(ch) && ch != char16_t(',')) + return false; + } + + if (indx + aElement.Length() < aList.Length()) { + char16_t ch = aList[indx + aElement.Length()]; + if (! nsCRT::IsAsciiSpace(ch) && ch != char16_t(',')) + return false; + } + + return true; +} + +NS_IMETHODIMP +nsXULCommandDispatcher::GetControllers(nsIControllers** aResult) +{ + nsCOMPtr<nsPIWindowRoot> root = GetWindowRoot(); + NS_ENSURE_TRUE(root, NS_ERROR_FAILURE); + + return root->GetControllers(aResult); +} + +NS_IMETHODIMP +nsXULCommandDispatcher::GetControllerForCommand(const char *aCommand, nsIController** _retval) +{ + nsCOMPtr<nsPIWindowRoot> root = GetWindowRoot(); + NS_ENSURE_TRUE(root, NS_ERROR_FAILURE); + + return root->GetControllerForCommand(aCommand, _retval); +} + +NS_IMETHODIMP +nsXULCommandDispatcher::GetSuppressFocusScroll(bool* aSuppressFocusScroll) +{ + *aSuppressFocusScroll = false; + return NS_OK; +} + +NS_IMETHODIMP +nsXULCommandDispatcher::SetSuppressFocusScroll(bool aSuppressFocusScroll) +{ + return NS_OK; +} + diff --git a/dom/xul/nsXULCommandDispatcher.h b/dom/xul/nsXULCommandDispatcher.h new file mode 100644 index 000000000..bb33edc8e --- /dev/null +++ b/dom/xul/nsXULCommandDispatcher.h @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +/* + + This is the focus manager for XUL documents. + +*/ + +#ifndef nsXULCommandDispatcher_h__ +#define nsXULCommandDispatcher_h__ + +#include "nsCOMPtr.h" +#include "nsIDOMXULCommandDispatcher.h" +#include "nsWeakReference.h" +#include "nsIDOMNode.h" +#include "nsString.h" +#include "nsCycleCollectionParticipant.h" + +class nsIDOMElement; +class nsPIWindowRoot; + +class nsXULCommandDispatcher : public nsIDOMXULCommandDispatcher, + public nsSupportsWeakReference +{ +public: + explicit nsXULCommandDispatcher(nsIDocument* aDocument); + + // nsISupports + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXULCommandDispatcher, + nsIDOMXULCommandDispatcher) + + // nsIDOMXULCommandDispatcher interface + NS_DECL_NSIDOMXULCOMMANDDISPATCHER + + void Disconnect(); +protected: + virtual ~nsXULCommandDispatcher(); + + already_AddRefed<nsPIWindowRoot> GetWindowRoot(); + + nsIContent* GetRootFocusedContentAndWindow(nsPIDOMWindowOuter** aWindow); + + nsCOMPtr<nsIDocument> mDocument; + + class Updater { + public: + Updater(nsIDOMElement* aElement, + const nsAString& aEvents, + const nsAString& aTargets) + : mElement(aElement), + mEvents(aEvents), + mTargets(aTargets), + mNext(nullptr) + {} + + nsCOMPtr<nsIDOMElement> mElement; + nsString mEvents; + nsString mTargets; + Updater* mNext; + }; + + Updater* mUpdaters; + + bool Matches(const nsString& aList, + const nsAString& aElement); +}; + +#endif // nsXULCommandDispatcher_h__ diff --git a/dom/xul/nsXULContentSink.cpp b/dom/xul/nsXULContentSink.cpp new file mode 100644 index 000000000..7103be758 --- /dev/null +++ b/dom/xul/nsXULContentSink.cpp @@ -0,0 +1,1051 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=4 et sw=4 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * An implementation for a Gecko-style content sink that knows how + * to build a content model (the "prototype" document) from XUL. + * + * For more information on XUL, + * see http://developer.mozilla.org/en/docs/XUL + */ + +#include "nsXULContentSink.h" + +#include "jsfriendapi.h" + +#include "nsCOMPtr.h" +#include "nsForwardReference.h" +#include "nsHTMLStyleSheet.h" +#include "nsIContentSink.h" +#include "nsIDocument.h" +#include "nsIDOMEventListener.h" +#include "nsIDOMHTMLFormElement.h" +#include "nsIDOMXULDocument.h" +#include "nsIFormControl.h" +#include "mozilla/dom/NodeInfo.h" +#include "nsIScriptContext.h" +#include "nsIScriptGlobalObject.h" +#include "nsIServiceManager.h" +#include "nsIURL.h" +#include "nsNameSpaceManager.h" +#include "nsParserBase.h" +#include "nsViewManager.h" +#include "nsIXULDocument.h" +#include "nsIScriptSecurityManager.h" +#include "nsLayoutCID.h" +#include "nsNetUtil.h" +#include "nsRDFCID.h" +#include "nsXPIDLString.h" +#include "nsReadableUtils.h" +#include "nsXULElement.h" +#include "mozilla/Logging.h" +#include "prmem.h" +#include "nsCRT.h" + +#include "nsXULPrototypeDocument.h" // XXXbe temporary +#include "mozilla/css/Loader.h" + +#include "nsUnicharUtils.h" +#include "nsGkAtoms.h" +#include "nsContentUtils.h" +#include "nsAttrName.h" +#include "nsXMLContentSink.h" +#include "nsIConsoleService.h" +#include "nsIScriptError.h" +#include "nsContentTypeParser.h" + +static mozilla::LazyLogModule gContentSinkLog("nsXULContentSink");; + +//---------------------------------------------------------------------- + +XULContentSinkImpl::ContextStack::ContextStack() + : mTop(nullptr), mDepth(0) +{ +} + +XULContentSinkImpl::ContextStack::~ContextStack() +{ + while (mTop) { + Entry* doomed = mTop; + mTop = mTop->mNext; + delete doomed; + } +} + +nsresult +XULContentSinkImpl::ContextStack::Push(nsXULPrototypeNode* aNode, State aState) +{ + Entry* entry = new Entry; + entry->mNode = aNode; + entry->mState = aState; + entry->mNext = mTop; + + mTop = entry; + + ++mDepth; + return NS_OK; +} + +nsresult +XULContentSinkImpl::ContextStack::Pop(State* aState) +{ + if (mDepth == 0) + return NS_ERROR_UNEXPECTED; + + Entry* entry = mTop; + mTop = mTop->mNext; + --mDepth; + + *aState = entry->mState; + delete entry; + + return NS_OK; +} + + +nsresult +XULContentSinkImpl::ContextStack::GetTopNode(RefPtr<nsXULPrototypeNode>& aNode) +{ + if (mDepth == 0) + return NS_ERROR_UNEXPECTED; + + aNode = mTop->mNode; + return NS_OK; +} + + +nsresult +XULContentSinkImpl::ContextStack::GetTopChildren(nsPrototypeArray** aChildren) +{ + if (mDepth == 0) + return NS_ERROR_UNEXPECTED; + + *aChildren = &(mTop->mChildren); + return NS_OK; +} + +void +XULContentSinkImpl::ContextStack::Clear() +{ + Entry *cur = mTop; + while (cur) { + // Release the root element (and its descendants). + Entry *next = cur->mNext; + delete cur; + cur = next; + } + + mTop = nullptr; + mDepth = 0; +} + +void +XULContentSinkImpl::ContextStack::Traverse(nsCycleCollectionTraversalCallback& aCb) +{ + nsCycleCollectionTraversalCallback& cb = aCb; + for (ContextStack::Entry* tmp = mTop; tmp; tmp = tmp->mNext) { + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNode) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildren) + } +} + +//---------------------------------------------------------------------- + + +XULContentSinkImpl::XULContentSinkImpl() + : mText(nullptr), + mTextLength(0), + mTextSize(0), + mConstrainSize(true), + mState(eInProlog) +{ +} + + +XULContentSinkImpl::~XULContentSinkImpl() +{ + // The context stack _should_ be empty, unless something has gone wrong. + NS_ASSERTION(mContextStack.Depth() == 0, "Context stack not empty?"); + mContextStack.Clear(); + + free(mText); +} + +//---------------------------------------------------------------------- +// nsISupports interface + +NS_IMPL_CYCLE_COLLECTION_CLASS(XULContentSinkImpl) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(XULContentSinkImpl) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mNodeInfoManager) + tmp->mContextStack.Clear(); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrototype) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mParser) +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(XULContentSinkImpl) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfoManager) + tmp->mContextStack.Traverse(cb); + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrototype) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XULContentSinkImpl) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXMLContentSink) + NS_INTERFACE_MAP_ENTRY(nsIXMLContentSink) + NS_INTERFACE_MAP_ENTRY(nsIExpatSink) + NS_INTERFACE_MAP_ENTRY(nsIContentSink) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(XULContentSinkImpl) +NS_IMPL_CYCLE_COLLECTING_RELEASE(XULContentSinkImpl) + +//---------------------------------------------------------------------- +// nsIContentSink interface + +NS_IMETHODIMP +XULContentSinkImpl::WillBuildModel(nsDTDMode aDTDMode) +{ +#if FIXME + if (! mParentContentSink) { + // If we're _not_ an overlay, then notify the document that + // the load is beginning. + mDocument->BeginLoad(); + } +#endif + + return NS_OK; +} + +NS_IMETHODIMP +XULContentSinkImpl::DidBuildModel(bool aTerminated) +{ + nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument); + if (doc) { + doc->EndLoad(); + mDocument = nullptr; + } + + // Drop our reference to the parser to get rid of a circular + // reference. + mParser = nullptr; + return NS_OK; +} + +NS_IMETHODIMP +XULContentSinkImpl::WillInterrupt(void) +{ + // XXX Notify the docshell, if necessary + return NS_OK; +} + +NS_IMETHODIMP +XULContentSinkImpl::WillResume(void) +{ + // XXX Notify the docshell, if necessary + return NS_OK; +} + +NS_IMETHODIMP +XULContentSinkImpl::SetParser(nsParserBase* aParser) +{ + mParser = aParser; + return NS_OK; +} + +NS_IMETHODIMP +XULContentSinkImpl::SetDocumentCharset(nsACString& aCharset) +{ + nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument); + if (doc) { + doc->SetDocumentCharacterSet(aCharset); + } + + return NS_OK; +} + +nsISupports * +XULContentSinkImpl::GetTarget() +{ + nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument); + return doc; +} + +//---------------------------------------------------------------------- + +nsresult +XULContentSinkImpl::Init(nsIDocument* aDocument, + nsXULPrototypeDocument* aPrototype) +{ + NS_PRECONDITION(aDocument != nullptr, "null ptr"); + if (! aDocument) + return NS_ERROR_NULL_POINTER; + + nsresult rv; + + mDocument = do_GetWeakReference(aDocument); + mPrototype = aPrototype; + + mDocumentURL = mPrototype->GetURI(); + + // XXX this presumes HTTP header info is already set in document + // XXX if it isn't we need to set it here... + // XXXbz not like GetHeaderData on the proto doc _does_ anything.... + nsAutoString preferredStyle; + rv = mPrototype->GetHeaderData(nsGkAtoms::headerDefaultStyle, + preferredStyle); + if (NS_FAILED(rv)) return rv; + + if (!preferredStyle.IsEmpty()) { + aDocument->SetHeaderData(nsGkAtoms::headerDefaultStyle, + preferredStyle); + } + + // Set the right preferred style on the document's CSSLoader. + aDocument->CSSLoader()->SetPreferredSheet(preferredStyle); + + mNodeInfoManager = aPrototype->GetNodeInfoManager(); + if (! mNodeInfoManager) + return NS_ERROR_UNEXPECTED; + + mState = eInProlog; + return NS_OK; +} + + +//---------------------------------------------------------------------- +// +// Text buffering +// + +bool +XULContentSinkImpl::IsDataInBuffer(char16_t* buffer, int32_t length) +{ + for (int32_t i = 0; i < length; ++i) { + if (buffer[i] == ' ' || + buffer[i] == '\t' || + buffer[i] == '\n' || + buffer[i] == '\r') + continue; + + return true; + } + return false; +} + + +nsresult +XULContentSinkImpl::FlushText(bool aCreateTextNode) +{ + nsresult rv; + + do { + // Don't do anything if there's no text to create a node from, or + // if they've told us not to create a text node + if (! mTextLength) + break; + + if (! aCreateTextNode) + break; + + RefPtr<nsXULPrototypeNode> node; + rv = mContextStack.GetTopNode(node); + if (NS_FAILED(rv)) return rv; + + bool stripWhitespace = false; + if (node->mType == nsXULPrototypeNode::eType_Element) { + mozilla::dom::NodeInfo *nodeInfo = + static_cast<nsXULPrototypeElement*>(node.get())->mNodeInfo; + + if (nodeInfo->NamespaceEquals(kNameSpaceID_XUL)) + stripWhitespace = !nodeInfo->Equals(nsGkAtoms::label) && + !nodeInfo->Equals(nsGkAtoms::description); + } + + // Don't bother if there's nothing but whitespace. + if (stripWhitespace && ! IsDataInBuffer(mText, mTextLength)) + break; + + // Don't bother if we're not in XUL document body + if (mState != eInDocumentElement || mContextStack.Depth() == 0) + break; + + nsXULPrototypeText* text = new nsXULPrototypeText(); + text->mValue.Assign(mText, mTextLength); + if (stripWhitespace) + text->mValue.Trim(" \t\n\r"); + + // hook it up + nsPrototypeArray* children = nullptr; + rv = mContextStack.GetTopChildren(&children); + if (NS_FAILED(rv)) return rv; + + // transfer ownership of 'text' to the children array + children->AppendElement(text); + } while (0); + + // Reset our text buffer + mTextLength = 0; + return NS_OK; +} + +//---------------------------------------------------------------------- + +nsresult +XULContentSinkImpl::NormalizeAttributeString(const char16_t *aExpatName, + nsAttrName &aName) +{ + int32_t nameSpaceID; + nsCOMPtr<nsIAtom> prefix, localName; + nsContentUtils::SplitExpatName(aExpatName, getter_AddRefs(prefix), + getter_AddRefs(localName), &nameSpaceID); + + if (nameSpaceID == kNameSpaceID_None) { + aName.SetTo(localName); + + return NS_OK; + } + + RefPtr<mozilla::dom::NodeInfo> ni; + ni = mNodeInfoManager->GetNodeInfo(localName, prefix, + nameSpaceID, + nsIDOMNode::ATTRIBUTE_NODE); + aName.SetTo(ni); + + return NS_OK; +} + +nsresult +XULContentSinkImpl::CreateElement(mozilla::dom::NodeInfo *aNodeInfo, + nsXULPrototypeElement** aResult) +{ + nsXULPrototypeElement* element = new nsXULPrototypeElement(); + element->mNodeInfo = aNodeInfo; + + *aResult = element; + return NS_OK; +} + +/**** BEGIN NEW APIs ****/ + + +NS_IMETHODIMP +XULContentSinkImpl::HandleStartElement(const char16_t *aName, + const char16_t **aAtts, + uint32_t aAttsCount, + uint32_t aLineNumber) +{ + // XXX Hopefully the parser will flag this before we get here. If + // we're in the epilog, there should be no new elements + NS_PRECONDITION(mState != eInEpilog, "tag in XUL doc epilog"); + NS_PRECONDITION(aAttsCount % 2 == 0, "incorrect aAttsCount"); + // Adjust aAttsCount so it's the actual number of attributes + aAttsCount /= 2; + + if (mState == eInEpilog) + return NS_ERROR_UNEXPECTED; + + if (mState != eInScript) { + FlushText(); + } + + int32_t nameSpaceID; + nsCOMPtr<nsIAtom> prefix, localName; + nsContentUtils::SplitExpatName(aName, getter_AddRefs(prefix), + getter_AddRefs(localName), &nameSpaceID); + + RefPtr<mozilla::dom::NodeInfo> nodeInfo; + nodeInfo = mNodeInfoManager->GetNodeInfo(localName, prefix, nameSpaceID, + nsIDOMNode::ELEMENT_NODE); + + nsresult rv = NS_OK; + switch (mState) { + case eInProlog: + // We're the root document element + rv = OpenRoot(aAtts, aAttsCount, nodeInfo); + break; + + case eInDocumentElement: + rv = OpenTag(aAtts, aAttsCount, aLineNumber, nodeInfo); + break; + + case eInEpilog: + case eInScript: + MOZ_LOG(gContentSinkLog, LogLevel::Warning, + ("xul: warning: unexpected tags in epilog at line %d", + aLineNumber)); + rv = NS_ERROR_UNEXPECTED; // XXX + break; + } + + return rv; +} + +NS_IMETHODIMP +XULContentSinkImpl::HandleEndElement(const char16_t *aName) +{ + // Never EVER return anything but NS_OK or + // NS_ERROR_HTMLPARSER_BLOCK from this method. Doing so will blow + // the parser's little mind all over the planet. + nsresult rv; + + RefPtr<nsXULPrototypeNode> node; + rv = mContextStack.GetTopNode(node); + + if (NS_FAILED(rv)) { + return NS_OK; + } + + switch (node->mType) { + case nsXULPrototypeNode::eType_Element: { + // Flush any text _now_, so that we'll get text nodes created + // before popping the stack. + FlushText(); + + // Pop the context stack and do prototype hookup. + nsPrototypeArray* children = nullptr; + rv = mContextStack.GetTopChildren(&children); + if (NS_FAILED(rv)) return rv; + + nsXULPrototypeElement* element = + static_cast<nsXULPrototypeElement*>(node.get()); + + int32_t count = children->Length(); + if (count) { + element->mChildren.SetCapacity(count); + + for (int32_t i = 0; i < count; ++i) + element->mChildren.AppendElement(children->ElementAt(i)); + + } + } + break; + + case nsXULPrototypeNode::eType_Script: { + nsXULPrototypeScript* script = + static_cast<nsXULPrototypeScript*>(node.get()); + + // If given a src= attribute, we must ignore script tag content. + if (!script->mSrcURI && !script->HasScriptObject()) { + nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument); + + script->mOutOfLine = false; + if (doc) + script->Compile(mText, mTextLength, mDocumentURL, + script->mLineNo, doc); + } + + FlushText(false); + } + break; + + default: + NS_ERROR("didn't expect that"); + break; + } + + rv = mContextStack.Pop(&mState); + NS_ASSERTION(NS_SUCCEEDED(rv), "context stack corrupted"); + if (NS_FAILED(rv)) return rv; + + if (mContextStack.Depth() == 0) { + // The root element should -always- be an element, because + // it'll have been created via XULContentSinkImpl::OpenRoot(). + NS_ASSERTION(node->mType == nsXULPrototypeNode::eType_Element, "root is not an element"); + if (node->mType != nsXULPrototypeNode::eType_Element) + return NS_ERROR_UNEXPECTED; + + // Now that we're done parsing, set the prototype document's + // root element. This transfers ownership of the prototype + // element tree to the prototype document. + nsXULPrototypeElement* element = + static_cast<nsXULPrototypeElement*>(node.get()); + + mPrototype->SetRootElement(element); + mState = eInEpilog; + } + + return NS_OK; +} + +NS_IMETHODIMP +XULContentSinkImpl::HandleComment(const char16_t *aName) +{ + FlushText(); + return NS_OK; +} + +NS_IMETHODIMP +XULContentSinkImpl::HandleCDataSection(const char16_t *aData, uint32_t aLength) +{ + FlushText(); + return AddText(aData, aLength); +} + +NS_IMETHODIMP +XULContentSinkImpl::HandleDoctypeDecl(const nsAString & aSubset, + const nsAString & aName, + const nsAString & aSystemId, + const nsAString & aPublicId, + nsISupports* aCatalogData) +{ + return NS_OK; +} + +NS_IMETHODIMP +XULContentSinkImpl::HandleCharacterData(const char16_t *aData, + uint32_t aLength) +{ + if (aData && mState != eInProlog && mState != eInEpilog) { + return AddText(aData, aLength); + } + return NS_OK; +} + +NS_IMETHODIMP +XULContentSinkImpl::HandleProcessingInstruction(const char16_t *aTarget, + const char16_t *aData) +{ + FlushText(); + + const nsDependentString target(aTarget); + const nsDependentString data(aData); + + // Note: the created nsXULPrototypePI has mRefCnt == 1 + RefPtr<nsXULPrototypePI> pi = new nsXULPrototypePI(); + pi->mTarget = target; + pi->mData = data; + + if (mState == eInProlog) { + // Note: passing in already addrefed pi + return mPrototype->AddProcessingInstruction(pi); + } + + nsresult rv; + nsPrototypeArray* children = nullptr; + rv = mContextStack.GetTopChildren(&children); + if (NS_FAILED(rv)) { + return rv; + } + + if (!children->AppendElement(pi)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + return NS_OK; +} + + +NS_IMETHODIMP +XULContentSinkImpl::HandleXMLDeclaration(const char16_t *aVersion, + const char16_t *aEncoding, + int32_t aStandalone) +{ + return NS_OK; +} + + +NS_IMETHODIMP +XULContentSinkImpl::ReportError(const char16_t* aErrorText, + const char16_t* aSourceText, + nsIScriptError *aError, + bool *_retval) +{ + NS_PRECONDITION(aError && aSourceText && aErrorText, "Check arguments!!!"); + + // The expat driver should report the error. + *_retval = true; + + nsresult rv = NS_OK; + + // make sure to empty the context stack so that + // <parsererror> could become the root element. + mContextStack.Clear(); + + mState = eInProlog; + + // Clear any buffered-up text we have. It's enough to set the length to 0. + // The buffer itself is allocated when we're created and deleted in our + // destructor, so don't mess with it. + mTextLength = 0; + + // return leaving the document empty if we're asked to not add a <parsererror> root node + nsCOMPtr<nsIDocument> idoc = do_QueryReferent(mDocument); + if (idoc && idoc->SuppressParserErrorElement()) { + return NS_OK; + }; + + nsCOMPtr<nsIXULDocument> doc = do_QueryReferent(mDocument); + if (doc && !doc->OnDocumentParserError()) { + // The overlay was broken. Don't add a messy element to the master doc. + return NS_OK; + } + + const char16_t* noAtts[] = { 0, 0 }; + + NS_NAMED_LITERAL_STRING(errorNs, + "http://www.mozilla.org/newlayout/xml/parsererror.xml"); + + nsAutoString parsererror(errorNs); + parsererror.Append((char16_t)0xFFFF); + parsererror.AppendLiteral("parsererror"); + + rv = HandleStartElement(parsererror.get(), noAtts, 0, 0); + NS_ENSURE_SUCCESS(rv,rv); + + rv = HandleCharacterData(aErrorText, NS_strlen(aErrorText)); + NS_ENSURE_SUCCESS(rv,rv); + + nsAutoString sourcetext(errorNs); + sourcetext.Append((char16_t)0xFFFF); + sourcetext.AppendLiteral("sourcetext"); + + rv = HandleStartElement(sourcetext.get(), noAtts, 0, 0); + NS_ENSURE_SUCCESS(rv,rv); + + rv = HandleCharacterData(aSourceText, NS_strlen(aSourceText)); + NS_ENSURE_SUCCESS(rv,rv); + + rv = HandleEndElement(sourcetext.get()); + NS_ENSURE_SUCCESS(rv,rv); + + rv = HandleEndElement(parsererror.get()); + NS_ENSURE_SUCCESS(rv,rv); + + return rv; +} + +nsresult +XULContentSinkImpl::OpenRoot(const char16_t** aAttributes, + const uint32_t aAttrLen, + mozilla::dom::NodeInfo *aNodeInfo) +{ + NS_ASSERTION(mState == eInProlog, "how'd we get here?"); + if (mState != eInProlog) + return NS_ERROR_UNEXPECTED; + + nsresult rv; + + if (aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML) || + aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XUL)) { + MOZ_LOG(gContentSinkLog, LogLevel::Error, + ("xul: script tag not allowed as root content element")); + + return NS_ERROR_UNEXPECTED; + } + + // Create the element + nsXULPrototypeElement* element; + rv = CreateElement(aNodeInfo, &element); + + if (NS_FAILED(rv)) { + if (MOZ_LOG_TEST(gContentSinkLog, LogLevel::Error)) { + nsAutoString anodeC; + aNodeInfo->GetName(anodeC); + MOZ_LOG(gContentSinkLog, LogLevel::Error, + ("xul: unable to create element '%s' at line %d", + NS_ConvertUTF16toUTF8(anodeC).get(), + -1)); // XXX pass in line number + } + + return rv; + } + + // Push the element onto the context stack, so that child + // containers will hook up to us as their parent. + rv = mContextStack.Push(element, mState); + if (NS_FAILED(rv)) { + element->Release(); + return rv; + } + + // Add the attributes + rv = AddAttributes(aAttributes, aAttrLen, element); + if (NS_FAILED(rv)) return rv; + + mState = eInDocumentElement; + return NS_OK; +} + +nsresult +XULContentSinkImpl::OpenTag(const char16_t** aAttributes, + const uint32_t aAttrLen, + const uint32_t aLineNumber, + mozilla::dom::NodeInfo *aNodeInfo) +{ + nsresult rv; + + // Create the element + nsXULPrototypeElement* element; + rv = CreateElement(aNodeInfo, &element); + + if (NS_FAILED(rv)) { + if (MOZ_LOG_TEST(gContentSinkLog, LogLevel::Error)) { + nsAutoString anodeC; + aNodeInfo->GetName(anodeC); + MOZ_LOG(gContentSinkLog, LogLevel::Error, + ("xul: unable to create element '%s' at line %d", + NS_ConvertUTF16toUTF8(anodeC).get(), + aLineNumber)); + } + + return rv; + } + + // Link this element to its parent. + nsPrototypeArray* children = nullptr; + rv = mContextStack.GetTopChildren(&children); + if (NS_FAILED(rv)) { + delete element; + return rv; + } + + // Add the attributes + rv = AddAttributes(aAttributes, aAttrLen, element); + if (NS_FAILED(rv)) return rv; + + children->AppendElement(element); + + if (aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XHTML) || + aNodeInfo->Equals(nsGkAtoms::script, kNameSpaceID_XUL)) { + // Do scripty things now + rv = OpenScript(aAttributes, aLineNumber); + NS_ENSURE_SUCCESS(rv, rv); + + NS_ASSERTION(mState == eInScript || mState == eInDocumentElement, + "Unexpected state"); + if (mState == eInScript) { + // OpenScript has pushed the nsPrototypeScriptElement onto the + // stack, so we're done. + return NS_OK; + } + } + + // Push the element onto the context stack, so that child + // containers will hook up to us as their parent. + rv = mContextStack.Push(element, mState); + if (NS_FAILED(rv)) return rv; + + mState = eInDocumentElement; + return NS_OK; +} + +nsresult +XULContentSinkImpl::OpenScript(const char16_t** aAttributes, + const uint32_t aLineNumber) +{ + bool isJavaScript = true; + uint32_t version = JSVERSION_LATEST; + nsresult rv; + + // Look for SRC attribute and look for a LANGUAGE attribute + nsAutoString src; + while (*aAttributes) { + const nsDependentString key(aAttributes[0]); + if (key.EqualsLiteral("src")) { + src.Assign(aAttributes[1]); + } else if (key.EqualsLiteral("type")) { + nsDependentString str(aAttributes[1]); + nsContentTypeParser parser(str); + nsAutoString mimeType; + rv = parser.GetType(mimeType); + if (NS_FAILED(rv)) { + if (rv == NS_ERROR_INVALID_ARG) { + // Fail immediately rather than checking if later things + // are okay. + return NS_OK; + } + // We do want the warning here + NS_ENSURE_SUCCESS(rv, rv); + } + + if (nsContentUtils::IsJavascriptMIMEType(mimeType)) { + isJavaScript = true; + version = JSVERSION_LATEST; + + // Get the version string, and ensure that JavaScript supports it. + nsAutoString versionName; + rv = parser.GetParameter("version", versionName); + + if (NS_SUCCEEDED(rv)) { + version = nsContentUtils::ParseJavascriptVersion(versionName); + } else if (rv != NS_ERROR_INVALID_ARG) { + return rv; + } + } else { + isJavaScript = false; + } + } else if (key.EqualsLiteral("language")) { + // Language is deprecated, and the impl in nsScriptLoader ignores the + // various version strings anyway. So we make no attempt to support + // languages other than JS for language= + nsAutoString lang(aAttributes[1]); + if (nsContentUtils::IsJavaScriptLanguage(lang)) { + isJavaScript = true; + version = JSVERSION_DEFAULT; + } + } + aAttributes += 2; + } + + // Don't process scripts that aren't JavaScript. + if (!isJavaScript) { + return NS_OK; + } + + nsCOMPtr<nsIDocument> doc(do_QueryReferent(mDocument)); + nsCOMPtr<nsIScriptGlobalObject> globalObject; + if (doc) + globalObject = do_QueryInterface(doc->GetWindow()); + RefPtr<nsXULPrototypeScript> script = + new nsXULPrototypeScript(aLineNumber, version); + + // If there is a SRC attribute... + if (! src.IsEmpty()) { + // Use the SRC attribute value to load the URL + rv = NS_NewURI(getter_AddRefs(script->mSrcURI), src, nullptr, mDocumentURL); + + // Check if this document is allowed to load a script from this source + // NOTE: if we ever allow scripts added via the DOM to run, we need to + // add a CheckLoadURI call for that as well. + if (NS_SUCCEEDED(rv)) { + if (!mSecMan) + mSecMan = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr<nsIDocument> doc = do_QueryReferent(mDocument, &rv); + + if (NS_SUCCEEDED(rv)) { + rv = mSecMan-> + CheckLoadURIWithPrincipal(doc->NodePrincipal(), + script->mSrcURI, + nsIScriptSecurityManager::ALLOW_CHROME); + } + } + } + + if (NS_FAILED(rv)) { + return rv; + } + + // Attempt to deserialize an out-of-line script from the FastLoad + // file right away. Otherwise we'll end up reloading the script and + // corrupting the FastLoad file trying to serialize it, in the case + // where it's already there. + script->DeserializeOutOfLine(nullptr, mPrototype); + } + + nsPrototypeArray* children = nullptr; + rv = mContextStack.GetTopChildren(&children); + if (NS_FAILED(rv)) { + return rv; + } + + children->AppendElement(script); + + mConstrainSize = false; + + mContextStack.Push(script, mState); + mState = eInScript; + + return NS_OK; +} + +nsresult +XULContentSinkImpl::AddAttributes(const char16_t** aAttributes, + const uint32_t aAttrLen, + nsXULPrototypeElement* aElement) +{ + // Add tag attributes to the element + nsresult rv; + + // Create storage for the attributes + nsXULPrototypeAttribute* attrs = nullptr; + if (aAttrLen > 0) { + attrs = new nsXULPrototypeAttribute[aAttrLen]; + } + + aElement->mAttributes = attrs; + aElement->mNumAttributes = aAttrLen; + + // Copy the attributes into the prototype + uint32_t i; + for (i = 0; i < aAttrLen; ++i) { + rv = NormalizeAttributeString(aAttributes[i * 2], attrs[i].mName); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aElement->SetAttrAt(i, nsDependentString(aAttributes[i * 2 + 1]), + mDocumentURL); + NS_ENSURE_SUCCESS(rv, rv); + + if (MOZ_LOG_TEST(gContentSinkLog, LogLevel::Debug)) { + nsAutoString extraWhiteSpace; + int32_t cnt = mContextStack.Depth(); + while (--cnt >= 0) + extraWhiteSpace.AppendLiteral(" "); + nsAutoString qnameC,valueC; + qnameC.Assign(aAttributes[0]); + valueC.Assign(aAttributes[1]); + MOZ_LOG(gContentSinkLog, LogLevel::Debug, + ("xul: %.5d. %s %s=%s", + -1, // XXX pass in line number + NS_ConvertUTF16toUTF8(extraWhiteSpace).get(), + NS_ConvertUTF16toUTF8(qnameC).get(), + NS_ConvertUTF16toUTF8(valueC).get())); + } + } + + return NS_OK; +} + +nsresult +XULContentSinkImpl::AddText(const char16_t* aText, + int32_t aLength) +{ + // Create buffer when we first need it + if (0 == mTextSize) { + mText = (char16_t *) malloc(sizeof(char16_t) * 4096); + if (nullptr == mText) { + return NS_ERROR_OUT_OF_MEMORY; + } + mTextSize = 4096; + } + + // Copy data from string into our buffer; flush buffer when it fills up + int32_t offset = 0; + while (0 != aLength) { + int32_t amount = mTextSize - mTextLength; + if (amount > aLength) { + amount = aLength; + } + if (0 == amount) { + if (mConstrainSize) { + nsresult rv = FlushText(); + if (NS_OK != rv) { + return rv; + } + } else { + CheckedInt32 size = mTextSize; + size += aLength; + if (!size.isValid()) { + return NS_ERROR_OUT_OF_MEMORY; + } + mTextSize = size.value(); + + mText = (char16_t *) realloc(mText, sizeof(char16_t) * mTextSize); + if (nullptr == mText) { + return NS_ERROR_OUT_OF_MEMORY; + } + } + } + memcpy(&mText[mTextLength],aText + offset, sizeof(char16_t) * amount); + + mTextLength += amount; + offset += amount; + aLength -= amount; + } + + return NS_OK; +} diff --git a/dom/xul/nsXULContentSink.h b/dom/xul/nsXULContentSink.h new file mode 100644 index 000000000..ff38cad60 --- /dev/null +++ b/dom/xul/nsXULContentSink.h @@ -0,0 +1,150 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsXULContentSink_h__ +#define nsXULContentSink_h__ + +#include "mozilla/Attributes.h" +#include "nsIExpatSink.h" +#include "nsIXMLContentSink.h" +#include "nsNodeInfoManager.h" +#include "nsWeakPtr.h" +#include "nsXULElement.h" +#include "nsIDTD.h" + +class nsIDocument; +class nsIScriptSecurityManager; +class nsAttrName; +class nsXULPrototypeDocument; +class nsXULPrototypeElement; +class nsXULPrototypeNode; + +class XULContentSinkImpl final : public nsIXMLContentSink, + public nsIExpatSink +{ +public: + XULContentSinkImpl(); + + // nsISupports + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_NSIEXPATSINK + + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(XULContentSinkImpl, nsIXMLContentSink) + + // nsIContentSink + NS_IMETHOD WillParse(void) override { return NS_OK; } + NS_IMETHOD WillBuildModel(nsDTDMode aDTDMode) override; + NS_IMETHOD DidBuildModel(bool aTerminated) override; + NS_IMETHOD WillInterrupt(void) override; + NS_IMETHOD WillResume(void) override; + NS_IMETHOD SetParser(nsParserBase* aParser) override; + virtual void FlushPendingNotifications(mozFlushType aType) override { } + NS_IMETHOD SetDocumentCharset(nsACString& aCharset) override; + virtual nsISupports *GetTarget() override; + + /** + * Initialize the content sink, giving it an nsIDocument object + * with which to communicate with the outside world, and an + * nsXULPrototypeDocument to build. + */ + nsresult Init(nsIDocument* aDocument, nsXULPrototypeDocument* aPrototype); + +protected: + virtual ~XULContentSinkImpl(); + + // pseudo-constants + char16_t* mText; + int32_t mTextLength; + int32_t mTextSize; + bool mConstrainSize; + + nsresult AddAttributes(const char16_t** aAttributes, + const uint32_t aAttrLen, + nsXULPrototypeElement* aElement); + + nsresult OpenRoot(const char16_t** aAttributes, + const uint32_t aAttrLen, + mozilla::dom::NodeInfo *aNodeInfo); + + nsresult OpenTag(const char16_t** aAttributes, + const uint32_t aAttrLen, + const uint32_t aLineNumber, + mozilla::dom::NodeInfo *aNodeInfo); + + // If OpenScript returns NS_OK and after it returns our state is eInScript, + // that means that we created a prototype script and stuck it on + // mContextStack. If NS_OK is returned but the state is still + // eInDocumentElement then we didn't create a prototype script (e.g. the + // script had an unknown type), and the caller should create a prototype + // element. + nsresult OpenScript(const char16_t** aAttributes, + const uint32_t aLineNumber); + + static bool IsDataInBuffer(char16_t* aBuffer, int32_t aLength); + + // Text management + nsresult FlushText(bool aCreateTextNode = true); + nsresult AddText(const char16_t* aText, int32_t aLength); + + + RefPtr<nsNodeInfoManager> mNodeInfoManager; + + nsresult NormalizeAttributeString(const char16_t *aExpatName, + nsAttrName &aName); + nsresult CreateElement(mozilla::dom::NodeInfo *aNodeInfo, + nsXULPrototypeElement** aResult); + + + public: + enum State { eInProlog, eInDocumentElement, eInScript, eInEpilog }; + protected: + + State mState; + + // content stack management + class ContextStack { + protected: + struct Entry { + RefPtr<nsXULPrototypeNode> mNode; + // a LOT of nodes have children; preallocate for 8 + nsPrototypeArray mChildren; + State mState; + Entry* mNext; + Entry() : mChildren(8) {} + }; + + Entry* mTop; + int32_t mDepth; + + public: + ContextStack(); + ~ContextStack(); + + int32_t Depth() { return mDepth; } + + nsresult Push(nsXULPrototypeNode* aNode, State aState); + nsresult Pop(State* aState); + + nsresult GetTopNode(RefPtr<nsXULPrototypeNode>& aNode); + nsresult GetTopChildren(nsPrototypeArray** aChildren); + + void Clear(); + + void Traverse(nsCycleCollectionTraversalCallback& aCallback); + }; + + friend class ContextStack; + ContextStack mContextStack; + + nsWeakPtr mDocument; // [OWNER] + nsCOMPtr<nsIURI> mDocumentURL; // [OWNER] + + RefPtr<nsXULPrototypeDocument> mPrototype; // [OWNER] + + RefPtr<nsParserBase> mParser; + nsCOMPtr<nsIScriptSecurityManager> mSecMan; +}; + +#endif /* nsXULContentSink_h__ */ diff --git a/dom/xul/nsXULControllers.cpp b/dom/xul/nsXULControllers.cpp new file mode 100644 index 000000000..6b795b29a --- /dev/null +++ b/dom/xul/nsXULControllers.cpp @@ -0,0 +1,246 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + + This file provides the implementation for the XUL "controllers" + object. + +*/ + +#include "nsString.h" + +#include "nsIControllers.h" +#include "nsIDOMElement.h" +#include "nsXULControllers.h" +#include "nsDOMClassInfoID.h" +#include "nsIController.h" + +//---------------------------------------------------------------------- + +nsXULControllers::nsXULControllers() +: mCurControllerID(0) +{ +} + +nsXULControllers::~nsXULControllers(void) +{ + DeleteControllers(); +} + +void +nsXULControllers::DeleteControllers() +{ + uint32_t count = mControllers.Length(); + for (uint32_t i = 0; i < count; i++) + { + nsXULControllerData* controllerData = mControllers.ElementAt(i); + delete controllerData; // releases the nsIController + } + + mControllers.Clear(); +} + + +nsresult +NS_NewXULControllers(nsISupports* aOuter, REFNSIID aIID, void** aResult) +{ + NS_PRECONDITION(aOuter == nullptr, "no aggregation"); + if (aOuter) + return NS_ERROR_NO_AGGREGATION; + + nsXULControllers* controllers = new nsXULControllers(); + nsresult rv; + NS_ADDREF(controllers); + rv = controllers->QueryInterface(aIID, aResult); + NS_RELEASE(controllers); + return rv; +} + +NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULControllers) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULControllers) + tmp->DeleteControllers(); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULControllers) + { + uint32_t i, count = tmp->mControllers.Length(); + for (i = 0; i < count; ++i) { + nsXULControllerData* controllerData = tmp->mControllers[i]; + if (controllerData) { + cb.NoteXPCOMChild(controllerData->mController); + } + } + } +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULControllers) + NS_INTERFACE_MAP_ENTRY(nsIControllers) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIControllers) + NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(XULControllers) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULControllers) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULControllers) + +NS_IMETHODIMP +nsXULControllers::GetControllerForCommand(const char *aCommand, nsIController** _retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + *_retval = nullptr; + + uint32_t count = mControllers.Length(); + for (uint32_t i=0; i < count; i++) + { + nsXULControllerData* controllerData = mControllers.ElementAt(i); + if (controllerData) + { + nsCOMPtr<nsIController> controller; + controllerData->GetController(getter_AddRefs(controller)); + if (controller) + { + bool supportsCommand; + controller->SupportsCommand(aCommand, &supportsCommand); + if (supportsCommand) { + controller.forget(_retval); + return NS_OK; + } + } + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXULControllers::InsertControllerAt(uint32_t aIndex, nsIController *controller) +{ + nsXULControllerData* controllerData = new nsXULControllerData(++mCurControllerID, controller); +#ifdef DEBUG + nsXULControllerData** inserted = +#endif + mControllers.InsertElementAt(aIndex, controllerData); + NS_ASSERTION(inserted != nullptr, "Insertion of controller failed"); + return NS_OK; +} + +NS_IMETHODIMP +nsXULControllers::RemoveControllerAt(uint32_t aIndex, nsIController **_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + *_retval = nullptr; + + nsXULControllerData* controllerData = mControllers.SafeElementAt(aIndex); + if (!controllerData) return NS_ERROR_FAILURE; + + mControllers.RemoveElementAt(aIndex); + + controllerData->GetController(_retval); + delete controllerData; + + return NS_OK; +} + + +NS_IMETHODIMP +nsXULControllers::GetControllerAt(uint32_t aIndex, nsIController **_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + *_retval = nullptr; + + nsXULControllerData* controllerData = mControllers.SafeElementAt(aIndex); + if (!controllerData) return NS_ERROR_FAILURE; + + return controllerData->GetController(_retval); // does the addref +} + +NS_IMETHODIMP +nsXULControllers::AppendController(nsIController *controller) +{ + // This assigns controller IDs starting at 1 so we can use 0 to test if an ID was obtained + nsXULControllerData* controllerData = new nsXULControllerData(++mCurControllerID, controller); + +#ifdef DEBUG + nsXULControllerData** appended = +#endif + mControllers.AppendElement(controllerData); + NS_ASSERTION(appended != nullptr, "Appending controller failed"); + return NS_OK; +} + +NS_IMETHODIMP +nsXULControllers::RemoveController(nsIController *controller) +{ + // first get the identity pointer + nsCOMPtr<nsISupports> controllerSup(do_QueryInterface(controller)); + // then find it + uint32_t count = mControllers.Length(); + for (uint32_t i = 0; i < count; i++) + { + nsXULControllerData* controllerData = mControllers.ElementAt(i); + if (controllerData) + { + nsCOMPtr<nsIController> thisController; + controllerData->GetController(getter_AddRefs(thisController)); + nsCOMPtr<nsISupports> thisControllerSup(do_QueryInterface(thisController)); // get identity + if (thisControllerSup == controllerSup) + { + mControllers.RemoveElementAt(i); + delete controllerData; + return NS_OK; + } + } + } + return NS_ERROR_FAILURE; // right thing to return if no controller found? +} + +NS_IMETHODIMP +nsXULControllers::GetControllerId(nsIController *controller, uint32_t *_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + + uint32_t count = mControllers.Length(); + for (uint32_t i = 0; i < count; i++) + { + nsXULControllerData* controllerData = mControllers.ElementAt(i); + if (controllerData) + { + nsCOMPtr<nsIController> thisController; + controllerData->GetController(getter_AddRefs(thisController)); + if (thisController.get() == controller) + { + *_retval = controllerData->GetControllerID(); + return NS_OK; + } + } + } + return NS_ERROR_FAILURE; // none found +} + +NS_IMETHODIMP +nsXULControllers::GetControllerById(uint32_t controllerID, nsIController **_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + + uint32_t count = mControllers.Length(); + for (uint32_t i = 0; i < count; i++) + { + nsXULControllerData* controllerData = mControllers.ElementAt(i); + if (controllerData && controllerData->GetControllerID() == controllerID) + { + return controllerData->GetController(_retval); + } + } + return NS_ERROR_FAILURE; // none found +} + +NS_IMETHODIMP +nsXULControllers::GetControllerCount(uint32_t *_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + *_retval = mControllers.Length(); + return NS_OK; +} + diff --git a/dom/xul/nsXULControllers.h b/dom/xul/nsXULControllers.h new file mode 100644 index 000000000..103b4aa91 --- /dev/null +++ b/dom/xul/nsXULControllers.h @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + + The XUL "controllers" object. + +*/ + +#ifndef nsXULControllers_h__ +#define nsXULControllers_h__ + +#include "nsCOMPtr.h" +#include "nsTArray.h" +#include "nsWeakPtr.h" +#include "nsIControllers.h" +#include "nsCycleCollectionParticipant.h" + +/* non-XPCOM class for holding controllers and their IDs */ +class nsXULControllerData +{ +public: + nsXULControllerData(uint32_t inControllerID, nsIController* inController) + : mControllerID(inControllerID) + , mController(inController) + { + } + + ~nsXULControllerData() {} + + uint32_t GetControllerID() { return mControllerID; } + + nsresult GetController(nsIController **outController) + { + NS_IF_ADDREF(*outController = mController); + return NS_OK; + } + + uint32_t mControllerID; + nsCOMPtr<nsIController> mController; +}; + + +nsresult NS_NewXULControllers(nsISupports* aOuter, REFNSIID aIID, void** aResult); + +class nsXULControllers : public nsIControllers +{ +public: + friend nsresult + NS_NewXULControllers(nsISupports* aOuter, REFNSIID aIID, void** aResult); + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXULControllers, nsIControllers) + NS_DECL_NSICONTROLLERS + +protected: + nsXULControllers(); + virtual ~nsXULControllers(void); + + void DeleteControllers(); + + nsTArray<nsXULControllerData*> mControllers; + uint32_t mCurControllerID; +}; + + + + +#endif // nsXULControllers_h__ diff --git a/dom/xul/nsXULElement.cpp b/dom/xul/nsXULElement.cpp new file mode 100644 index 000000000..14fa898ab --- /dev/null +++ b/dom/xul/nsXULElement.cpp @@ -0,0 +1,2975 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This Original Code has been modified by IBM Corporation. + * Modifications made by IBM described herein are + * Copyright (c) International Business Machines + * Corporation, 2000 + * + * Modifications to Mozilla code or documentation + * identified per MPL Section 3.3 + * + * Date Modified by Description of modification + * 03/27/2000 IBM Corp. Added PR_CALLBACK for Optlink + * use in OS2 + */ + +#include "nsCOMPtr.h" +#include "nsDOMCID.h" +#include "nsError.h" +#include "nsDOMString.h" +#include "nsIDOMEvent.h" +#include "nsIAtom.h" +#include "nsIBaseWindow.h" +#include "nsIDOMAttr.h" +#include "nsIDOMDocument.h" +#include "nsIDOMElement.h" +#include "nsIDOMEventListener.h" +#include "nsIDOMNodeList.h" +#include "nsIDOMXULCommandDispatcher.h" +#include "nsIDOMXULElement.h" +#include "nsIDOMXULSelectCntrlItemEl.h" +#include "nsIDocument.h" +#include "nsLayoutStylesheetCache.h" +#include "mozilla/AsyncEventDispatcher.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/EventListenerManager.h" +#include "mozilla/EventStateManager.h" +#include "mozilla/EventStates.h" +#include "mozilla/DeclarationBlockInlines.h" +#include "nsFocusManager.h" +#include "nsHTMLStyleSheet.h" +#include "nsNameSpaceManager.h" +#include "nsIObjectInputStream.h" +#include "nsIObjectOutputStream.h" +#include "nsIPresShell.h" +#include "nsIPrincipal.h" +#include "nsIRDFCompositeDataSource.h" +#include "nsIRDFNode.h" +#include "nsIRDFService.h" +#include "nsIScriptContext.h" +#include "nsIScriptError.h" +#include "nsIScriptSecurityManager.h" +#include "nsIServiceManager.h" +#include "mozilla/css/StyleRule.h" +#include "nsIURL.h" +#include "nsViewManager.h" +#include "nsIWidget.h" +#include "nsIXULDocument.h" +#include "nsIXULTemplateBuilder.h" +#include "nsLayoutCID.h" +#include "nsContentCID.h" +#include "mozilla/dom/Event.h" +#include "nsRDFCID.h" +#include "nsStyleConsts.h" +#include "nsXPIDLString.h" +#include "nsXULControllers.h" +#include "nsIBoxObject.h" +#include "nsPIBoxObject.h" +#include "XULDocument.h" +#include "nsXULPopupListener.h" +#include "nsRuleWalker.h" +#include "nsIDOMCSSStyleDeclaration.h" +#include "nsCSSParser.h" +#include "ListBoxObject.h" +#include "nsContentUtils.h" +#include "nsContentList.h" +#include "mozilla/InternalMutationEvent.h" +#include "mozilla/MouseEvents.h" +#include "nsIDOMMutationEvent.h" +#include "nsPIDOMWindow.h" +#include "nsJSPrincipals.h" +#include "nsDOMAttributeMap.h" +#include "nsGkAtoms.h" +#include "nsXULContentUtils.h" +#include "nsNodeUtils.h" +#include "nsFrameLoader.h" +#include "mozilla/Logging.h" +#include "rdf.h" +#include "nsIControllers.h" +#include "nsAttrValueOrString.h" +#include "nsAttrValueInlines.h" +#include "mozilla/Attributes.h" +#include "nsIController.h" +#include "nsQueryObject.h" +#include <algorithm> +#include "nsIDOMChromeWindow.h" + +// The XUL doc interface +#include "nsIDOMXULDocument.h" + +#include "nsReadableUtils.h" +#include "nsIFrame.h" +#include "nsNodeInfoManager.h" +#include "nsXBLBinding.h" +#include "mozilla/EventDispatcher.h" +#include "mozAutoDocUpdate.h" +#include "nsIDOMXULCommandEvent.h" +#include "nsCCUncollectableMarker.h" +#include "nsICSSDeclaration.h" + +#include "mozilla/dom/XULElementBinding.h" +#include "mozilla/dom/BoxObject.h" +#include "mozilla/dom/HTMLIFrameElement.h" + +using namespace mozilla; +using namespace mozilla::dom; + +#ifdef XUL_PROTOTYPE_ATTRIBUTE_METERING +uint32_t nsXULPrototypeAttribute::gNumElements; +uint32_t nsXULPrototypeAttribute::gNumAttributes; +uint32_t nsXULPrototypeAttribute::gNumCacheTests; +uint32_t nsXULPrototypeAttribute::gNumCacheHits; +uint32_t nsXULPrototypeAttribute::gNumCacheSets; +uint32_t nsXULPrototypeAttribute::gNumCacheFills; +#endif + +class nsXULElementTearoff final : public nsIFrameLoaderOwner +{ + ~nsXULElementTearoff() {} + +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_CLASS(nsXULElementTearoff) + + explicit nsXULElementTearoff(nsXULElement* aElement) + : mElement(aElement) + { + } + + NS_FORWARD_NSIFRAMELOADEROWNER(static_cast<nsXULElement*>(mElement.get())->) +private: + nsCOMPtr<nsIDOMXULElement> mElement; +}; + +NS_IMPL_CYCLE_COLLECTION(nsXULElementTearoff, mElement) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULElementTearoff) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULElementTearoff) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULElementTearoff) + NS_INTERFACE_MAP_ENTRY(nsIFrameLoaderOwner) +NS_INTERFACE_MAP_END_AGGREGATED(mElement) + +//---------------------------------------------------------------------- +// nsXULElement +// + +nsXULElement::nsXULElement(already_AddRefed<mozilla::dom::NodeInfo> aNodeInfo) + : nsStyledElement(aNodeInfo), + mBindingParent(nullptr) +{ + XUL_PROTOTYPE_ATTRIBUTE_METER(gNumElements); + + // We may be READWRITE by default; check. + if (IsReadWriteTextElement()) { + AddStatesSilently(NS_EVENT_STATE_MOZ_READWRITE); + RemoveStatesSilently(NS_EVENT_STATE_MOZ_READONLY); + } +} + +nsXULElement::~nsXULElement() +{ +} + +nsXULElement::nsXULSlots::nsXULSlots() + : nsXULElement::nsDOMSlots() +{ +} + +nsXULElement::nsXULSlots::~nsXULSlots() +{ + NS_IF_RELEASE(mControllers); // Forces release + nsCOMPtr<nsIFrameLoader> frameLoader = do_QueryInterface(mFrameLoaderOrOpener); + if (frameLoader) { + static_cast<nsFrameLoader*>(frameLoader.get())->Destroy(); + } +} + +void +nsXULElement::nsXULSlots::Traverse(nsCycleCollectionTraversalCallback &cb) +{ + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mFrameLoaderOrOpener"); + cb.NoteXPCOMChild(mFrameLoaderOrOpener); +} + +nsINode::nsSlots* +nsXULElement::CreateSlots() +{ + return new nsXULSlots(); +} + +void +nsXULElement::MaybeUpdatePrivateLifetime() +{ + if (AttrValueIs(kNameSpaceID_None, nsGkAtoms::windowtype, + NS_LITERAL_STRING("navigator:browser"), + eCaseMatters)) { + return; + } + + nsPIDOMWindowOuter* win = OwnerDoc()->GetWindow(); + nsCOMPtr<nsIDocShell> docShell = win ? win->GetDocShell() : nullptr; + if (docShell) { + docShell->SetAffectPrivateSessionLifetime(false); + } +} + +/* static */ +already_AddRefed<nsXULElement> +nsXULElement::Create(nsXULPrototypeElement* aPrototype, mozilla::dom::NodeInfo *aNodeInfo, + bool aIsScriptable, bool aIsRoot) +{ + RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo; + RefPtr<nsXULElement> element = new nsXULElement(ni.forget()); + if (element) { + if (aPrototype->mHasIdAttribute) { + element->SetHasID(); + } + if (aPrototype->mHasClassAttribute) { + element->SetFlags(NODE_MAY_HAVE_CLASS); + } + if (aPrototype->mHasStyleAttribute) { + element->SetMayHaveStyle(); + } + + element->MakeHeavyweight(aPrototype); + if (aIsScriptable) { + // Check each attribute on the prototype to see if we need to do + // any additional processing and hookup that would otherwise be + // done 'automagically' by SetAttr(). + for (uint32_t i = 0; i < aPrototype->mNumAttributes; ++i) { + element->AddListenerFor(aPrototype->mAttributes[i].mName, + true); + } + } + + if (aIsRoot && aPrototype->mNodeInfo->Equals(nsGkAtoms::window)) { + for (uint32_t i = 0; i < aPrototype->mNumAttributes; ++i) { + if (aPrototype->mAttributes[i].mName.Equals(nsGkAtoms::windowtype)) { + element->MaybeUpdatePrivateLifetime(); + } + } + } + } + + return element.forget(); +} + +nsresult +nsXULElement::Create(nsXULPrototypeElement* aPrototype, + nsIDocument* aDocument, + bool aIsScriptable, + bool aIsRoot, + Element** aResult) +{ + // Create an nsXULElement from a prototype + NS_PRECONDITION(aPrototype != nullptr, "null ptr"); + if (! aPrototype) + return NS_ERROR_NULL_POINTER; + + NS_PRECONDITION(aResult != nullptr, "null ptr"); + if (! aResult) + return NS_ERROR_NULL_POINTER; + + RefPtr<mozilla::dom::NodeInfo> nodeInfo; + if (aDocument) { + mozilla::dom::NodeInfo* ni = aPrototype->mNodeInfo; + nodeInfo = aDocument->NodeInfoManager()-> + GetNodeInfo(ni->NameAtom(), ni->GetPrefixAtom(), ni->NamespaceID(), + nsIDOMNode::ELEMENT_NODE); + } else { + nodeInfo = aPrototype->mNodeInfo; + } + + RefPtr<nsXULElement> element = Create(aPrototype, nodeInfo, + aIsScriptable, aIsRoot); + element.forget(aResult); + + return NS_OK; +} + +nsresult +NS_NewXULElement(Element** aResult, already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) +{ + RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo; + + NS_PRECONDITION(ni, "need nodeinfo for non-proto Create"); + + nsIDocument* doc = ni->GetDocument(); + if (doc && !doc->AllowXULXBL()) { + return NS_ERROR_NOT_AVAILABLE; + } + + NS_ADDREF(*aResult = new nsXULElement(ni.forget())); + + return NS_OK; +} + +void +NS_TrustedNewXULElement(nsIContent** aResult, + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) +{ + RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo; + NS_PRECONDITION(ni, "need nodeinfo for non-proto Create"); + + // Create an nsXULElement with the specified namespace and tag. + NS_ADDREF(*aResult = new nsXULElement(ni.forget())); +} + +//---------------------------------------------------------------------- +// nsISupports interface + +NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULElement) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXULElement, + nsStyledElement) + { + nsXULSlots* slots = static_cast<nsXULSlots*>(tmp->GetExistingSlots()); + if (slots) { + slots->Traverse(cb); + } + } +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsXULElement, + nsStyledElement) + // Why aren't we unlinking the prototype? + tmp->ClearHasID(); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_ADDREF_INHERITED(nsXULElement, nsStyledElement) +NS_IMPL_RELEASE_INHERITED(nsXULElement, nsStyledElement) + +NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsXULElement) + NS_INTERFACE_TABLE_INHERITED(nsXULElement, nsIDOMNode, nsIDOMElement, + nsIDOMXULElement) + NS_ELEMENT_INTERFACE_TABLE_TO_MAP_SEGUE + NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIFrameLoaderOwner, + new nsXULElementTearoff(this)) +NS_INTERFACE_MAP_END_INHERITING(nsStyledElement) + +//---------------------------------------------------------------------- +// nsIDOMNode interface + +nsresult +nsXULElement::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const +{ + *aResult = nullptr; + + RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo; + RefPtr<nsXULElement> element = new nsXULElement(ni.forget()); + + // XXX TODO: set up RDF generic builder n' stuff if there is a + // 'datasources' attribute? This is really kind of tricky, + // because then we'd need to -selectively- copy children that + // -weren't- generated from RDF. Ugh. Forget it. + + // Note that we're _not_ copying mControllers. + + uint32_t count = mAttrsAndChildren.AttrCount(); + nsresult rv = NS_OK; + for (uint32_t i = 0; i < count; ++i) { + const nsAttrName* originalName = mAttrsAndChildren.AttrNameAt(i); + const nsAttrValue* originalValue = mAttrsAndChildren.AttrAt(i); + nsAttrValue attrValue; + + // Style rules need to be cloned. + if (originalValue->Type() == nsAttrValue::eCSSDeclaration) { + DeclarationBlock* decl = originalValue->GetCSSDeclarationValue(); + RefPtr<css::Declaration> + declClone = new css::Declaration(*decl->AsGecko()); + + nsString stringValue; + originalValue->ToString(stringValue); + + attrValue.SetTo(declClone.forget(), &stringValue); + } else { + attrValue.SetTo(*originalValue); + } + + if (originalName->IsAtom()) { + rv = element->mAttrsAndChildren.SetAndSwapAttr(originalName->Atom(), + attrValue); + } else { + rv = element->mAttrsAndChildren.SetAndSwapAttr(originalName->NodeInfo(), + attrValue); + } + NS_ENSURE_SUCCESS(rv, rv); + element->AddListenerFor(*originalName, true); + if (originalName->Equals(nsGkAtoms::id) && + !originalValue->IsEmptyString()) { + element->SetHasID(); + } + if (originalName->Equals(nsGkAtoms::_class)) { + element->SetFlags(NODE_MAY_HAVE_CLASS); + } + if (originalName->Equals(nsGkAtoms::style)) { + element->SetMayHaveStyle(); + } + } + + element.forget(aResult); + return rv; +} + +//---------------------------------------------------------------------- + +NS_IMETHODIMP +nsXULElement::GetElementsByAttribute(const nsAString& aAttribute, + const nsAString& aValue, + nsIDOMNodeList** aReturn) +{ + *aReturn = GetElementsByAttribute(aAttribute, aValue).take(); + return NS_OK; +} + +already_AddRefed<nsINodeList> +nsXULElement::GetElementsByAttribute(const nsAString& aAttribute, + const nsAString& aValue) +{ + nsCOMPtr<nsIAtom> attrAtom(NS_Atomize(aAttribute)); + void* attrValue = new nsString(aValue); + RefPtr<nsContentList> list = + new nsContentList(this, + XULDocument::MatchAttribute, + nsContentUtils::DestroyMatchString, + attrValue, + true, + attrAtom, + kNameSpaceID_Unknown); + return list.forget(); +} + +NS_IMETHODIMP +nsXULElement::GetElementsByAttributeNS(const nsAString& aNamespaceURI, + const nsAString& aAttribute, + const nsAString& aValue, + nsIDOMNodeList** aReturn) +{ + ErrorResult rv; + *aReturn = + GetElementsByAttributeNS(aNamespaceURI, aAttribute, aValue, rv).take(); + return rv.StealNSResult(); +} + +already_AddRefed<nsINodeList> +nsXULElement::GetElementsByAttributeNS(const nsAString& aNamespaceURI, + const nsAString& aAttribute, + const nsAString& aValue, + ErrorResult& rv) +{ + nsCOMPtr<nsIAtom> attrAtom(NS_Atomize(aAttribute)); + + int32_t nameSpaceId = kNameSpaceID_Wildcard; + if (!aNamespaceURI.EqualsLiteral("*")) { + rv = + nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI, + nameSpaceId); + if (rv.Failed()) { + return nullptr; + } + } + + void* attrValue = new nsString(aValue); + RefPtr<nsContentList> list = + new nsContentList(this, + XULDocument::MatchAttribute, + nsContentUtils::DestroyMatchString, + attrValue, + true, + attrAtom, + nameSpaceId); + + return list.forget(); +} + +EventListenerManager* +nsXULElement::GetEventListenerManagerForAttr(nsIAtom* aAttrName, bool* aDefer) +{ + // XXXbz sXBL/XBL2 issue: should we instead use GetComposedDoc() + // here, override BindToTree for those classes and munge event + // listeners there? + nsIDocument* doc = OwnerDoc(); + + nsPIDOMWindowInner *window; + Element *root = doc->GetRootElement(); + if ((!root || root == this) && !mNodeInfo->Equals(nsGkAtoms::overlay) && + (window = doc->GetInnerWindow())) { + + nsCOMPtr<EventTarget> piTarget = do_QueryInterface(window); + + *aDefer = false; + return piTarget->GetOrCreateListenerManager(); + } + + return nsStyledElement::GetEventListenerManagerForAttr(aAttrName, aDefer); +} + +// returns true if the element is not a list +static bool IsNonList(mozilla::dom::NodeInfo* aNodeInfo) +{ + return !aNodeInfo->Equals(nsGkAtoms::tree) && + !aNodeInfo->Equals(nsGkAtoms::listbox) && + !aNodeInfo->Equals(nsGkAtoms::richlistbox); +} + +bool +nsXULElement::IsFocusableInternal(int32_t *aTabIndex, bool aWithMouse) +{ + /* + * Returns true if an element may be focused, and false otherwise. The inout + * argument aTabIndex will be set to the tab order index to be used; -1 for + * elements that should not be part of the tab order and a greater value to + * indicate its tab order. + * + * Confusingly, the supplied value for the aTabIndex argument may indicate + * whether the element may be focused as a result of the -moz-user-focus + * property, where -1 means no and 0 means yes. + * + * For controls, the element cannot be focused and is not part of the tab + * order if it is disabled. + * + * Controls (those that implement nsIDOMXULControlElement): + * *aTabIndex = -1 no tabindex Not focusable or tabbable + * *aTabIndex = -1 tabindex="-1" Not focusable or tabbable + * *aTabIndex = -1 tabindex=">=0" Focusable and tabbable + * *aTabIndex >= 0 no tabindex Focusable and tabbable + * *aTabIndex >= 0 tabindex="-1" Focusable but not tabbable + * *aTabIndex >= 0 tabindex=">=0" Focusable and tabbable + * Non-controls: + * *aTabIndex = -1 Not focusable or tabbable + * *aTabIndex >= 0 Focusable and tabbable + * + * If aTabIndex is null, then the tabindex is not computed, and + * true is returned for non-disabled controls and false otherwise. + */ + + // elements are not focusable by default + bool shouldFocus = false; + +#ifdef XP_MACOSX + // on Mac, mouse interactions only focus the element if it's a list, + // or if it's a remote target, since the remote target must handle + // the focus. + if (aWithMouse && + IsNonList(mNodeInfo) && + !EventStateManager::IsRemoteTarget(this)) + { + return false; + } +#endif + + nsCOMPtr<nsIDOMXULControlElement> xulControl = do_QueryObject(this); + if (xulControl) { + // a disabled element cannot be focused and is not part of the tab order + bool disabled; + xulControl->GetDisabled(&disabled); + if (disabled) { + if (aTabIndex) + *aTabIndex = -1; + return false; + } + shouldFocus = true; + } + + if (aTabIndex) { + if (xulControl) { + if (HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)) { + // if either the aTabIndex argument or a specified tabindex is non-negative, + // the element becomes focusable. + int32_t tabIndex = 0; + xulControl->GetTabIndex(&tabIndex); + shouldFocus = *aTabIndex >= 0 || tabIndex >= 0; + *aTabIndex = tabIndex; + } else { + // otherwise, if there is no tabindex attribute, just use the value of + // *aTabIndex to indicate focusability. Reset any supplied tabindex to 0. + shouldFocus = *aTabIndex >= 0; + if (shouldFocus) + *aTabIndex = 0; + } + + if (shouldFocus && sTabFocusModelAppliesToXUL && + !(sTabFocusModel & eTabFocus_formElementsMask)) { + // By default, the tab focus model doesn't apply to xul element on any system but OS X. + // on OS X we're following it for UI elements (XUL) as sTabFocusModel is based on + // "Full Keyboard Access" system setting (see mac/nsILookAndFeel). + // both textboxes and list elements (i.e. trees and list) should always be focusable + // (textboxes are handled as html:input) + // For compatibility, we only do this for controls, otherwise elements like <browser> + // cannot take this focus. + if (IsNonList(mNodeInfo)) + *aTabIndex = -1; + } + } else { + shouldFocus = *aTabIndex >= 0; + } + } + + return shouldFocus; +} + +bool +nsXULElement::PerformAccesskey(bool aKeyCausesActivation, + bool aIsTrustedEvent) +{ + nsCOMPtr<nsIContent> content(this); + + if (IsXULElement(nsGkAtoms::label)) { + nsCOMPtr<nsIDOMElement> element; + + nsAutoString control; + GetAttr(kNameSpaceID_None, nsGkAtoms::control, control); + if (!control.IsEmpty()) { + //XXXsmaug Should we use ShadowRoot::GetElementById in case + // content is in Shadow DOM? + nsCOMPtr<nsIDOMDocument> domDocument = + do_QueryInterface(content->GetUncomposedDoc()); + if (domDocument) + domDocument->GetElementById(control, getter_AddRefs(element)); + } + // here we'll either change |content| to the element referenced by + // |element|, or clear it. + content = do_QueryInterface(element); + + if (!content) { + return false; + } + } + + nsIFrame* frame = content->GetPrimaryFrame(); + if (!frame || !frame->IsVisibleConsideringAncestors()) { + return false; + } + + bool focused = false; + nsXULElement* elm = FromContent(content); + if (elm) { + // Define behavior for each type of XUL element. + if (!content->IsXULElement(nsGkAtoms::toolbarbutton)) { + nsIFocusManager* fm = nsFocusManager::GetFocusManager(); + if (fm) { + nsCOMPtr<nsIDOMElement> elementToFocus; + // for radio buttons, focus the radiogroup instead + if (content->IsXULElement(nsGkAtoms::radio)) { + nsCOMPtr<nsIDOMXULSelectControlItemElement> controlItem(do_QueryInterface(content)); + if (controlItem) { + bool disabled; + controlItem->GetDisabled(&disabled); + if (!disabled) { + nsCOMPtr<nsIDOMXULSelectControlElement> selectControl; + controlItem->GetControl(getter_AddRefs(selectControl)); + elementToFocus = do_QueryInterface(selectControl); + } + } + } else { + elementToFocus = do_QueryInterface(content); + } + if (elementToFocus) { + fm->SetFocus(elementToFocus, nsIFocusManager::FLAG_BYKEY); + + // Return true if the element became focused. + nsPIDOMWindowOuter* window = OwnerDoc()->GetWindow(); + focused = (window && window->GetFocusedNode()); + } + } + } + if (aKeyCausesActivation && + !content->IsAnyOfXULElements(nsGkAtoms::textbox, nsGkAtoms::menulist)) { + elm->ClickWithInputSource(nsIDOMMouseEvent::MOZ_SOURCE_KEYBOARD, aIsTrustedEvent); + } + } else { + return content->PerformAccesskey(aKeyCausesActivation, aIsTrustedEvent); + } + + return focused; +} + +//---------------------------------------------------------------------- + +void +nsXULElement::AddListenerFor(const nsAttrName& aName, + bool aCompileEventHandlers) +{ + // If appropriate, add a popup listener and/or compile the event + // handler. Called when we change the element's document, create a + // new element, change an attribute's value, etc. + // Eventlistenener-attributes are always in the null namespace + if (aName.IsAtom()) { + nsIAtom *attr = aName.Atom(); + MaybeAddPopupListener(attr); + if (aCompileEventHandlers && + nsContentUtils::IsEventAttributeName(attr, EventNameType_XUL)) { + nsAutoString value; + GetAttr(kNameSpaceID_None, attr, value); + SetEventHandler(attr, value, true); + } + } +} + +void +nsXULElement::MaybeAddPopupListener(nsIAtom* aLocalName) +{ + // If appropriate, add a popup listener. Called when we change the + // element's document, create a new element, change an attribute's + // value, etc. + if (aLocalName == nsGkAtoms::menu || + aLocalName == nsGkAtoms::contextmenu || + // XXXdwh popup and context are deprecated + aLocalName == nsGkAtoms::popup || + aLocalName == nsGkAtoms::context) { + AddPopupListener(aLocalName); + } +} + +//---------------------------------------------------------------------- +// +// nsIContent interface +// +void +nsXULElement::UpdateEditableState(bool aNotify) +{ + // Don't call through to Element here because the things + // it does don't work for cases when we're an editable control. + nsIContent *parent = GetParent(); + + SetEditableFlag(parent && parent->HasFlag(NODE_IS_EDITABLE)); + UpdateState(aNotify); +} + +/** + * Returns true if the user-agent style sheet rules for this XUL element are + * in minimal-xul.css instead of xul.css. + */ +static inline bool XULElementsRulesInMinimalXULSheet(nsIAtom* aTag) +{ + return // scrollbar parts: + aTag == nsGkAtoms::scrollbar || + aTag == nsGkAtoms::scrollbarbutton || + aTag == nsGkAtoms::scrollcorner || + aTag == nsGkAtoms::slider || + aTag == nsGkAtoms::thumb || + aTag == nsGkAtoms::scale || + // other + aTag == nsGkAtoms::resizer || + aTag == nsGkAtoms::label || + aTag == nsGkAtoms::videocontrols; +} + +#ifdef DEBUG +/** + * Returns true if aElement is a XUL element created by the video controls + * binding. HTML <video> and <audio> bindings pull in this binding. This + * binding creates lots of different types of XUL elements. + */ +static inline bool +IsInVideoControls(nsXULElement* aElement) +{ + nsIContent* ancestor = aElement->GetParent(); + while (ancestor) { + if (ancestor->NodeInfo()->Equals(nsGkAtoms::videocontrols, kNameSpaceID_XUL)) { + return true; + } + ancestor = ancestor->GetParent(); + } + return false; +} + +/** + * Returns true if aElement is an element created by the <binding + * id="feedreaderUI"> binding or one of the bindings bound to such an element. + * element in one of the binding for such an element. Only + * subscribe.xhtml#feedSubscribeLine pulls in the feedreaderUI binding. This + * binding creates lots of different types of XUL elements. + */ +bool +IsInFeedSubscribeLine(nsXULElement* aElement) +{ + nsIContent* bindingParent = aElement->GetBindingParent(); + if (bindingParent) { + while (bindingParent->GetBindingParent()) { + bindingParent = bindingParent->GetBindingParent(); + } + nsIAtom* idAtom = bindingParent->GetID(); + if (idAtom && idAtom->Equals(NS_LITERAL_STRING("feedSubscribeLine"))) { + return true; + } + } + return false; +} +#endif + +class XULInContentErrorReporter : public Runnable +{ +public: + explicit XULInContentErrorReporter(nsIDocument* aDocument) : mDocument(aDocument) {} + + NS_IMETHOD Run() override + { + mDocument->WarnOnceAbout(nsIDocument::eImportXULIntoContent, false); + return NS_OK; + } + +private: + nsCOMPtr<nsIDocument> mDocument; +}; + +nsresult +nsXULElement::BindToTree(nsIDocument* aDocument, + nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandlers) +{ + if (!aBindingParent && + aDocument && + !aDocument->IsLoadedAsInteractiveData() && + !aDocument->AllowXULXBL() && + !aDocument->HasWarnedAbout(nsIDocument::eImportXULIntoContent)) { + nsContentUtils::AddScriptRunner(new XULInContentErrorReporter(aDocument)); + } + + nsresult rv = nsStyledElement::BindToTree(aDocument, aParent, + aBindingParent, + aCompileEventHandlers); + NS_ENSURE_SUCCESS(rv, rv); + + nsIDocument* doc = GetComposedDoc(); + if (doc && + !doc->LoadsFullXULStyleSheetUpFront() && + !doc->IsUnstyledDocument()) { + + // To save CPU cycles and memory, non-XUL documents only load the user + // agent style sheet rules for a minimal set of XUL elements such as + // 'scrollbar' that may be created implicitly for their content (those + // rules being in minimal-xul.css). This is where we make sure that all + // the other XUL UA style sheet rules (xul.css) have been loaded if the + // minimal set is not sufficient. + // + // We do this during binding, not element construction, because elements + // can be moved from the document that creates them to another document. + + if (!XULElementsRulesInMinimalXULSheet(NodeInfo()->NameAtom())) { + auto cache = nsLayoutStylesheetCache::For(doc->GetStyleBackendType()); + doc->EnsureOnDemandBuiltInUASheet(cache->XULSheet()); + // To keep memory usage down it is important that we try and avoid + // pulling xul.css into non-XUL documents. That should be very rare, and + // for HTML we currently should only pull it in if the document contains + // an <audio> or <video> element. This assertion is here to make sure + // that we don't fail to notice if a change to bindings causes us to + // start pulling in xul.css much more frequently. If this assertion + // fails then we need to figure out why, and how we can continue to avoid + // pulling in xul.css. + // Note that add-ons may introduce bindings that cause this assertion to + // fire. + NS_ASSERTION(IsInVideoControls(this) || + IsInFeedSubscribeLine(this) || + IsXULElement(nsGkAtoms::datetimebox), + "Unexpected XUL element in non-XUL doc"); + } + } + + if (aDocument) { + NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(), + "Missing a script blocker!"); + // We're in a document now. Kick off the frame load. + LoadSrc(); + } + + return rv; +} + +void +nsXULElement::UnbindFromTree(bool aDeep, bool aNullParent) +{ + // mControllers can own objects that are implemented + // in JavaScript (such as some implementations of + // nsIControllers. These objects prevent their global + // object's script object from being garbage collected, + // which means JS continues to hold an owning reference + // to the nsGlobalWindow, which owns the document, + // which owns this content. That's a cycle, so we break + // it here. (It might be better to break this by releasing + // mDocument in nsGlobalWindow::SetDocShell, but I'm not + // sure whether that would fix all possible cycles through + // mControllers.) + nsXULSlots* slots = static_cast<nsXULSlots*>(GetExistingDOMSlots()); + if (slots) { + NS_IF_RELEASE(slots->mControllers); + RefPtr<nsFrameLoader> frameLoader = GetFrameLoader(); + if (frameLoader) { + frameLoader->Destroy(); + } + slots->mFrameLoaderOrOpener = nullptr; + } + + nsStyledElement::UnbindFromTree(aDeep, aNullParent); +} + +void +nsXULElement::RemoveChildAt(uint32_t aIndex, bool aNotify) +{ + nsCOMPtr<nsIContent> oldKid = mAttrsAndChildren.GetSafeChildAt(aIndex); + if (!oldKid) { + return; + } + + // On the removal of a <treeitem>, <treechildren>, or <treecell> element, + // the possibility exists that some of the items in the removed subtree + // are selected (and therefore need to be deselected). We need to account for this. + nsCOMPtr<nsIDOMXULMultiSelectControlElement> controlElement; + nsCOMPtr<nsIListBoxObject> listBox; + bool fireSelectionHandler = false; + + // -1 = do nothing, -2 = null out current item + // anything else = index to re-set as current + int32_t newCurrentIndex = -1; + + if (oldKid->NodeInfo()->Equals(nsGkAtoms::listitem, kNameSpaceID_XUL)) { + // This is the nasty case. We have (potentially) a slew of selected items + // and cells going away. + // First, retrieve the tree. + // Check first whether this element IS the tree + controlElement = do_QueryObject(this); + + // If it's not, look at our parent + if (!controlElement) + GetParentTree(getter_AddRefs(controlElement)); + nsCOMPtr<nsIDOMXULElement> xulElement(do_QueryInterface(controlElement)); + + nsCOMPtr<nsIDOMElement> oldKidElem = do_QueryInterface(oldKid); + if (xulElement && oldKidElem) { + // Iterate over all of the items and find out if they are contained inside + // the removed subtree. + int32_t length; + controlElement->GetSelectedCount(&length); + for (int32_t i = 0; i < length; i++) { + nsCOMPtr<nsIDOMXULSelectControlItemElement> node; + controlElement->MultiGetSelectedItem(i, getter_AddRefs(node)); + // we need to QI here to do an XPCOM-correct pointercompare + nsCOMPtr<nsIDOMElement> selElem = do_QueryInterface(node); + if (selElem == oldKidElem && + NS_SUCCEEDED(controlElement->RemoveItemFromSelection(node))) { + length--; + i--; + fireSelectionHandler = true; + } + } + + nsCOMPtr<nsIDOMXULSelectControlItemElement> curItem; + controlElement->GetCurrentItem(getter_AddRefs(curItem)); + nsCOMPtr<nsIContent> curNode = do_QueryInterface(curItem); + if (curNode && nsContentUtils::ContentIsDescendantOf(curNode, oldKid)) { + // Current item going away + nsCOMPtr<nsIBoxObject> box; + xulElement->GetBoxObject(getter_AddRefs(box)); + listBox = do_QueryInterface(box); + if (listBox && oldKidElem) { + listBox->GetIndexOfItem(oldKidElem, &newCurrentIndex); + } + + // If any of this fails, we'll just set the current item to null + if (newCurrentIndex == -1) + newCurrentIndex = -2; + } + } + } + + nsStyledElement::RemoveChildAt(aIndex, aNotify); + + if (newCurrentIndex == -2) { + controlElement->SetCurrentItem(nullptr); + } else if (newCurrentIndex > -1) { + // Make sure the index is still valid + int32_t treeRows; + listBox->GetRowCount(&treeRows); + if (treeRows > 0) { + newCurrentIndex = std::min((treeRows - 1), newCurrentIndex); + nsCOMPtr<nsIDOMElement> newCurrentItem; + listBox->GetItemAtIndex(newCurrentIndex, getter_AddRefs(newCurrentItem)); + nsCOMPtr<nsIDOMXULSelectControlItemElement> xulCurItem = do_QueryInterface(newCurrentItem); + if (xulCurItem) + controlElement->SetCurrentItem(xulCurItem); + } else { + controlElement->SetCurrentItem(nullptr); + } + } + + nsIDocument* doc; + if (fireSelectionHandler && (doc = GetComposedDoc())) { + nsContentUtils::DispatchTrustedEvent(doc, + static_cast<nsIContent*>(this), + NS_LITERAL_STRING("select"), + false, + true); + } +} + +void +nsXULElement::UnregisterAccessKey(const nsAString& aOldValue) +{ + // If someone changes the accesskey, unregister the old one + // + nsIDocument* doc = GetComposedDoc(); + if (doc && !aOldValue.IsEmpty()) { + nsIPresShell *shell = doc->GetShell(); + + if (shell) { + nsIContent *content = this; + + // find out what type of content node this is + if (mNodeInfo->Equals(nsGkAtoms::label)) { + // For anonymous labels the unregistering must + // occur on the binding parent control. + // XXXldb: And what if the binding parent is null? + content = GetBindingParent(); + } + + if (content) { + shell->GetPresContext()->EventStateManager()-> + UnregisterAccessKey(content, aOldValue.First()); + } + } + } +} + +nsresult +nsXULElement::BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, + nsAttrValueOrString* aValue, bool aNotify) +{ + if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::accesskey && + IsInUncomposedDoc()) { + nsAutoString oldValue; + if (GetAttr(aNamespaceID, aName, oldValue)) { + UnregisterAccessKey(oldValue); + } + } else if (aNamespaceID == kNameSpaceID_None && + (aName == nsGkAtoms::command || aName == nsGkAtoms::observes) && + IsInUncomposedDoc()) { +// XXX sXBL/XBL2 issue! Owner or current document? + nsAutoString oldValue; + GetAttr(kNameSpaceID_None, nsGkAtoms::observes, oldValue); + if (oldValue.IsEmpty()) { + GetAttr(kNameSpaceID_None, nsGkAtoms::command, oldValue); + } + + if (!oldValue.IsEmpty()) { + RemoveBroadcaster(oldValue); + } + } else if (aNamespaceID == kNameSpaceID_None && + aValue && + mNodeInfo->Equals(nsGkAtoms::window) && + aName == nsGkAtoms::chromemargin) { + nsAttrValue attrValue; + // Make sure the margin format is valid first + if (!attrValue.ParseIntMarginValue(aValue->String())) { + return NS_ERROR_INVALID_ARG; + } + } else if (aNamespaceID == kNameSpaceID_None && + aName == nsGkAtoms::usercontextid) { + nsAutoString oldValue; + bool hasAttribute = GetAttr(kNameSpaceID_None, nsGkAtoms::usercontextid, oldValue); + if (hasAttribute && (!aValue || !aValue->String().Equals(oldValue))) { + MOZ_ASSERT(false, "Changing usercontextid is not allowed."); + return NS_ERROR_INVALID_ARG; + } + } + + return nsStyledElement::BeforeSetAttr(aNamespaceID, aName, + aValue, aNotify); +} + +nsresult +nsXULElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, bool aNotify) +{ + if (aNamespaceID == kNameSpaceID_None) { + if (aValue) { + // Add popup and event listeners. We can't call AddListenerFor since + // the attribute isn't set yet. + MaybeAddPopupListener(aName); + if (nsContentUtils::IsEventAttributeName(aName, EventNameType_XUL)) { + if (aValue->Type() == nsAttrValue::eString) { + SetEventHandler(aName, aValue->GetStringValue(), true); + } else { + nsAutoString body; + aValue->ToString(body); + SetEventHandler(aName, body, true); + } + } + + nsIDocument* document = GetUncomposedDoc(); + + // Hide chrome if needed + if (mNodeInfo->Equals(nsGkAtoms::window)) { + if (aName == nsGkAtoms::hidechrome) { + HideWindowChrome( + aValue->Equals(NS_LITERAL_STRING("true"), eCaseMatters)); + } else if (aName == nsGkAtoms::chromemargin) { + SetChromeMargins(aValue); + } else if (aName == nsGkAtoms::windowtype && + document && document->GetRootElement() == this) { + MaybeUpdatePrivateLifetime(); + } + } + // title, (in)activetitlebarcolor and drawintitlebar are settable on + // any root node (windows, dialogs, etc) + if (document && document->GetRootElement() == this) { + if (aName == nsGkAtoms::title) { + document->NotifyPossibleTitleChange(false); + } else if ((aName == nsGkAtoms::activetitlebarcolor || + aName == nsGkAtoms::inactivetitlebarcolor)) { + nscolor color = NS_RGBA(0, 0, 0, 0); + if (aValue->Type() == nsAttrValue::eColor) { + aValue->GetColorValue(color); + } else { + nsAutoString tmp; + nsAttrValue attrValue; + aValue->ToString(tmp); + attrValue.ParseColor(tmp); + attrValue.GetColorValue(color); + } + SetTitlebarColor(color, aName == nsGkAtoms::activetitlebarcolor); + } else if (aName == nsGkAtoms::drawintitlebar) { + SetDrawsInTitlebar( + aValue->Equals(NS_LITERAL_STRING("true"), eCaseMatters)); + } else if (aName == nsGkAtoms::drawtitle) { + SetDrawsTitle( + aValue->Equals(NS_LITERAL_STRING("true"), eCaseMatters)); + } else if (aName == nsGkAtoms::localedir) { + // if the localedir changed on the root element, reset the document direction + nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(document); + if (xuldoc) { + xuldoc->ResetDocumentDirection(); + } + } else if (aName == nsGkAtoms::lwtheme || + aName == nsGkAtoms::lwthemetextcolor) { + // if the lwtheme changed, make sure to reset the document lwtheme cache + nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(document); + if (xuldoc) { + xuldoc->ResetDocumentLWTheme(); + UpdateBrightTitlebarForeground(document); + } + } else if (aName == nsGkAtoms::brighttitlebarforeground) { + UpdateBrightTitlebarForeground(document); + } + } + + if (aName == nsGkAtoms::src && document) { + LoadSrc(); + } + } else { + if (mNodeInfo->Equals(nsGkAtoms::window)) { + if (aName == nsGkAtoms::hidechrome) { + HideWindowChrome(false); + } else if (aName == nsGkAtoms::chromemargin) { + ResetChromeMargins(); + } + } + + nsIDocument* doc = GetUncomposedDoc(); + if (doc && doc->GetRootElement() == this) { + if ((aName == nsGkAtoms::activetitlebarcolor || + aName == nsGkAtoms::inactivetitlebarcolor)) { + // Use 0, 0, 0, 0 as the "none" color. + SetTitlebarColor(NS_RGBA(0, 0, 0, 0), aName == nsGkAtoms::activetitlebarcolor); + } else if (aName == nsGkAtoms::localedir) { + // if the localedir changed on the root element, reset the document direction + nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(doc); + if (xuldoc) { + xuldoc->ResetDocumentDirection(); + } + } else if ((aName == nsGkAtoms::lwtheme || + aName == nsGkAtoms::lwthemetextcolor)) { + // if the lwtheme changed, make sure to restyle appropriately + nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(doc); + if (xuldoc) { + xuldoc->ResetDocumentLWTheme(); + UpdateBrightTitlebarForeground(doc); + } + } else if (aName == nsGkAtoms::brighttitlebarforeground) { + UpdateBrightTitlebarForeground(doc); + } else if (aName == nsGkAtoms::drawintitlebar) { + SetDrawsInTitlebar(false); + } else if (aName == nsGkAtoms::drawtitle) { + SetDrawsTitle(false); + } + } + } + + // XXX need to check if they're changing an event handler: if + // so, then we need to unhook the old one. Or something. + } + + return nsStyledElement::AfterSetAttr(aNamespaceID, aName, + aValue, aNotify); +} + +bool +nsXULElement::ParseAttribute(int32_t aNamespaceID, + nsIAtom* aAttribute, + const nsAString& aValue, + nsAttrValue& aResult) +{ + // Parse into a nsAttrValue + if (!nsStyledElement::ParseAttribute(aNamespaceID, aAttribute, aValue, + aResult)) { + // Fall back to parsing as atom for short values + aResult.ParseStringOrAtom(aValue); + } + + return true; +} + +void +nsXULElement::RemoveBroadcaster(const nsAString & broadcasterId) +{ + nsCOMPtr<nsIDOMXULDocument> xuldoc = do_QueryInterface(OwnerDoc()); + if (xuldoc) { + nsCOMPtr<nsIDOMElement> broadcaster; + nsCOMPtr<nsIDOMDocument> domDoc (do_QueryInterface(xuldoc)); + domDoc->GetElementById(broadcasterId, getter_AddRefs(broadcaster)); + if (broadcaster) { + xuldoc->RemoveBroadcastListenerFor(broadcaster, this, + NS_LITERAL_STRING("*")); + } + } +} + +void +nsXULElement::DestroyContent() +{ + nsXULSlots* slots = static_cast<nsXULSlots*>(GetExistingDOMSlots()); + if (slots) { + NS_IF_RELEASE(slots->mControllers); + RefPtr<nsFrameLoader> frameLoader = GetFrameLoader(); + if (frameLoader) { + frameLoader->Destroy(); + } + slots->mFrameLoaderOrOpener = nullptr; + } + + nsStyledElement::DestroyContent(); +} + +#ifdef DEBUG +void +nsXULElement::List(FILE* out, int32_t aIndent) const +{ + nsCString prefix("XUL"); + if (HasSlots()) { + prefix.Append('*'); + } + prefix.Append(' '); + + nsStyledElement::List(out, aIndent, prefix); +} +#endif + +nsresult +nsXULElement::PreHandleEvent(EventChainPreVisitor& aVisitor) +{ + aVisitor.mForceContentDispatch = true; //FIXME! Bug 329119 + if (IsRootOfNativeAnonymousSubtree() && + (IsAnyOfXULElements(nsGkAtoms::scrollbar, nsGkAtoms::scrollcorner)) && + (aVisitor.mEvent->mMessage == eMouseClick || + aVisitor.mEvent->mMessage == eMouseDoubleClick || + aVisitor.mEvent->mMessage == eXULCommand || + aVisitor.mEvent->mMessage == eContextMenu || + aVisitor.mEvent->mMessage == eDragStart)) { + // Don't propagate these events from native anonymous scrollbar. + aVisitor.mCanHandle = true; + aVisitor.mParentTarget = nullptr; + return NS_OK; + } + if (aVisitor.mEvent->mMessage == eXULCommand && + aVisitor.mEvent->mClass == eInputEventClass && + aVisitor.mEvent->mOriginalTarget == static_cast<nsIContent*>(this) && + !IsXULElement(nsGkAtoms::command)) { + // Check that we really have an xul command event. That will be handled + // in a special way. + nsCOMPtr<nsIDOMXULCommandEvent> xulEvent = + do_QueryInterface(aVisitor.mDOMEvent); + // See if we have a command elt. If so, we execute on the command + // instead of on our content element. + nsAutoString command; + if (xulEvent && GetAttr(kNameSpaceID_None, nsGkAtoms::command, command) && + !command.IsEmpty()) { + // Stop building the event target chain for the original event. + // We don't want it to propagate to any DOM nodes. + aVisitor.mCanHandle = false; + aVisitor.mAutomaticChromeDispatch = false; + + // XXX sXBL/XBL2 issue! Owner or current document? + nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(GetUncomposedDoc())); + NS_ENSURE_STATE(domDoc); + nsCOMPtr<nsIDOMElement> commandElt; + domDoc->GetElementById(command, getter_AddRefs(commandElt)); + nsCOMPtr<nsIContent> commandContent(do_QueryInterface(commandElt)); + if (commandContent) { + // Create a new command event to dispatch to the element + // pointed to by the command attribute. The new event's + // sourceEvent will be the original command event that we're + // handling. + nsCOMPtr<nsIDOMEvent> domEvent = aVisitor.mDOMEvent; + while (domEvent) { + Event* event = domEvent->InternalDOMEvent(); + NS_ENSURE_STATE(!SameCOMIdentity(event->GetOriginalTarget(), + commandContent)); + nsCOMPtr<nsIDOMXULCommandEvent> commandEvent = + do_QueryInterface(domEvent); + if (commandEvent) { + commandEvent->GetSourceEvent(getter_AddRefs(domEvent)); + } else { + domEvent = nullptr; + } + } + + WidgetInputEvent* orig = aVisitor.mEvent->AsInputEvent(); + nsContentUtils::DispatchXULCommand( + commandContent, + aVisitor.mEvent->IsTrusted(), + aVisitor.mDOMEvent, + nullptr, + orig->IsControl(), + orig->IsAlt(), + orig->IsShift(), + orig->IsMeta()); + } else { + NS_WARNING("A XUL element is attached to a command that doesn't exist!\n"); + } + return NS_OK; + } + } + + return nsStyledElement::PreHandleEvent(aVisitor); +} + +// XXX This _should_ be an implementation method, _not_ publicly exposed :-( +NS_IMETHODIMP +nsXULElement::GetResource(nsIRDFResource** aResource) +{ + ErrorResult rv; + *aResource = GetResource(rv).take(); + return rv.StealNSResult(); +} + +already_AddRefed<nsIRDFResource> +nsXULElement::GetResource(ErrorResult& rv) +{ + nsAutoString id; + GetAttr(kNameSpaceID_None, nsGkAtoms::ref, id); + if (id.IsEmpty()) { + GetAttr(kNameSpaceID_None, nsGkAtoms::id, id); + } + + if (id.IsEmpty()) { + return nullptr; + } + + nsCOMPtr<nsIRDFResource> resource; + rv = nsXULContentUtils::RDFService()-> + GetUnicodeResource(id, getter_AddRefs(resource)); + return resource.forget(); +} + +NS_IMETHODIMP +nsXULElement::GetDatabase(nsIRDFCompositeDataSource** aDatabase) +{ + *aDatabase = GetDatabase().take(); + return NS_OK; +} + +already_AddRefed<nsIRDFCompositeDataSource> +nsXULElement::GetDatabase() +{ + nsCOMPtr<nsIXULTemplateBuilder> builder = GetBuilder(); + if (!builder) { + return nullptr; + } + + nsCOMPtr<nsIRDFCompositeDataSource> database; + builder->GetDatabase(getter_AddRefs(database)); + return database.forget(); +} + + +NS_IMETHODIMP +nsXULElement::GetBuilder(nsIXULTemplateBuilder** aBuilder) +{ + *aBuilder = GetBuilder().take(); + return NS_OK; +} + +already_AddRefed<nsIXULTemplateBuilder> +nsXULElement::GetBuilder() +{ + // XXX sXBL/XBL2 issue! Owner or current document? + nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(GetUncomposedDoc()); + if (!xuldoc) { + return nullptr; + } + + nsCOMPtr<nsIXULTemplateBuilder> builder; + xuldoc->GetTemplateBuilderFor(this, getter_AddRefs(builder)); + return builder.forget(); +} + +//---------------------------------------------------------------------- +// Implementation methods + +NS_IMETHODIMP +nsXULElement::WalkContentStyleRules(nsRuleWalker* aRuleWalker) +{ + return NS_OK; +} + +nsChangeHint +nsXULElement::GetAttributeChangeHint(const nsIAtom* aAttribute, + int32_t aModType) const +{ + nsChangeHint retval(nsChangeHint(0)); + + if (aAttribute == nsGkAtoms::value && + (aModType == nsIDOMMutationEvent::REMOVAL || + aModType == nsIDOMMutationEvent::ADDITION)) { + if (IsAnyOfXULElements(nsGkAtoms::label, nsGkAtoms::description)) + // Label and description dynamically morph between a normal + // block and a cropping single-line XUL text frame. If the + // value attribute is being added or removed, then we need to + // return a hint of frame change. (See bugzilla bug 95475 for + // details.) + retval = nsChangeHint_ReconstructFrame; + } else { + // if left or top changes we reflow. This will happen in xul + // containers that manage positioned children such as a stack. + if (nsGkAtoms::left == aAttribute || nsGkAtoms::top == aAttribute || + nsGkAtoms::right == aAttribute || nsGkAtoms::bottom == aAttribute || + nsGkAtoms::start == aAttribute || nsGkAtoms::end == aAttribute) + retval = NS_STYLE_HINT_REFLOW; + } + + return retval; +} + +NS_IMETHODIMP_(bool) +nsXULElement::IsAttributeMapped(const nsIAtom* aAttribute) const +{ + return false; +} + +// Controllers Methods +NS_IMETHODIMP +nsXULElement::GetControllers(nsIControllers** aResult) +{ + ErrorResult rv; + NS_IF_ADDREF(*aResult = GetControllers(rv)); + return rv.StealNSResult(); +} + +nsIControllers* +nsXULElement::GetControllers(ErrorResult& rv) +{ + if (! Controllers()) { + nsDOMSlots* slots = DOMSlots(); + + rv = NS_NewXULControllers(nullptr, NS_GET_IID(nsIControllers), + reinterpret_cast<void**>(&slots->mControllers)); + + NS_ASSERTION(!rv.Failed(), "unable to create a controllers"); + if (rv.Failed()) { + return nullptr; + } + } + + return Controllers(); +} + +NS_IMETHODIMP +nsXULElement::GetBoxObject(nsIBoxObject** aResult) +{ + ErrorResult rv; + *aResult = GetBoxObject(rv).take(); + return rv.StealNSResult(); +} + +already_AddRefed<BoxObject> +nsXULElement::GetBoxObject(ErrorResult& rv) +{ + // XXX sXBL/XBL2 issue! Owner or current document? + return OwnerDoc()->GetBoxObjectFor(this, rv); +} + +// Methods for setting/getting attributes from nsIDOMXULElement +#define NS_IMPL_XUL_STRING_ATTR(_method, _atom) \ + NS_IMETHODIMP \ + nsXULElement::Get##_method(nsAString& aReturn) \ + { \ + GetAttr(kNameSpaceID_None, nsGkAtoms::_atom, aReturn); \ + return NS_OK; \ + } \ + NS_IMETHODIMP \ + nsXULElement::Set##_method(const nsAString& aValue) \ + { \ + return SetAttr(kNameSpaceID_None, nsGkAtoms::_atom, aValue, \ + true); \ + } + +#define NS_IMPL_XUL_BOOL_ATTR(_method, _atom) \ + NS_IMETHODIMP \ + nsXULElement::Get##_method(bool* aResult) \ + { \ + *aResult = _method(); \ + return NS_OK; \ + } \ + NS_IMETHODIMP \ + nsXULElement::Set##_method(bool aValue) \ + { \ + SetXULBoolAttr(nsGkAtoms::_atom, aValue); \ + return NS_OK; \ + } + + +NS_IMPL_XUL_STRING_ATTR(Align, align) +NS_IMPL_XUL_STRING_ATTR(Dir, dir) +NS_IMPL_XUL_STRING_ATTR(Flex, flex) +NS_IMPL_XUL_STRING_ATTR(FlexGroup, flexgroup) +NS_IMPL_XUL_STRING_ATTR(Ordinal, ordinal) +NS_IMPL_XUL_STRING_ATTR(Orient, orient) +NS_IMPL_XUL_STRING_ATTR(Pack, pack) +NS_IMPL_XUL_BOOL_ATTR(Hidden, hidden) +NS_IMPL_XUL_BOOL_ATTR(Collapsed, collapsed) +NS_IMPL_XUL_BOOL_ATTR(AllowEvents, allowevents) +NS_IMPL_XUL_STRING_ATTR(Observes, observes) +NS_IMPL_XUL_STRING_ATTR(Menu, menu) +NS_IMPL_XUL_STRING_ATTR(ContextMenu, contextmenu) +NS_IMPL_XUL_STRING_ATTR(Tooltip, tooltip) +NS_IMPL_XUL_STRING_ATTR(Width, width) +NS_IMPL_XUL_STRING_ATTR(Height, height) +NS_IMPL_XUL_STRING_ATTR(MinWidth, minwidth) +NS_IMPL_XUL_STRING_ATTR(MinHeight, minheight) +NS_IMPL_XUL_STRING_ATTR(MaxWidth, maxwidth) +NS_IMPL_XUL_STRING_ATTR(MaxHeight, maxheight) +NS_IMPL_XUL_STRING_ATTR(Persist, persist) +NS_IMPL_XUL_STRING_ATTR(Left, left) +NS_IMPL_XUL_STRING_ATTR(Top, top) +NS_IMPL_XUL_STRING_ATTR(Datasources, datasources) +NS_IMPL_XUL_STRING_ATTR(Ref, ref) +NS_IMPL_XUL_STRING_ATTR(TooltipText, tooltiptext) +NS_IMPL_XUL_STRING_ATTR(StatusText, statustext) + +nsresult +nsXULElement::LoadSrc() +{ + // Allow frame loader only on objects for which a container box object + // can be obtained. + if (!IsAnyOfXULElements(nsGkAtoms::browser, nsGkAtoms::editor, + nsGkAtoms::iframe)) { + return NS_OK; + } + if (!IsInUncomposedDoc() || + !OwnerDoc()->GetRootElement() || + OwnerDoc()->GetRootElement()-> + NodeInfo()->Equals(nsGkAtoms::overlay, kNameSpaceID_XUL)) { + return NS_OK; + } + RefPtr<nsFrameLoader> frameLoader = GetFrameLoader(); + if (!frameLoader) { + // Check if we have an opener we need to be setting + nsXULSlots* slots = static_cast<nsXULSlots*>(Slots()); + nsCOMPtr<nsPIDOMWindowOuter> opener = do_QueryInterface(slots->mFrameLoaderOrOpener); + if (!opener) { + // If we are a content-primary xul-browser, we want to take the opener property! + nsCOMPtr<nsIDOMChromeWindow> chromeWindow = do_QueryInterface(OwnerDoc()->GetWindow()); + if (AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, + NS_LITERAL_STRING("content-primary"), eIgnoreCase) && + chromeWindow) { + nsCOMPtr<mozIDOMWindowProxy> wp; + chromeWindow->TakeOpenerForInitialContentBrowser(getter_AddRefs(wp)); + opener = nsPIDOMWindowOuter::From(wp); + } + } + + // false as the last parameter so that xul:iframe/browser/editor + // session history handling works like dynamic html:iframes. + // Usually xul elements are used in chrome, which doesn't have + // session history at all. + frameLoader = nsFrameLoader::Create(this, opener, false); + slots->mFrameLoaderOrOpener = static_cast<nsIFrameLoader*>(frameLoader); + NS_ENSURE_TRUE(frameLoader, NS_OK); + + (new AsyncEventDispatcher(this, + NS_LITERAL_STRING("XULFrameLoaderCreated"), + /* aBubbles */ true))->RunDOMEventWhenSafe(); + + if (AttrValueIs(kNameSpaceID_None, nsGkAtoms::prerendered, + NS_LITERAL_STRING("true"), eIgnoreCase)) { + nsresult rv = frameLoader->SetIsPrerendered(); + NS_ENSURE_SUCCESS(rv,rv); + } + } + + return frameLoader->LoadFrame(); +} + +nsresult +nsXULElement::GetFrameLoaderXPCOM(nsIFrameLoader **aFrameLoader) +{ + *aFrameLoader = GetFrameLoader().take(); + return NS_OK; +} + +already_AddRefed<nsFrameLoader> +nsXULElement::GetFrameLoader() +{ + nsXULSlots* slots = static_cast<nsXULSlots*>(GetExistingSlots()); + if (!slots) + return nullptr; + + nsCOMPtr<nsIFrameLoader> loader = do_QueryInterface(slots->mFrameLoaderOrOpener); + return already_AddRefed<nsFrameLoader>(static_cast<nsFrameLoader*>(loader.forget().take())); +} + +nsresult +nsXULElement::GetParentApplication(mozIApplication** aApplication) +{ + if (!aApplication) { + return NS_ERROR_FAILURE; + } + + *aApplication = nullptr; + return NS_OK; +} + +void +nsXULElement::PresetOpenerWindow(mozIDOMWindowProxy* aWindow, ErrorResult& aRv) +{ + nsXULSlots* slots = static_cast<nsXULSlots*>(Slots()); + MOZ_ASSERT(!slots->mFrameLoaderOrOpener, "A frameLoader or opener is present when calling PresetOpenerWindow"); + + slots->mFrameLoaderOrOpener = aWindow; +} + +nsresult +nsXULElement::SetIsPrerendered() +{ + return SetAttr(kNameSpaceID_None, nsGkAtoms::prerendered, nullptr, + NS_LITERAL_STRING("true"), true); +} + +void +nsXULElement::InternalSetFrameLoader(nsIFrameLoader* aNewFrameLoader) +{ + nsXULSlots* slots = static_cast<nsXULSlots*>(GetExistingDOMSlots()); + MOZ_ASSERT(slots); + + slots->mFrameLoaderOrOpener = aNewFrameLoader; +} + +void +nsXULElement::SwapFrameLoaders(HTMLIFrameElement& aOtherLoaderOwner, + ErrorResult& rv) +{ + if (!GetExistingDOMSlots()) { + rv.Throw(NS_ERROR_NOT_IMPLEMENTED); + return; + } + + nsCOMPtr<nsIFrameLoaderOwner> flo = do_QueryInterface(static_cast<nsIDOMXULElement*>(this)); + aOtherLoaderOwner.SwapFrameLoaders(flo, rv); +} + +void +nsXULElement::SwapFrameLoaders(nsXULElement& aOtherLoaderOwner, + ErrorResult& rv) +{ + if (&aOtherLoaderOwner == this) { + // nothing to do + return; + } + + if (!GetExistingDOMSlots()) { + rv.Throw(NS_ERROR_NOT_IMPLEMENTED); + return; + } + + nsCOMPtr<nsIFrameLoaderOwner> flo = do_QueryInterface(static_cast<nsIDOMXULElement*>(this)); + aOtherLoaderOwner.SwapFrameLoaders(flo, rv); +} + +void +nsXULElement::SwapFrameLoaders(nsIFrameLoaderOwner* aOtherLoaderOwner, + mozilla::ErrorResult& rv) +{ + if (!GetExistingDOMSlots()) { + rv.Throw(NS_ERROR_NOT_IMPLEMENTED); + return; + } + + RefPtr<nsFrameLoader> loader = GetFrameLoader(); + RefPtr<nsFrameLoader> otherLoader = aOtherLoaderOwner->GetFrameLoader(); + if (!loader || !otherLoader) { + rv.Throw(NS_ERROR_NOT_IMPLEMENTED); + return; + } + + nsCOMPtr<nsIFrameLoaderOwner> flo = do_QueryInterface(static_cast<nsIDOMXULElement*>(this)); + rv = loader->SwapWithOtherLoader(otherLoader, flo, aOtherLoaderOwner); +} + +NS_IMETHODIMP +nsXULElement::GetParentTree(nsIDOMXULMultiSelectControlElement** aTreeElement) +{ + for (nsIContent* current = GetParent(); current; + current = current->GetParent()) { + if (current->NodeInfo()->Equals(nsGkAtoms::listbox, + kNameSpaceID_XUL)) { + CallQueryInterface(current, aTreeElement); + // XXX returning NS_OK because that's what the code used to do; + // is that the right thing, though? + + return NS_OK; + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXULElement::Focus() +{ + ErrorResult rv; + Focus(rv); + return rv.StealNSResult(); +} + +NS_IMETHODIMP +nsXULElement::Blur() +{ + ErrorResult rv; + Blur(rv); + return rv.StealNSResult(); +} + +NS_IMETHODIMP +nsXULElement::Click() +{ + return ClickWithInputSource(nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN, /* aIsTrusted = */ true); +} + +void +nsXULElement::Click(ErrorResult& rv) +{ + rv = ClickWithInputSource(nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN, nsContentUtils::IsCallerChrome()); +} + +nsresult +nsXULElement::ClickWithInputSource(uint16_t aInputSource, bool aIsTrustedEvent) +{ + if (BoolAttrIsTrue(nsGkAtoms::disabled)) + return NS_OK; + + nsCOMPtr<nsIDocument> doc = GetComposedDoc(); // Strong just in case + if (doc) { + nsCOMPtr<nsIPresShell> shell = doc->GetShell(); + if (shell) { + // strong ref to PresContext so events don't destroy it + RefPtr<nsPresContext> context = shell->GetPresContext(); + + WidgetMouseEvent eventDown(aIsTrustedEvent, eMouseDown, + nullptr, WidgetMouseEvent::eReal); + WidgetMouseEvent eventUp(aIsTrustedEvent, eMouseUp, + nullptr, WidgetMouseEvent::eReal); + WidgetMouseEvent eventClick(aIsTrustedEvent, eMouseClick, nullptr, + WidgetMouseEvent::eReal); + eventDown.inputSource = eventUp.inputSource = eventClick.inputSource + = aInputSource; + + // send mouse down + nsEventStatus status = nsEventStatus_eIgnore; + EventDispatcher::Dispatch(static_cast<nsIContent*>(this), + context, &eventDown, nullptr, &status); + + // send mouse up + status = nsEventStatus_eIgnore; // reset status + EventDispatcher::Dispatch(static_cast<nsIContent*>(this), + context, &eventUp, nullptr, &status); + + // send mouse click + status = nsEventStatus_eIgnore; // reset status + EventDispatcher::Dispatch(static_cast<nsIContent*>(this), + context, &eventClick, nullptr, &status); + + // If the click has been prevented, lets skip the command call + // this is how a physical click works + if (status == nsEventStatus_eConsumeNoDefault) { + return NS_OK; + } + } + } + + // oncommand is fired when an element is clicked... + return DoCommand(); +} + +NS_IMETHODIMP +nsXULElement::DoCommand() +{ + nsCOMPtr<nsIDocument> doc = GetComposedDoc(); // strong just in case + if (doc) { + nsContentUtils::DispatchXULCommand(this, true); + } + + return NS_OK; +} + +nsIContent * +nsXULElement::GetBindingParent() const +{ + return mBindingParent; +} + +bool +nsXULElement::IsNodeOfType(uint32_t aFlags) const +{ + return !(aFlags & ~eCONTENT); +} + +nsresult +nsXULElement::AddPopupListener(nsIAtom* aName) +{ + // Add a popup listener to the element + bool isContext = (aName == nsGkAtoms::context || + aName == nsGkAtoms::contextmenu); + uint32_t listenerFlag = isContext ? + XUL_ELEMENT_HAS_CONTENTMENU_LISTENER : + XUL_ELEMENT_HAS_POPUP_LISTENER; + + if (HasFlag(listenerFlag)) { + return NS_OK; + } + + nsCOMPtr<nsIDOMEventListener> listener = + new nsXULPopupListener(this, isContext); + + // Add the popup as a listener on this element. + EventListenerManager* manager = GetOrCreateListenerManager(); + SetFlags(listenerFlag); + + if (isContext) { + manager->AddEventListenerByType(listener, + NS_LITERAL_STRING("contextmenu"), + TrustedEventsAtSystemGroupBubble()); + } else { + manager->AddEventListenerByType(listener, + NS_LITERAL_STRING("mousedown"), + TrustedEventsAtSystemGroupBubble()); + } + return NS_OK; +} + +EventStates +nsXULElement::IntrinsicState() const +{ + EventStates state = nsStyledElement::IntrinsicState(); + + if (IsReadWriteTextElement()) { + state |= NS_EVENT_STATE_MOZ_READWRITE; + state &= ~NS_EVENT_STATE_MOZ_READONLY; + } + + return state; +} + +//---------------------------------------------------------------------- + +nsresult +nsXULElement::MakeHeavyweight(nsXULPrototypeElement* aPrototype) +{ + if (!aPrototype) { + return NS_OK; + } + + uint32_t i; + nsresult rv; + for (i = 0; i < aPrototype->mNumAttributes; ++i) { + nsXULPrototypeAttribute* protoattr = &aPrototype->mAttributes[i]; + nsAttrValue attrValue; + + // Style rules need to be cloned. + if (protoattr->mValue.Type() == nsAttrValue::eCSSDeclaration) { + DeclarationBlock* decl = protoattr->mValue.GetCSSDeclarationValue(); + RefPtr<css::Declaration> + declClone = new css::Declaration(*decl->AsGecko()); + + nsString stringValue; + protoattr->mValue.ToString(stringValue); + + attrValue.SetTo(declClone.forget(), &stringValue); + } else { + attrValue.SetTo(protoattr->mValue); + } + + // XXX we might wanna have a SetAndTakeAttr that takes an nsAttrName + if (protoattr->mName.IsAtom()) { + rv = mAttrsAndChildren.SetAndSwapAttr(protoattr->mName.Atom(), attrValue); + } else { + rv = mAttrsAndChildren.SetAndSwapAttr(protoattr->mName.NodeInfo(), + attrValue); + } + NS_ENSURE_SUCCESS(rv, rv); + } + return NS_OK; +} + +nsresult +nsXULElement::HideWindowChrome(bool aShouldHide) +{ + nsIDocument* doc = GetUncomposedDoc(); + if (!doc || doc->GetRootElement() != this) + return NS_ERROR_UNEXPECTED; + + // only top level chrome documents can hide the window chrome + if (!doc->IsRootDisplayDocument()) + return NS_OK; + + nsIPresShell *shell = doc->GetShell(); + + if (shell) { + nsIFrame* frame = GetPrimaryFrame(); + + nsPresContext *presContext = shell->GetPresContext(); + + if (frame && presContext && presContext->IsChrome()) { + nsView* view = frame->GetClosestView(); + + if (view) { + nsIWidget* w = view->GetWidget(); + NS_ENSURE_STATE(w); + w->HideWindowChrome(aShouldHide); + } + } + } + + return NS_OK; +} + +nsIWidget* +nsXULElement::GetWindowWidget() +{ + nsIDocument* doc = GetComposedDoc(); + + // only top level chrome documents can set the titlebar color + if (doc && doc->IsRootDisplayDocument()) { + nsCOMPtr<nsISupports> container = doc->GetContainer(); + nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container); + if (baseWindow) { + nsCOMPtr<nsIWidget> mainWidget; + baseWindow->GetMainWidget(getter_AddRefs(mainWidget)); + return mainWidget; + } + } + return nullptr; +} + +void +nsXULElement::SetTitlebarColor(nscolor aColor, bool aActive) +{ + nsIWidget* mainWidget = GetWindowWidget(); + if (mainWidget) { + mainWidget->SetWindowTitlebarColor(aColor, aActive); + } +} + +class SetDrawInTitleBarEvent : public Runnable +{ +public: + SetDrawInTitleBarEvent(nsIWidget* aWidget, bool aState) + : mWidget(aWidget) + , mState(aState) + {} + + NS_IMETHOD Run() override { + NS_ASSERTION(mWidget, "You shouldn't call this runnable with a null widget!"); + + mWidget->SetDrawsInTitlebar(mState); + return NS_OK; + } + +private: + nsCOMPtr<nsIWidget> mWidget; + bool mState; +}; + +void +nsXULElement::SetDrawsInTitlebar(bool aState) +{ + nsIWidget* mainWidget = GetWindowWidget(); + if (mainWidget) { + nsContentUtils::AddScriptRunner(new SetDrawInTitleBarEvent(mainWidget, aState)); + } +} + +void +nsXULElement::SetDrawsTitle(bool aState) +{ + nsIWidget* mainWidget = GetWindowWidget(); + if (mainWidget) { + // We can do this synchronously because SetDrawsTitle doesn't have any + // synchronous effects apart from a harmless invalidation. + mainWidget->SetDrawsTitle(aState); + } +} + +void +nsXULElement::UpdateBrightTitlebarForeground(nsIDocument* aDoc) +{ + nsIWidget* mainWidget = GetWindowWidget(); + if (mainWidget) { + // We can do this synchronously because SetBrightTitlebarForeground doesn't have any + // synchronous effects apart from a harmless invalidation. + mainWidget->SetUseBrightTitlebarForeground( + aDoc->GetDocumentLWTheme() == nsIDocument::Doc_Theme_Bright || + aDoc->GetRootElement()->AttrValueIs(kNameSpaceID_None, + nsGkAtoms::brighttitlebarforeground, + NS_LITERAL_STRING("true"), + eCaseMatters)); + } +} + +class MarginSetter : public Runnable +{ +public: + explicit MarginSetter(nsIWidget* aWidget) : + mWidget(aWidget), mMargin(-1, -1, -1, -1) + {} + MarginSetter(nsIWidget *aWidget, const LayoutDeviceIntMargin& aMargin) : + mWidget(aWidget), mMargin(aMargin) + {} + + NS_IMETHOD Run() override + { + // SetNonClientMargins can dispatch native events, hence doing + // it off a script runner. + mWidget->SetNonClientMargins(mMargin); + return NS_OK; + } + +private: + nsCOMPtr<nsIWidget> mWidget; + LayoutDeviceIntMargin mMargin; +}; + +void +nsXULElement::SetChromeMargins(const nsAttrValue* aValue) +{ + if (!aValue) + return; + + nsIWidget* mainWidget = GetWindowWidget(); + if (!mainWidget) + return; + + // top, right, bottom, left - see nsAttrValue + nsIntMargin margins; + bool gotMargins = false; + + if (aValue->Type() == nsAttrValue::eIntMarginValue) { + gotMargins = aValue->GetIntMarginValue(margins); + } else { + nsAutoString tmp; + aValue->ToString(tmp); + gotMargins = nsContentUtils::ParseIntMarginValue(tmp, margins); + } + if (gotMargins) { + nsContentUtils::AddScriptRunner( + new MarginSetter( + mainWidget, LayoutDeviceIntMargin::FromUnknownMargin(margins))); + } +} + +void +nsXULElement::ResetChromeMargins() +{ + nsIWidget* mainWidget = GetWindowWidget(); + if (!mainWidget) + return; + // See nsIWidget + nsContentUtils::AddScriptRunner(new MarginSetter(mainWidget)); +} + +bool +nsXULElement::BoolAttrIsTrue(nsIAtom* aName) const +{ + const nsAttrValue* attr = + GetAttrInfo(kNameSpaceID_None, aName).mValue; + + return attr && attr->Type() == nsAttrValue::eAtom && + attr->GetAtomValue() == nsGkAtoms::_true; +} + +void +nsXULElement::RecompileScriptEventListeners() +{ + int32_t i, count = mAttrsAndChildren.AttrCount(); + for (i = 0; i < count; ++i) { + const nsAttrName *name = mAttrsAndChildren.AttrNameAt(i); + + // Eventlistenener-attributes are always in the null namespace + if (!name->IsAtom()) { + continue; + } + + nsIAtom *attr = name->Atom(); + if (!nsContentUtils::IsEventAttributeName(attr, EventNameType_XUL)) { + continue; + } + + nsAutoString value; + GetAttr(kNameSpaceID_None, attr, value); + SetEventHandler(attr, value, true); + } +} + +bool +nsXULElement::IsEventAttributeName(nsIAtom *aName) +{ + return nsContentUtils::IsEventAttributeName(aName, EventNameType_XUL); +} + +JSObject* +nsXULElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) +{ + return dom::XULElementBinding::Wrap(aCx, this, aGivenProto); +} + +NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULPrototypeNode) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULPrototypeNode) + if (tmp->mType == nsXULPrototypeNode::eType_Element) { + static_cast<nsXULPrototypeElement*>(tmp)->Unlink(); + } else if (tmp->mType == nsXULPrototypeNode::eType_Script) { + static_cast<nsXULPrototypeScript*>(tmp)->UnlinkJSObjects(); + } +NS_IMPL_CYCLE_COLLECTION_UNLINK_END +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULPrototypeNode) + if (tmp->mType == nsXULPrototypeNode::eType_Element) { + nsXULPrototypeElement *elem = + static_cast<nsXULPrototypeElement*>(tmp); + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mNodeInfo"); + cb.NoteNativeChild(elem->mNodeInfo, + NS_CYCLE_COLLECTION_PARTICIPANT(NodeInfo)); + uint32_t i; + for (i = 0; i < elem->mNumAttributes; ++i) { + const nsAttrName& name = elem->mAttributes[i].mName; + if (!name.IsAtom()) { + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, + "mAttributes[i].mName.NodeInfo()"); + cb.NoteNativeChild(name.NodeInfo(), + NS_CYCLE_COLLECTION_PARTICIPANT(NodeInfo)); + } + } + ImplCycleCollectionTraverse(cb, elem->mChildren, "mChildren"); + } + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsXULPrototypeNode) + if (tmp->mType == nsXULPrototypeNode::eType_Script) { + nsXULPrototypeScript *script = + static_cast<nsXULPrototypeScript*>(tmp); + script->Trace(aCallbacks, aClosure); + } +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsXULPrototypeNode, AddRef) +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsXULPrototypeNode, Release) + +//---------------------------------------------------------------------- +// +// nsXULPrototypeAttribute +// + +nsXULPrototypeAttribute::~nsXULPrototypeAttribute() +{ + MOZ_COUNT_DTOR(nsXULPrototypeAttribute); +} + + +//---------------------------------------------------------------------- +// +// nsXULPrototypeElement +// + +nsresult +nsXULPrototypeElement::Serialize(nsIObjectOutputStream* aStream, + nsXULPrototypeDocument* aProtoDoc, + const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos) +{ + nsresult rv; + + // Write basic prototype data + rv = aStream->Write32(mType); + + // Write Node Info + int32_t index = aNodeInfos->IndexOf(mNodeInfo); + NS_ASSERTION(index >= 0, "unknown mozilla::dom::NodeInfo index"); + nsresult tmp = aStream->Write32(index); + if (NS_FAILED(tmp)) { + rv = tmp; + } + + // Write Attributes + tmp = aStream->Write32(mNumAttributes); + if (NS_FAILED(tmp)) { + rv = tmp; + } + + nsAutoString attributeValue; + uint32_t i; + for (i = 0; i < mNumAttributes; ++i) { + RefPtr<mozilla::dom::NodeInfo> ni; + if (mAttributes[i].mName.IsAtom()) { + ni = mNodeInfo->NodeInfoManager()-> + GetNodeInfo(mAttributes[i].mName.Atom(), nullptr, + kNameSpaceID_None, + nsIDOMNode::ATTRIBUTE_NODE); + NS_ASSERTION(ni, "the nodeinfo should already exist"); + } else { + ni = mAttributes[i].mName.NodeInfo(); + } + + index = aNodeInfos->IndexOf(ni); + NS_ASSERTION(index >= 0, "unknown mozilla::dom::NodeInfo index"); + tmp = aStream->Write32(index); + if (NS_FAILED(tmp)) { + rv = tmp; + } + + mAttributes[i].mValue.ToString(attributeValue); + tmp = aStream->WriteWStringZ(attributeValue.get()); + if (NS_FAILED(tmp)) { + rv = tmp; + } + } + + // Now write children + tmp = aStream->Write32(uint32_t(mChildren.Length())); + if (NS_FAILED(tmp)) { + rv = tmp; + } + for (i = 0; i < mChildren.Length(); i++) { + nsXULPrototypeNode* child = mChildren[i].get(); + switch (child->mType) { + case eType_Element: + case eType_Text: + case eType_PI: + tmp = child->Serialize(aStream, aProtoDoc, aNodeInfos); + if (NS_FAILED(tmp)) { + rv = tmp; + } + break; + case eType_Script: + tmp = aStream->Write32(child->mType); + if (NS_FAILED(tmp)) { + rv = tmp; + } + nsXULPrototypeScript* script = static_cast<nsXULPrototypeScript*>(child); + + tmp = aStream->Write8(script->mOutOfLine); + if (NS_FAILED(tmp)) { + rv = tmp; + } + if (! script->mOutOfLine) { + tmp = script->Serialize(aStream, aProtoDoc, aNodeInfos); + if (NS_FAILED(tmp)) { + rv = tmp; + } + } else { + tmp = aStream->WriteCompoundObject(script->mSrcURI, + NS_GET_IID(nsIURI), + true); + if (NS_FAILED(tmp)) { + rv = tmp; + } + + if (script->HasScriptObject()) { + // This may return NS_OK without muxing script->mSrcURI's + // data into the cache file, in the case where that + // muxed document is already there (written by a prior + // session, or by an earlier cache episode during this + // session). + tmp = script->SerializeOutOfLine(aStream, aProtoDoc); + if (NS_FAILED(tmp)) { + rv = tmp; + } + } + } + break; + } + } + + return rv; +} + +nsresult +nsXULPrototypeElement::Deserialize(nsIObjectInputStream* aStream, + nsXULPrototypeDocument* aProtoDoc, + nsIURI* aDocumentURI, + const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos) +{ + NS_PRECONDITION(aNodeInfos, "missing nodeinfo array"); + + // Read Node Info + uint32_t number = 0; + nsresult rv = aStream->Read32(&number); + if (NS_WARN_IF(NS_FAILED(rv))) return rv; + mNodeInfo = aNodeInfos->SafeElementAt(number, nullptr); + if (!mNodeInfo) { + return NS_ERROR_UNEXPECTED; + } + + // Read Attributes + rv = aStream->Read32(&number); + if (NS_WARN_IF(NS_FAILED(rv))) return rv; + mNumAttributes = int32_t(number); + + if (mNumAttributes > 0) { + mAttributes = new (fallible) nsXULPrototypeAttribute[mNumAttributes]; + if (!mAttributes) { + return NS_ERROR_OUT_OF_MEMORY; + } + + nsAutoString attributeValue; + for (uint32_t i = 0; i < mNumAttributes; ++i) { + rv = aStream->Read32(&number); + if (NS_WARN_IF(NS_FAILED(rv))) return rv; + mozilla::dom::NodeInfo* ni = aNodeInfos->SafeElementAt(number, nullptr); + if (!ni) { + return NS_ERROR_UNEXPECTED; + } + + mAttributes[i].mName.SetTo(ni); + + rv = aStream->ReadString(attributeValue); + if (NS_WARN_IF(NS_FAILED(rv))) return rv; + rv = SetAttrAt(i, attributeValue, aDocumentURI); + if (NS_WARN_IF(NS_FAILED(rv))) return rv; + } + } + + rv = aStream->Read32(&number); + if (NS_WARN_IF(NS_FAILED(rv))) return rv; + uint32_t numChildren = int32_t(number); + + if (numChildren > 0) { + if (!mChildren.SetCapacity(numChildren, fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + for (uint32_t i = 0; i < numChildren; i++) { + rv = aStream->Read32(&number); + if (NS_WARN_IF(NS_FAILED(rv))) return rv; + Type childType = (Type)number; + + RefPtr<nsXULPrototypeNode> child; + + switch (childType) { + case eType_Element: + child = new nsXULPrototypeElement(); + rv = child->Deserialize(aStream, aProtoDoc, aDocumentURI, + aNodeInfos); + if (NS_WARN_IF(NS_FAILED(rv))) return rv; + break; + case eType_Text: + child = new nsXULPrototypeText(); + rv = child->Deserialize(aStream, aProtoDoc, aDocumentURI, + aNodeInfos); + if (NS_WARN_IF(NS_FAILED(rv))) return rv; + break; + case eType_PI: + child = new nsXULPrototypePI(); + rv = child->Deserialize(aStream, aProtoDoc, aDocumentURI, + aNodeInfos); + if (NS_WARN_IF(NS_FAILED(rv))) return rv; + break; + case eType_Script: { + // language version/options obtained during deserialization. + RefPtr<nsXULPrototypeScript> script = new nsXULPrototypeScript(0, 0); + + rv = aStream->ReadBoolean(&script->mOutOfLine); + if (NS_WARN_IF(NS_FAILED(rv))) return rv; + if (!script->mOutOfLine) { + rv = script->Deserialize(aStream, aProtoDoc, aDocumentURI, + aNodeInfos); + if (NS_WARN_IF(NS_FAILED(rv))) return rv; + } else { + nsCOMPtr<nsISupports> supports; + rv = aStream->ReadObject(true, getter_AddRefs(supports)); + if (NS_WARN_IF(NS_FAILED(rv))) return rv; + script->mSrcURI = do_QueryInterface(supports); + + rv = script->DeserializeOutOfLine(aStream, aProtoDoc); + if (NS_WARN_IF(NS_FAILED(rv))) return rv; + } + + child = script.forget(); + break; + } + default: + MOZ_ASSERT(false, "Unexpected child type!"); + return NS_ERROR_UNEXPECTED; + } + + MOZ_ASSERT(child, "Don't append null to mChildren"); + MOZ_ASSERT(child->mType == childType); + mChildren.AppendElement(child); + + // Oh dear. Something failed during the deserialization. + // We don't know what. But likely consequences of failed + // deserializations included calls to |AbortCaching| which + // shuts down the cache and closes our streams. + // If that happens, next time through this loop, we die a messy + // death. So, let's just fail now, and propagate that failure + // upward so that the ChromeProtocolHandler knows it can't use + // a cached chrome channel for this. + if (NS_WARN_IF(NS_FAILED(rv))) + return rv; + } + } + + return rv; +} + +nsresult +nsXULPrototypeElement::SetAttrAt(uint32_t aPos, const nsAString& aValue, + nsIURI* aDocumentURI) +{ + NS_PRECONDITION(aPos < mNumAttributes, "out-of-bounds"); + + // WARNING!! + // This code is largely duplicated in nsXULElement::SetAttr. + // Any changes should be made to both functions. + + if (!mNodeInfo->NamespaceEquals(kNameSpaceID_XUL)) { + mAttributes[aPos].mValue.ParseStringOrAtom(aValue); + + return NS_OK; + } + + if (mAttributes[aPos].mName.Equals(nsGkAtoms::id) && + !aValue.IsEmpty()) { + mHasIdAttribute = true; + // Store id as atom. + // id="" means that the element has no id. Not that it has + // emptystring as id. + mAttributes[aPos].mValue.ParseAtom(aValue); + + return NS_OK; + } else if (mAttributes[aPos].mName.Equals(nsGkAtoms::_class)) { + mHasClassAttribute = true; + // Compute the element's class list + mAttributes[aPos].mValue.ParseAtomArray(aValue); + + return NS_OK; + } else if (mAttributes[aPos].mName.Equals(nsGkAtoms::style)) { + mHasStyleAttribute = true; + // Parse the element's 'style' attribute + + nsCSSParser parser; + + // XXX Get correct Base URI (need GetBaseURI on *prototype* element) + // TODO: If we implement Content Security Policy for chrome documents + // as has been discussed, the CSP should be checked here to see if + // inline styles are allowed to be applied. + RefPtr<css::Declaration> declaration = + parser.ParseStyleAttribute(aValue, aDocumentURI, aDocumentURI, + // This is basically duplicating what + // nsINode::NodePrincipal() does + mNodeInfo->NodeInfoManager()-> + DocumentPrincipal()); + if (declaration) { + mAttributes[aPos].mValue.SetTo(declaration.forget(), &aValue); + + return NS_OK; + } + // Don't abort if parsing failed, it could just be malformed css. + } + + mAttributes[aPos].mValue.ParseStringOrAtom(aValue); + + return NS_OK; +} + +void +nsXULPrototypeElement::Unlink() +{ + mNumAttributes = 0; + delete[] mAttributes; + mAttributes = nullptr; + mChildren.Clear(); +} + +void +nsXULPrototypeElement::TraceAllScripts(JSTracer* aTrc) +{ + for (uint32_t i = 0; i < mChildren.Length(); ++i) { + nsXULPrototypeNode* child = mChildren[i]; + if (child->mType == nsXULPrototypeNode::eType_Element) { + static_cast<nsXULPrototypeElement*>(child)->TraceAllScripts(aTrc); + } else if (child->mType == nsXULPrototypeNode::eType_Script) { + static_cast<nsXULPrototypeScript*>(child)->TraceScriptObject(aTrc); + } + } +} + +//---------------------------------------------------------------------- +// +// nsXULPrototypeScript +// + +nsXULPrototypeScript::nsXULPrototypeScript(uint32_t aLineNo, uint32_t aVersion) + : nsXULPrototypeNode(eType_Script), + mLineNo(aLineNo), + mSrcLoading(false), + mOutOfLine(true), + mSrcLoadWaiters(nullptr), + mLangVersion(aVersion), + mScriptObject(nullptr) +{ +} + + +nsXULPrototypeScript::~nsXULPrototypeScript() +{ + UnlinkJSObjects(); +} + +nsresult +nsXULPrototypeScript::Serialize(nsIObjectOutputStream* aStream, + nsXULPrototypeDocument* aProtoDoc, + const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos) +{ + NS_ENSURE_TRUE(aProtoDoc, NS_ERROR_UNEXPECTED); + + AutoJSAPI jsapi; + if (!jsapi.Init(xpc::CompilationScope())) { + return NS_ERROR_UNEXPECTED; + } + + NS_ASSERTION(!mSrcLoading || mSrcLoadWaiters != nullptr || + !mScriptObject, + "script source still loading when serializing?!"); + if (!mScriptObject) + return NS_ERROR_FAILURE; + + // Write basic prototype data + nsresult rv; + rv = aStream->Write32(mLineNo); + if (NS_FAILED(rv)) return rv; + rv = aStream->Write32(mLangVersion); + if (NS_FAILED(rv)) return rv; + + JSContext* cx = jsapi.cx(); + JS::Rooted<JSScript*> script(cx, mScriptObject); + MOZ_ASSERT(xpc::CompilationScope() == JS::CurrentGlobalOrNull(cx)); + return nsContentUtils::XPConnect()->WriteScript(aStream, cx, script); +} + +nsresult +nsXULPrototypeScript::SerializeOutOfLine(nsIObjectOutputStream* aStream, + nsXULPrototypeDocument* aProtoDoc) +{ + nsresult rv = NS_ERROR_NOT_IMPLEMENTED; + + bool isChrome = false; + if (NS_FAILED(mSrcURI->SchemeIs("chrome", &isChrome)) || !isChrome) + // Don't cache scripts that don't come from chrome uris. + return rv; + + nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance(); + if (!cache) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ASSERTION(cache->IsEnabled(), + "writing to the cache file, but the XUL cache is off?"); + bool exists; + cache->HasData(mSrcURI, &exists); + + /* return will be NS_OK from GetAsciiSpec. + * that makes no sense. + * nor does returning NS_OK from HasMuxedDocument. + * XXX return something meaningful. + */ + if (exists) + return NS_OK; + + nsCOMPtr<nsIObjectOutputStream> oos; + rv = cache->GetOutputStream(mSrcURI, getter_AddRefs(oos)); + NS_ENSURE_SUCCESS(rv, rv); + + nsresult tmp = Serialize(oos, aProtoDoc, nullptr); + if (NS_FAILED(tmp)) { + rv = tmp; + } + tmp = cache->FinishOutputStream(mSrcURI); + if (NS_FAILED(tmp)) { + rv = tmp; + } + + if (NS_FAILED(rv)) + cache->AbortCaching(); + return rv; +} + + +nsresult +nsXULPrototypeScript::Deserialize(nsIObjectInputStream* aStream, + nsXULPrototypeDocument* aProtoDoc, + nsIURI* aDocumentURI, + const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos) +{ + nsresult rv; + NS_ASSERTION(!mSrcLoading || mSrcLoadWaiters != nullptr || + !mScriptObject, + "prototype script not well-initialized when deserializing?!"); + + // Read basic prototype data + rv = aStream->Read32(&mLineNo); + if (NS_FAILED(rv)) return rv; + rv = aStream->Read32(&mLangVersion); + if (NS_FAILED(rv)) return rv; + + AutoJSAPI jsapi; + if (!jsapi.Init(xpc::CompilationScope())) { + return NS_ERROR_UNEXPECTED; + } + JSContext* cx = jsapi.cx(); + + JS::Rooted<JSScript*> newScriptObject(cx); + rv = nsContentUtils::XPConnect()->ReadScript(aStream, cx, + newScriptObject.address()); + NS_ENSURE_SUCCESS(rv, rv); + Set(newScriptObject); + return NS_OK; +} + + +nsresult +nsXULPrototypeScript::DeserializeOutOfLine(nsIObjectInputStream* aInput, + nsXULPrototypeDocument* aProtoDoc) +{ + // Keep track of failure via rv, so we can + // AbortCaching if things look bad. + nsresult rv = NS_OK; + nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance(); + + nsCOMPtr<nsIObjectInputStream> objectInput = aInput; + if (cache) { + bool useXULCache = true; + if (mSrcURI) { + // NB: we must check the XUL script cache early, to avoid + // multiple deserialization attempts for a given script. + // Note that XULDocument::LoadScript + // checks the XUL script cache too, in order to handle the + // serialization case. + // + // We need do this only for <script src='strres.js'> and the + // like, i.e., out-of-line scripts that are included by several + // different XUL documents stored in the cache file. + useXULCache = cache->IsEnabled(); + + if (useXULCache) { + JSScript* newScriptObject = + cache->GetScript(mSrcURI); + if (newScriptObject) + Set(newScriptObject); + } + } + + if (!mScriptObject) { + if (mSrcURI) { + rv = cache->GetInputStream(mSrcURI, getter_AddRefs(objectInput)); + } + // If !mSrcURI, we have an inline script. We shouldn't have + // to do anything else in that case, I think. + + // We do reflect errors into rv, but our caller may want to + // ignore our return value, because mScriptObject will be null + // after any error, and that suffices to cause the script to + // be reloaded (from the src= URI, if any) and recompiled. + // We're better off slow-loading than bailing out due to a + // error. + if (NS_SUCCEEDED(rv)) + rv = Deserialize(objectInput, aProtoDoc, nullptr, nullptr); + + if (NS_SUCCEEDED(rv)) { + if (useXULCache && mSrcURI) { + bool isChrome = false; + mSrcURI->SchemeIs("chrome", &isChrome); + if (isChrome) { + JS::Rooted<JSScript*> script(RootingCx(), GetScriptObject()); + cache->PutScript(mSrcURI, script); + } + } + cache->FinishInputStream(mSrcURI); + } else { + // If mSrcURI is not in the cache, + // rv will be NS_ERROR_NOT_AVAILABLE and we'll try to + // update the cache file to hold a serialization of + // this script, once it has finished loading. + if (rv != NS_ERROR_NOT_AVAILABLE) + cache->AbortCaching(); + } + } + } + return rv; +} + +class NotifyOffThreadScriptCompletedRunnable : public Runnable +{ + // An array of all outstanding script receivers. All reference counting of + // these objects happens on the main thread. When we return to the main + // thread from script compilation we make sure our receiver is still in + // this array (still alive) before proceeding. This array is cleared during + // shutdown, potentially before all outstanding script compilations have + // finished. We do not need to worry about pointer replay here, because + // a) we should not be starting script compilation after clearing this + // array and b) in all other cases the receiver will still be alive. + static StaticAutoPtr<nsTArray<nsCOMPtr<nsIOffThreadScriptReceiver>>> sReceivers; + static bool sSetupClearOnShutdown; + + nsIOffThreadScriptReceiver* mReceiver; + void *mToken; + +public: + NotifyOffThreadScriptCompletedRunnable(nsIOffThreadScriptReceiver* aReceiver, + void *aToken) + : mReceiver(aReceiver), mToken(aToken) + {} + + static void NoteReceiver(nsIOffThreadScriptReceiver* aReceiver) { + if (!sSetupClearOnShutdown) { + ClearOnShutdown(&sReceivers); + sSetupClearOnShutdown = true; + sReceivers = new nsTArray<nsCOMPtr<nsIOffThreadScriptReceiver>>(); + } + + // If we ever crash here, it's because we tried to lazy compile script + // too late in shutdown. + sReceivers->AppendElement(aReceiver); + } + + NS_DECL_NSIRUNNABLE +}; + +StaticAutoPtr<nsTArray<nsCOMPtr<nsIOffThreadScriptReceiver>>> NotifyOffThreadScriptCompletedRunnable::sReceivers; +bool NotifyOffThreadScriptCompletedRunnable::sSetupClearOnShutdown = false; + +NS_IMETHODIMP +NotifyOffThreadScriptCompletedRunnable::Run() +{ + MOZ_ASSERT(NS_IsMainThread()); + + JS::Rooted<JSScript*> script(RootingCx()); + { + AutoJSAPI jsapi; + if (!jsapi.Init(xpc::CompilationScope())) { + // Now what? I guess we just leak... this should probably never + // happen. + return NS_ERROR_UNEXPECTED; + } + JSContext* cx = jsapi.cx(); + script = JS::FinishOffThreadScript(cx, mToken); + } + + if (!sReceivers) { + // We've already shut down. + return NS_OK; + } + + auto index = sReceivers->IndexOf(mReceiver); + MOZ_RELEASE_ASSERT(index != sReceivers->NoIndex); + nsCOMPtr<nsIOffThreadScriptReceiver> receiver = (*sReceivers)[index].forget(); + sReceivers->RemoveElementAt(index); + + return receiver->OnScriptCompileComplete(script, script ? NS_OK : NS_ERROR_FAILURE); +} + +static void +OffThreadScriptReceiverCallback(void *aToken, void *aCallbackData) +{ + // Be careful not to adjust the refcount on the receiver, as this callback + // may be invoked off the main thread. + nsIOffThreadScriptReceiver* aReceiver = static_cast<nsIOffThreadScriptReceiver*>(aCallbackData); + RefPtr<NotifyOffThreadScriptCompletedRunnable> notify = + new NotifyOffThreadScriptCompletedRunnable(aReceiver, aToken); + NS_DispatchToMainThread(notify); +} + +nsresult +nsXULPrototypeScript::Compile(JS::SourceBufferHolder& aSrcBuf, + nsIURI* aURI, uint32_t aLineNo, + nsIDocument* aDocument, + nsIOffThreadScriptReceiver *aOffThreadReceiver /* = nullptr */) +{ + // We'll compile the script in the compilation scope. + AutoJSAPI jsapi; + if (!jsapi.Init(xpc::CompilationScope())) { + return NS_ERROR_UNEXPECTED; + } + JSContext* cx = jsapi.cx(); + + nsresult rv; + nsAutoCString urlspec; + nsContentUtils::GetWrapperSafeScriptFilename(aDocument, aURI, urlspec, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Ok, compile it to create a prototype script object! + NS_ENSURE_TRUE(JSVersion(mLangVersion) != JSVERSION_UNKNOWN, NS_OK); + JS::CompileOptions options(cx); + options.setIntroductionType("scriptElement") + .setFileAndLine(urlspec.get(), aLineNo) + .setVersion(JSVersion(mLangVersion)); + // If the script was inline, tell the JS parser to save source for + // Function.prototype.toSource(). If it's out of line, we retrieve the + // source from the files on demand. + options.setSourceIsLazy(mOutOfLine); + JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx)); + if (scope) { + JS::ExposeObjectToActiveJS(scope); + } + + if (aOffThreadReceiver && JS::CanCompileOffThread(cx, options, aSrcBuf.length())) { + if (!JS::CompileOffThread(cx, options, + aSrcBuf.get(), aSrcBuf.length(), + OffThreadScriptReceiverCallback, + static_cast<void*>(aOffThreadReceiver))) { + return NS_ERROR_OUT_OF_MEMORY; + } + NotifyOffThreadScriptCompletedRunnable::NoteReceiver(aOffThreadReceiver); + } else { + JS::Rooted<JSScript*> script(cx); + if (!JS::Compile(cx, options, aSrcBuf, &script)) + return NS_ERROR_OUT_OF_MEMORY; + Set(script); + } + return NS_OK; +} + +nsresult +nsXULPrototypeScript::Compile(const char16_t* aText, + int32_t aTextLength, + nsIURI* aURI, + uint32_t aLineNo, + nsIDocument* aDocument, + nsIOffThreadScriptReceiver *aOffThreadReceiver /* = nullptr */) +{ + JS::SourceBufferHolder srcBuf(aText, aTextLength, + JS::SourceBufferHolder::NoOwnership); + return Compile(srcBuf, aURI, aLineNo, aDocument, aOffThreadReceiver); +} + +void +nsXULPrototypeScript::UnlinkJSObjects() +{ + if (mScriptObject) { + mScriptObject = nullptr; + mozilla::DropJSObjects(this); + } +} + +void +nsXULPrototypeScript::Set(JSScript* aObject) +{ + MOZ_ASSERT(!mScriptObject, "Leaking script object."); + if (!aObject) { + mScriptObject = nullptr; + return; + } + + mScriptObject = aObject; + mozilla::HoldJSObjects(this); +} + +//---------------------------------------------------------------------- +// +// nsXULPrototypeText +// + +nsresult +nsXULPrototypeText::Serialize(nsIObjectOutputStream* aStream, + nsXULPrototypeDocument* aProtoDoc, + const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos) +{ + nsresult rv; + + // Write basic prototype data + rv = aStream->Write32(mType); + + nsresult tmp = aStream->WriteWStringZ(mValue.get()); + if (NS_FAILED(tmp)) { + rv = tmp; + } + + return rv; +} + +nsresult +nsXULPrototypeText::Deserialize(nsIObjectInputStream* aStream, + nsXULPrototypeDocument* aProtoDoc, + nsIURI* aDocumentURI, + const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos) +{ + nsresult rv = aStream->ReadString(mValue); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + return NS_OK; +} + +//---------------------------------------------------------------------- +// +// nsXULPrototypePI +// + +nsresult +nsXULPrototypePI::Serialize(nsIObjectOutputStream* aStream, + nsXULPrototypeDocument* aProtoDoc, + const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos) +{ + nsresult rv; + + // Write basic prototype data + rv = aStream->Write32(mType); + + nsresult tmp = aStream->WriteWStringZ(mTarget.get()); + if (NS_FAILED(tmp)) { + rv = tmp; + } + tmp = aStream->WriteWStringZ(mData.get()); + if (NS_FAILED(tmp)) { + rv = tmp; + } + + return rv; +} + +nsresult +nsXULPrototypePI::Deserialize(nsIObjectInputStream* aStream, + nsXULPrototypeDocument* aProtoDoc, + nsIURI* aDocumentURI, + const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos) +{ + nsresult rv; + + rv = aStream->ReadString(mTarget); + if (NS_FAILED(rv)) return rv; + rv = aStream->ReadString(mData); + if (NS_FAILED(rv)) return rv; + + return rv; +} diff --git a/dom/xul/nsXULElement.h b/dom/xul/nsXULElement.h new file mode 100644 index 000000000..164afacd3 --- /dev/null +++ b/dom/xul/nsXULElement.h @@ -0,0 +1,707 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + + The base XUL element class and associates. + +*/ + +#ifndef nsXULElement_h__ +#define nsXULElement_h__ + +#include "js/TracingAPI.h" +#include "mozilla/Attributes.h" +#include "nsIDOMEvent.h" +#include "nsIServiceManager.h" +#include "nsIAtom.h" +#include "mozilla/dom/NodeInfo.h" +#include "nsIControllers.h" +#include "nsIDOMElement.h" +#include "nsIDOMXULElement.h" +#include "nsIDOMXULMultSelectCntrlEl.h" +#include "nsIRDFCompositeDataSource.h" +#include "nsIRDFResource.h" +#include "nsIURI.h" +#include "nsIXULTemplateBuilder.h" +#include "nsLayoutCID.h" +#include "nsAttrAndChildArray.h" +#include "nsGkAtoms.h" +#include "nsStyledElement.h" +#include "nsIFrameLoader.h" +#include "nsFrameLoader.h" +#include "mozilla/dom/DOMRect.h" +#include "mozilla/dom/ElementInlines.h" + +class nsIDocument; +class nsString; +class nsXULPrototypeDocument; + +class nsIObjectInputStream; +class nsIObjectOutputStream; +class nsIOffThreadScriptReceiver; +class nsXULPrototypeNode; +typedef nsTArray<RefPtr<nsXULPrototypeNode> > nsPrototypeArray; + +namespace mozilla { +class EventChainPreVisitor; +class EventListenerManager; +namespace css { +class StyleRule; +} // namespace css +namespace dom { +class BoxObject; +class HTMLIFrameElement; +} // namespace dom +} // namespace mozilla + +namespace JS { +class SourceBufferHolder; +} // namespace JS + +//////////////////////////////////////////////////////////////////////// + +#ifdef XUL_PROTOTYPE_ATTRIBUTE_METERING +#define XUL_PROTOTYPE_ATTRIBUTE_METER(counter) (nsXULPrototypeAttribute::counter++) +#else +#define XUL_PROTOTYPE_ATTRIBUTE_METER(counter) ((void) 0) +#endif + + +/** + + A prototype attribute for an nsXULPrototypeElement. + + */ + +class nsXULPrototypeAttribute +{ +public: + nsXULPrototypeAttribute() + : mName(nsGkAtoms::id) // XXX this is a hack, but names have to have a value + { + XUL_PROTOTYPE_ATTRIBUTE_METER(gNumAttributes); + MOZ_COUNT_CTOR(nsXULPrototypeAttribute); + } + + ~nsXULPrototypeAttribute(); + + nsAttrName mName; + nsAttrValue mValue; + +#ifdef XUL_PROTOTYPE_ATTRIBUTE_METERING + static uint32_t gNumElements; + static uint32_t gNumAttributes; + static uint32_t gNumCacheTests; + static uint32_t gNumCacheHits; + static uint32_t gNumCacheSets; + static uint32_t gNumCacheFills; +#endif /* !XUL_PROTOTYPE_ATTRIBUTE_METERING */ +}; + + +/** + + A prototype content model element that holds the "primordial" values + that have been parsed from the original XUL document. + + */ + +class nsXULPrototypeNode +{ +public: + enum Type { eType_Element, eType_Script, eType_Text, eType_PI }; + + Type mType; + + virtual nsresult Serialize(nsIObjectOutputStream* aStream, + nsXULPrototypeDocument* aProtoDoc, + const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos) = 0; + virtual nsresult Deserialize(nsIObjectInputStream* aStream, + nsXULPrototypeDocument* aProtoDoc, + nsIURI* aDocumentURI, + const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos) = 0; + + /** + * The prototype document must call ReleaseSubtree when it is going + * away. This makes the parents through the tree stop owning their + * children, whether or not the parent's reference count is zero. + * Individual elements may still own individual prototypes, but + * those prototypes no longer remember their children to allow them + * to be constructed. + */ + virtual void ReleaseSubtree() { } + + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(nsXULPrototypeNode) + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(nsXULPrototypeNode) + +protected: + explicit nsXULPrototypeNode(Type aType) + : mType(aType) {} + virtual ~nsXULPrototypeNode() {} +}; + +class nsXULPrototypeElement : public nsXULPrototypeNode +{ +public: + nsXULPrototypeElement() + : nsXULPrototypeNode(eType_Element), + mNumAttributes(0), + mHasIdAttribute(false), + mHasClassAttribute(false), + mHasStyleAttribute(false), + mAttributes(nullptr) + { + } + + virtual ~nsXULPrototypeElement() + { + Unlink(); + } + + virtual void ReleaseSubtree() override + { + for (int32_t i = mChildren.Length() - 1; i >= 0; i--) { + if (mChildren[i].get()) + mChildren[i]->ReleaseSubtree(); + } + mChildren.Clear(); + nsXULPrototypeNode::ReleaseSubtree(); + } + + virtual nsresult Serialize(nsIObjectOutputStream* aStream, + nsXULPrototypeDocument* aProtoDoc, + const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos) override; + virtual nsresult Deserialize(nsIObjectInputStream* aStream, + nsXULPrototypeDocument* aProtoDoc, + nsIURI* aDocumentURI, + const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos) override; + + nsresult SetAttrAt(uint32_t aPos, const nsAString& aValue, nsIURI* aDocumentURI); + + void Unlink(); + + // Trace all scripts held by this element and its children. + void TraceAllScripts(JSTracer* aTrc); + + nsPrototypeArray mChildren; + + RefPtr<mozilla::dom::NodeInfo> mNodeInfo; + + uint32_t mNumAttributes:29; + uint32_t mHasIdAttribute:1; + uint32_t mHasClassAttribute:1; + uint32_t mHasStyleAttribute:1; + nsXULPrototypeAttribute* mAttributes; // [OWNER] +}; + +namespace mozilla { +namespace dom { +class XULDocument; +} // namespace dom +} // namespace mozilla + +class nsXULPrototypeScript : public nsXULPrototypeNode +{ +public: + nsXULPrototypeScript(uint32_t aLineNo, uint32_t version); + virtual ~nsXULPrototypeScript(); + + virtual nsresult Serialize(nsIObjectOutputStream* aStream, + nsXULPrototypeDocument* aProtoDoc, + const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos) override; + nsresult SerializeOutOfLine(nsIObjectOutputStream* aStream, + nsXULPrototypeDocument* aProtoDoc); + virtual nsresult Deserialize(nsIObjectInputStream* aStream, + nsXULPrototypeDocument* aProtoDoc, + nsIURI* aDocumentURI, + const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos) override; + nsresult DeserializeOutOfLine(nsIObjectInputStream* aInput, + nsXULPrototypeDocument* aProtoDoc); + + nsresult Compile(JS::SourceBufferHolder& aSrcBuf, + nsIURI* aURI, uint32_t aLineNo, + nsIDocument* aDocument, + nsIOffThreadScriptReceiver *aOffThreadReceiver = nullptr); + + nsresult Compile(const char16_t* aText, int32_t aTextLength, + nsIURI* aURI, uint32_t aLineNo, + nsIDocument* aDocument, + nsIOffThreadScriptReceiver *aOffThreadReceiver = nullptr); + + void UnlinkJSObjects(); + + void Set(JSScript* aObject); + + bool HasScriptObject() + { + // Conversion to bool doesn't trigger mScriptObject's read barrier. + return mScriptObject; + } + + JSScript* GetScriptObject() + { + return mScriptObject; + } + + void TraceScriptObject(JSTracer* aTrc) + { + JS::TraceEdge(aTrc, &mScriptObject, "active window XUL prototype script"); + } + + void Trace(const TraceCallbacks& aCallbacks, void* aClosure) + { + if (mScriptObject) { + aCallbacks.Trace(&mScriptObject, "mScriptObject", aClosure); + } + } + + nsCOMPtr<nsIURI> mSrcURI; + uint32_t mLineNo; + bool mSrcLoading; + bool mOutOfLine; + mozilla::dom::XULDocument* mSrcLoadWaiters; // [OWNER] but not COMPtr + uint32_t mLangVersion; +private: + JS::Heap<JSScript*> mScriptObject; +}; + +class nsXULPrototypeText : public nsXULPrototypeNode +{ +public: + nsXULPrototypeText() + : nsXULPrototypeNode(eType_Text) + { + } + + virtual ~nsXULPrototypeText() + { + } + + virtual nsresult Serialize(nsIObjectOutputStream* aStream, + nsXULPrototypeDocument* aProtoDoc, + const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos) override; + virtual nsresult Deserialize(nsIObjectInputStream* aStream, + nsXULPrototypeDocument* aProtoDoc, + nsIURI* aDocumentURI, + const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos) override; + + nsString mValue; +}; + +class nsXULPrototypePI : public nsXULPrototypeNode +{ +public: + nsXULPrototypePI() + : nsXULPrototypeNode(eType_PI) + { + } + + virtual ~nsXULPrototypePI() + { + } + + virtual nsresult Serialize(nsIObjectOutputStream* aStream, + nsXULPrototypeDocument* aProtoDoc, + const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos) override; + virtual nsresult Deserialize(nsIObjectInputStream* aStream, + nsXULPrototypeDocument* aProtoDoc, + nsIURI* aDocumentURI, + const nsTArray<RefPtr<mozilla::dom::NodeInfo>> *aNodeInfos) override; + + nsString mTarget; + nsString mData; +}; + +//////////////////////////////////////////////////////////////////////// + +/** + + The XUL element. + + */ + +#define XUL_ELEMENT_FLAG_BIT(n_) NODE_FLAG_BIT(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + (n_)) + +// XUL element specific bits +enum { + XUL_ELEMENT_TEMPLATE_GENERATED = XUL_ELEMENT_FLAG_BIT(0), + XUL_ELEMENT_HAS_CONTENTMENU_LISTENER = XUL_ELEMENT_FLAG_BIT(1), + XUL_ELEMENT_HAS_POPUP_LISTENER = XUL_ELEMENT_FLAG_BIT(2) +}; + +ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + 3); + +#undef XUL_ELEMENT_FLAG_BIT + +class nsXULElement final : public nsStyledElement, + public nsIDOMXULElement +{ +public: + using Element::Blur; + using Element::Focus; + explicit nsXULElement(already_AddRefed<mozilla::dom::NodeInfo> aNodeInfo); + + static nsresult + Create(nsXULPrototypeElement* aPrototype, nsIDocument* aDocument, + bool aIsScriptable, bool aIsRoot, mozilla::dom::Element** aResult); + + NS_IMPL_FROMCONTENT(nsXULElement, kNameSpaceID_XUL) + + // nsISupports + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsXULElement, nsStyledElement) + + // nsINode + virtual nsresult PreHandleEvent( + mozilla::EventChainPreVisitor& aVisitor) override; + + // nsIContent + virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, + nsIContent* aBindingParent, + bool aCompileEventHandlers) override; + virtual void UnbindFromTree(bool aDeep, bool aNullParent) override; + virtual void RemoveChildAt(uint32_t aIndex, bool aNotify) override; + virtual void DestroyContent() override; + +#ifdef DEBUG + virtual void List(FILE* out, int32_t aIndent) const override; + virtual void DumpContent(FILE* out, int32_t aIndent,bool aDumpAll) const override + { + } +#endif + + virtual bool PerformAccesskey(bool aKeyCausesActivation, + bool aIsTrustedEvent) override; + nsresult ClickWithInputSource(uint16_t aInputSource, bool aIsTrustedEvent); + + virtual nsIContent *GetBindingParent() const override; + virtual bool IsNodeOfType(uint32_t aFlags) const override; + virtual bool IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse) override; + + NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker) override; + virtual nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute, + int32_t aModType) const override; + NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; + + // XUL element methods + /** + * The template-generated flag is used to indicate that a + * template-generated element has already had its children generated. + */ + void SetTemplateGenerated() { SetFlags(XUL_ELEMENT_TEMPLATE_GENERATED); } + void ClearTemplateGenerated() { UnsetFlags(XUL_ELEMENT_TEMPLATE_GENERATED); } + bool GetTemplateGenerated() { return HasFlag(XUL_ELEMENT_TEMPLATE_GENERATED); } + + // nsIDOMNode + NS_FORWARD_NSIDOMNODE_TO_NSINODE + // And since that shadowed GetParentElement with the XPCOM + // signature, pull in the one we care about. + using nsStyledElement::GetParentElement; + + // nsIDOMElement + NS_FORWARD_NSIDOMELEMENT_TO_GENERIC + + // nsIDOMXULElement + NS_DECL_NSIDOMXULELEMENT + + virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; + virtual mozilla::EventStates IntrinsicState() const override; + + nsresult GetFrameLoaderXPCOM(nsIFrameLoader** aFrameLoader); + nsresult GetParentApplication(mozIApplication** aApplication); + void PresetOpenerWindow(mozIDOMWindowProxy* aWindow, ErrorResult& aRv); + nsresult SetIsPrerendered(); + + virtual void RecompileScriptEventListeners() override; + + // This function should ONLY be used by BindToTree implementations. + // The function exists solely because XUL elements store the binding + // parent as a member instead of in the slots, as Element does. + void SetXULBindingParent(nsIContent* aBindingParent) + { + mBindingParent = aBindingParent; + } + + virtual nsIDOMNode* AsDOMNode() override { return this; } + + virtual bool IsEventAttributeName(nsIAtom* aName) override; + + void SetXULAttr(nsIAtom* aName, const nsAString& aValue, + mozilla::ErrorResult& aError) + { + aError = SetAttr(kNameSpaceID_None, aName, aValue, true); + } + void SetXULBoolAttr(nsIAtom* aName, bool aValue) + { + if (aValue) { + SetAttr(kNameSpaceID_None, aName, NS_LITERAL_STRING("true"), true); + } else { + UnsetAttr(kNameSpaceID_None, aName, true); + } + } + + // WebIDL API + // The XPCOM getter is fine for our string attributes. + // The XPCOM setter is fine for our bool attributes. + void SetClassName(const nsAString& aValue, mozilla::ErrorResult& rv) + { + SetXULAttr(nsGkAtoms::_class, aValue, rv); + } + void SetAlign(const nsAString& aValue, mozilla::ErrorResult& rv) + { + SetXULAttr(nsGkAtoms::align, aValue, rv); + } + void SetDir(const nsAString& aValue, mozilla::ErrorResult& rv) + { + SetXULAttr(nsGkAtoms::dir, aValue, rv); + } + void SetFlex(const nsAString& aValue, mozilla::ErrorResult& rv) + { + SetXULAttr(nsGkAtoms::flex, aValue, rv); + } + void SetFlexGroup(const nsAString& aValue, mozilla::ErrorResult& rv) + { + SetXULAttr(nsGkAtoms::flexgroup, aValue, rv); + } + void SetOrdinal(const nsAString& aValue, mozilla::ErrorResult& rv) + { + SetXULAttr(nsGkAtoms::ordinal, aValue, rv); + } + void SetOrient(const nsAString& aValue, mozilla::ErrorResult& rv) + { + SetXULAttr(nsGkAtoms::orient, aValue, rv); + } + void SetPack(const nsAString& aValue, mozilla::ErrorResult& rv) + { + SetXULAttr(nsGkAtoms::pack, aValue, rv); + } + bool Hidden() const + { + return BoolAttrIsTrue(nsGkAtoms::hidden); + } + bool Collapsed() const + { + return BoolAttrIsTrue(nsGkAtoms::collapsed); + } + void SetObserves(const nsAString& aValue, mozilla::ErrorResult& rv) + { + SetXULAttr(nsGkAtoms::observes, aValue, rv); + } + void SetMenu(const nsAString& aValue, mozilla::ErrorResult& rv) + { + SetXULAttr(nsGkAtoms::menu, aValue, rv); + } + void SetContextMenu(const nsAString& aValue, mozilla::ErrorResult& rv) + { + SetXULAttr(nsGkAtoms::contextmenu, aValue, rv); + } + void SetTooltip(const nsAString& aValue, mozilla::ErrorResult& rv) + { + SetXULAttr(nsGkAtoms::tooltip, aValue, rv); + } + void SetWidth(const nsAString& aValue, mozilla::ErrorResult& rv) + { + SetXULAttr(nsGkAtoms::width, aValue, rv); + } + void SetHeight(const nsAString& aValue, mozilla::ErrorResult& rv) + { + SetXULAttr(nsGkAtoms::height, aValue, rv); + } + void SetMinWidth(const nsAString& aValue, mozilla::ErrorResult& rv) + { + SetXULAttr(nsGkAtoms::minwidth, aValue, rv); + } + void SetMinHeight(const nsAString& aValue, mozilla::ErrorResult& rv) + { + SetXULAttr(nsGkAtoms::minheight, aValue, rv); + } + void SetMaxWidth(const nsAString& aValue, mozilla::ErrorResult& rv) + { + SetXULAttr(nsGkAtoms::maxwidth, aValue, rv); + } + void SetMaxHeight(const nsAString& aValue, mozilla::ErrorResult& rv) + { + SetXULAttr(nsGkAtoms::maxheight, aValue, rv); + } + void SetPersist(const nsAString& aValue, mozilla::ErrorResult& rv) + { + SetXULAttr(nsGkAtoms::persist, aValue, rv); + } + void SetLeft(const nsAString& aValue, mozilla::ErrorResult& rv) + { + SetXULAttr(nsGkAtoms::left, aValue, rv); + } + void SetTop(const nsAString& aValue, mozilla::ErrorResult& rv) + { + SetXULAttr(nsGkAtoms::top, aValue, rv); + } + void SetDatasources(const nsAString& aValue, mozilla::ErrorResult& rv) + { + SetXULAttr(nsGkAtoms::datasources, aValue, rv); + } + void SetRef(const nsAString& aValue, mozilla::ErrorResult& rv) + { + SetXULAttr(nsGkAtoms::ref, aValue, rv); + } + void SetTooltipText(const nsAString& aValue, mozilla::ErrorResult& rv) + { + SetXULAttr(nsGkAtoms::tooltiptext, aValue, rv); + } + void SetStatusText(const nsAString& aValue, mozilla::ErrorResult& rv) + { + SetXULAttr(nsGkAtoms::statustext, aValue, rv); + } + bool AllowEvents() const + { + return BoolAttrIsTrue(nsGkAtoms::allowevents); + } + already_AddRefed<nsIRDFCompositeDataSource> GetDatabase(); + already_AddRefed<nsIXULTemplateBuilder> GetBuilder(); + already_AddRefed<nsIRDFResource> GetResource(mozilla::ErrorResult& rv); + nsIControllers* GetControllers(mozilla::ErrorResult& rv); + already_AddRefed<mozilla::dom::BoxObject> GetBoxObject(mozilla::ErrorResult& rv); + void Click(mozilla::ErrorResult& rv); + // The XPCOM DoCommand never fails, so it's OK for us. + already_AddRefed<nsINodeList> + GetElementsByAttribute(const nsAString& aAttribute, + const nsAString& aValue); + already_AddRefed<nsINodeList> + GetElementsByAttributeNS(const nsAString& aNamespaceURI, + const nsAString& aAttribute, + const nsAString& aValue, + mozilla::ErrorResult& rv); + // Style() inherited from nsStyledElement + already_AddRefed<nsFrameLoader> GetFrameLoader(); + void InternalSetFrameLoader(nsIFrameLoader* aNewFrameLoader); + void SwapFrameLoaders(mozilla::dom::HTMLIFrameElement& aOtherLoaderOwner, + mozilla::ErrorResult& rv); + void SwapFrameLoaders(nsXULElement& aOtherLoaderOwner, + mozilla::ErrorResult& rv); + void SwapFrameLoaders(nsIFrameLoaderOwner* aOtherLoaderOwner, + mozilla::ErrorResult& rv); + + nsINode* GetScopeChainParent() const override + { + // For XUL, the parent is the parent element, if any + Element* parent = GetParentElement(); + return parent ? parent : nsStyledElement::GetScopeChainParent(); + } + +protected: + ~nsXULElement(); + + // This can be removed if EnsureContentsGenerated dies. + friend class nsNSElementTearoff; + + // Implementation methods + nsresult EnsureContentsGenerated(void) const; + + nsresult ExecuteOnBroadcastHandler(nsIDOMElement* anElement, const nsAString& attrName); + + static nsresult + ExecuteJSCode(nsIDOMElement* anElement, mozilla::WidgetEvent* aEvent); + + // Helper routine that crawls a parent chain looking for a tree element. + NS_IMETHOD GetParentTree(nsIDOMXULMultiSelectControlElement** aTreeElement); + + nsresult AddPopupListener(nsIAtom* aName); + + class nsXULSlots : public mozilla::dom::Element::nsDOMSlots + { + public: + nsXULSlots(); + virtual ~nsXULSlots(); + + void Traverse(nsCycleCollectionTraversalCallback &cb); + + nsCOMPtr<nsISupports> mFrameLoaderOrOpener; + }; + + virtual nsINode::nsSlots* CreateSlots() override; + + nsresult LoadSrc(); + + /** + * The nearest enclosing content node with a binding + * that created us. [Weak] + */ + nsIContent* mBindingParent; + + /** + * Abandon our prototype linkage, and copy all attributes locally + */ + nsresult MakeHeavyweight(nsXULPrototypeElement* aPrototype); + + virtual nsresult BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, + nsAttrValueOrString* aValue, + bool aNotify) override; + virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, + const nsAttrValue* aValue, bool aNotify) override; + + virtual void UpdateEditableState(bool aNotify) override; + + virtual bool ParseAttribute(int32_t aNamespaceID, + nsIAtom* aAttribute, + const nsAString& aValue, + nsAttrValue& aResult) override; + + virtual mozilla::EventListenerManager* + GetEventListenerManagerForAttr(nsIAtom* aAttrName, + bool* aDefer) override; + + /** + * Add a listener for the specified attribute, if appropriate. + */ + void AddListenerFor(const nsAttrName& aName, + bool aCompileEventHandlers); + void MaybeAddPopupListener(nsIAtom* aLocalName); + + nsIWidget* GetWindowWidget(); + + // attribute setters for widget + nsresult HideWindowChrome(bool aShouldHide); + void SetChromeMargins(const nsAttrValue* aValue); + void ResetChromeMargins(); + void SetTitlebarColor(nscolor aColor, bool aActive); + + void SetDrawsInTitlebar(bool aState); + void SetDrawsTitle(bool aState); + void UpdateBrightTitlebarForeground(nsIDocument* aDocument); + + void RemoveBroadcaster(const nsAString & broadcasterId); + +protected: + // Internal accessor. This shadows the 'Slots', and returns + // appropriate value. + nsIControllers *Controllers() { + nsDOMSlots* slots = GetExistingDOMSlots(); + return slots ? slots->mControllers : nullptr; + } + + void UnregisterAccessKey(const nsAString& aOldValue); + bool BoolAttrIsTrue(nsIAtom* aName) const; + + friend nsresult + NS_NewXULElement(mozilla::dom::Element** aResult, mozilla::dom::NodeInfo *aNodeInfo); + friend void + NS_TrustedNewXULElement(nsIContent** aResult, mozilla::dom::NodeInfo *aNodeInfo); + + static already_AddRefed<nsXULElement> + Create(nsXULPrototypeElement* aPrototype, mozilla::dom::NodeInfo *aNodeInfo, + bool aIsScriptable, bool aIsRoot); + + bool IsReadWriteTextElement() const + { + return IsAnyOfXULElements(nsGkAtoms::textbox, nsGkAtoms::textarea) && + !HasAttr(kNameSpaceID_None, nsGkAtoms::readonly); + } + + virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override; + + void MaybeUpdatePrivateLifetime(); +}; + +#endif // nsXULElement_h__ diff --git a/dom/xul/nsXULPopupListener.cpp b/dom/xul/nsXULPopupListener.cpp new file mode 100644 index 000000000..889553c4d --- /dev/null +++ b/dom/xul/nsXULPopupListener.cpp @@ -0,0 +1,442 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + This file provides the implementation for xul popup listener which + tracks xul popups and context menus + */ + +#include "nsXULPopupListener.h" +#include "nsCOMPtr.h" +#include "nsGkAtoms.h" +#include "nsIDOMElement.h" +#include "nsIDOMXULElement.h" +#include "nsIDOMNodeList.h" +#include "nsIDOMDocument.h" +#include "nsIDOMDocumentXBL.h" +#include "nsContentCID.h" +#include "nsContentUtils.h" +#include "nsXULPopupManager.h" +#include "nsIScriptContext.h" +#include "nsIDOMWindow.h" +#include "nsIDOMXULDocument.h" +#include "nsIDocument.h" +#include "nsServiceManagerUtils.h" +#include "nsIPrincipal.h" +#include "nsIScriptSecurityManager.h" +#include "nsLayoutUtils.h" +#include "mozilla/ReflowInput.h" +#include "nsIObjectLoadingContent.h" +#include "mozilla/EventStateManager.h" +#include "mozilla/EventStates.h" +#include "mozilla/Preferences.h" +#include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent() +#include "mozilla/dom/EventTarget.h" +#include "mozilla/dom/FragmentOrElement.h" + +// for event firing in context menus +#include "nsPresContext.h" +#include "nsIPresShell.h" +#include "nsFocusManager.h" +#include "nsPIDOMWindow.h" +#include "nsViewManager.h" +#include "nsError.h" +#include "nsMenuFrame.h" + +using namespace mozilla; +using namespace mozilla::dom; + +// on win32 and os/2, context menus come up on mouse up. On other platforms, +// they appear on mouse down. Certain bits of code care about this difference. +#if defined(XP_WIN) +#define NS_CONTEXT_MENU_IS_MOUSEUP 1 +#endif + +nsXULPopupListener::nsXULPopupListener(mozilla::dom::Element* aElement, + bool aIsContext) + : mElement(aElement), mPopupContent(nullptr), mIsContext(aIsContext) +{ +} + +nsXULPopupListener::~nsXULPopupListener(void) +{ + ClosePopup(); +} + +NS_IMPL_CYCLE_COLLECTION(nsXULPopupListener, mElement, mPopupContent) +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULPopupListener) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULPopupListener) + +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsXULPopupListener) + // If the owner, mElement, can be skipped, so can we. + if (tmp->mElement) { + return mozilla::dom::FragmentOrElement::CanSkip(tmp->mElement, true); + } +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END + +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsXULPopupListener) + if (tmp->mElement) { + return mozilla::dom::FragmentOrElement::CanSkipInCC(tmp->mElement); + } +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END + +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsXULPopupListener) + if (tmp->mElement) { + return mozilla::dom::FragmentOrElement::CanSkipThis(tmp->mElement); + } +NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULPopupListener) + NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +//////////////////////////////////////////////////////////////// +// nsIDOMEventListener + +nsresult +nsXULPopupListener::HandleEvent(nsIDOMEvent* aEvent) +{ + nsAutoString eventType; + aEvent->GetType(eventType); + + if(!((eventType.EqualsLiteral("mousedown") && !mIsContext) || + (eventType.EqualsLiteral("contextmenu") && mIsContext))) + return NS_OK; + + int16_t button; + + nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aEvent); + if (!mouseEvent) { + //non-ui event passed in. bad things. + return NS_OK; + } + + // Get the node that was clicked on. + EventTarget* target = mouseEvent->AsEvent()->InternalDOMEvent()->GetTarget(); + nsCOMPtr<nsIDOMNode> targetNode = do_QueryInterface(target); + + if (!targetNode && mIsContext) { + // Not a DOM node, see if it's the DOM window (bug 380818). + nsCOMPtr<nsPIDOMWindowInner> domWin = do_QueryInterface(target); + if (!domWin) { + return NS_ERROR_DOM_WRONG_TYPE_ERR; + } + // Try to use the root node as target node. + nsCOMPtr<nsIDocument> doc = domWin->GetDoc(); + + if (doc) + targetNode = do_QueryInterface(doc->GetRootElement()); + if (!targetNode) { + return NS_ERROR_FAILURE; + } + } + + nsCOMPtr<nsIContent> targetContent = do_QueryInterface(target); + if (!targetContent) { + return NS_OK; + } + if (EventStateManager::IsRemoteTarget(targetContent)) { + return NS_OK; + } + + bool preventDefault; + mouseEvent->AsEvent()->GetDefaultPrevented(&preventDefault); + if (preventDefault && targetNode && mIsContext) { + // Someone called preventDefault on a context menu. + // Let's make sure they are allowed to do so. + bool eventEnabled = + Preferences::GetBool("dom.event.contextmenu.enabled", true); + if (!eventEnabled) { + // If the target node is for plug-in, we should not open XUL context + // menu on windowless plug-ins. + nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(targetNode); + uint32_t type; + if (olc && NS_SUCCEEDED(olc->GetDisplayedType(&type)) && + type == nsIObjectLoadingContent::TYPE_PLUGIN) { + return NS_OK; + } + + // The user wants his contextmenus. Let's make sure that this is a website + // and not chrome since there could be places in chrome which don't want + // contextmenus. + nsCOMPtr<nsINode> node = do_QueryInterface(targetNode); + if (node) { + nsCOMPtr<nsIPrincipal> system; + nsContentUtils::GetSecurityManager()-> + GetSystemPrincipal(getter_AddRefs(system)); + if (node->NodePrincipal() != system) { + // This isn't chrome. Cancel the preventDefault() and + // let the event go forth. + preventDefault = false; + } + } + } + } + + if (preventDefault) { + // someone called preventDefault. bail. + return NS_OK; + } + + // prevent popups on menu and menuitems as they handle their own popups + // This was added for bug 96920. + // If a menu item child was clicked on that leads to a popup needing + // to show, we know (guaranteed) that we're dealing with a menu or + // submenu of an already-showing popup. We don't need to do anything at all. + if (!mIsContext) { + if (targetContent && + targetContent->IsAnyOfXULElements(nsGkAtoms::menu, nsGkAtoms::menuitem)) + return NS_OK; + } + + if (mIsContext) { +#ifndef NS_CONTEXT_MENU_IS_MOUSEUP + uint16_t inputSource = nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN; + mouseEvent->GetMozInputSource(&inputSource); + bool isTouch = inputSource == nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; + // If the context menu launches on mousedown, + // we have to fire focus on the content we clicked on + FireFocusOnTargetContent(targetNode, isTouch); +#endif + } + else { + // Only open popups when the left mouse button is down. + mouseEvent->GetButton(&button); + if (button != 0) + return NS_OK; + } + + // Open the popup. LaunchPopup will call StopPropagation and PreventDefault + // in the right situations. + LaunchPopup(aEvent, targetContent); + + return NS_OK; +} + +#ifndef NS_CONTEXT_MENU_IS_MOUSEUP +nsresult +nsXULPopupListener::FireFocusOnTargetContent(nsIDOMNode* aTargetNode, bool aIsTouch) +{ + nsresult rv; + nsCOMPtr<nsIDOMDocument> domDoc; + rv = aTargetNode->GetOwnerDocument(getter_AddRefs(domDoc)); + if(NS_SUCCEEDED(rv) && domDoc) + { + nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc); + + // Get nsIDOMElement for targetNode + nsIPresShell *shell = doc->GetShell(); + if (!shell) + return NS_ERROR_FAILURE; + + // strong reference to keep this from going away between events + // XXXbz between what events? We don't use this local at all! + RefPtr<nsPresContext> context = shell->GetPresContext(); + + nsCOMPtr<nsIContent> content = do_QueryInterface(aTargetNode); + nsIFrame* targetFrame = content->GetPrimaryFrame(); + if (!targetFrame) return NS_ERROR_FAILURE; + + const nsStyleUserInterface* ui = targetFrame->StyleUserInterface(); + bool suppressBlur = (ui->mUserFocus == StyleUserFocus::Ignore); + + nsCOMPtr<nsIDOMElement> element; + nsCOMPtr<nsIContent> newFocus = do_QueryInterface(content); + + nsIFrame* currFrame = targetFrame; + // Look for the nearest enclosing focusable frame. + while (currFrame) { + int32_t tabIndexUnused; + if (currFrame->IsFocusable(&tabIndexUnused, true)) { + newFocus = currFrame->GetContent(); + nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(newFocus)); + if (domElement) { + element = domElement; + break; + } + } + currFrame = currFrame->GetParent(); + } + + nsIFocusManager* fm = nsFocusManager::GetFocusManager(); + if (fm) { + if (element) { + uint32_t focusFlags = nsIFocusManager::FLAG_BYMOUSE | + nsIFocusManager::FLAG_NOSCROLL; + if (aIsTouch) { + focusFlags |= nsIFocusManager::FLAG_BYTOUCH; + } + fm->SetFocus(element, focusFlags); + } else if (!suppressBlur) { + nsPIDOMWindowOuter *window = doc->GetWindow(); + fm->ClearFocus(window); + } + } + + EventStateManager* esm = context->EventStateManager(); + nsCOMPtr<nsIContent> focusableContent = do_QueryInterface(element); + esm->SetContentState(focusableContent, NS_EVENT_STATE_ACTIVE); + } + return rv; +} +#endif + +// ClosePopup +// +// Do everything needed to shut down the popup. +// +// NOTE: This routine is safe to call even if the popup is already closed. +// +void +nsXULPopupListener::ClosePopup() +{ + if (mPopupContent) { + // this is called when the listener is going away, so make sure that the + // popup is hidden. Use asynchronous hiding just to be safe so we don't + // fire events during destruction. + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm) + pm->HidePopup(mPopupContent, false, true, true, false); + mPopupContent = nullptr; // release the popup + } +} // ClosePopup + +static already_AddRefed<nsIContent> +GetImmediateChild(nsIContent* aContent, nsIAtom *aTag) +{ + for (nsIContent* child = aContent->GetFirstChild(); + child; + child = child->GetNextSibling()) { + if (child->IsXULElement(aTag)) { + nsCOMPtr<nsIContent> ret = child; + return ret.forget(); + } + } + + return nullptr; +} + +// +// LaunchPopup +// +// Given the element on which the event was triggered and the mouse locations in +// Client and widget coordinates, popup a new window showing the appropriate +// content. +// +// aTargetContent is the target of the mouse event aEvent that triggered the +// popup. mElement is the element that the popup menu is attached to. +// aTargetContent may be equal to mElement or it may be a descendant. +// +// This looks for an attribute on |mElement| of the appropriate popup type +// (popup, context) and uses that attribute's value as an ID for +// the popup content in the document. +// +nsresult +nsXULPopupListener::LaunchPopup(nsIDOMEvent* aEvent, nsIContent* aTargetContent) +{ + nsresult rv = NS_OK; + + nsAutoString identifier; + nsIAtom* type = mIsContext ? nsGkAtoms::context : nsGkAtoms::popup; + bool hasPopupAttr = mElement->GetAttr(kNameSpaceID_None, type, identifier); + + if (identifier.IsEmpty()) { + hasPopupAttr = mElement->GetAttr(kNameSpaceID_None, + mIsContext ? nsGkAtoms::contextmenu : nsGkAtoms::menu, + identifier) || hasPopupAttr; + } + + if (hasPopupAttr) { + aEvent->StopPropagation(); + aEvent->PreventDefault(); + } + + if (identifier.IsEmpty()) + return rv; + + // Try to find the popup content and the document. + nsCOMPtr<nsIDocument> document = mElement->GetComposedDoc(); + if (!document) { + NS_WARNING("No document!"); + return NS_ERROR_FAILURE; + } + + // Handle the _child case for popups and context menus + nsCOMPtr<nsIContent> popup; + if (identifier.EqualsLiteral("_child")) { + popup = GetImmediateChild(mElement, nsGkAtoms::menupopup); + if (!popup) { + nsCOMPtr<nsIDOMDocumentXBL> nsDoc(do_QueryInterface(document)); + nsCOMPtr<nsIDOMNodeList> list; + nsCOMPtr<nsIDOMElement> el = do_QueryInterface(mElement); + nsDoc->GetAnonymousNodes(el, getter_AddRefs(list)); + if (list) { + uint32_t ctr,listLength; + nsCOMPtr<nsIDOMNode> node; + list->GetLength(&listLength); + for (ctr = 0; ctr < listLength; ctr++) { + list->Item(ctr, getter_AddRefs(node)); + nsCOMPtr<nsIContent> childContent(do_QueryInterface(node)); + + if (childContent->NodeInfo()->Equals(nsGkAtoms::menupopup, + kNameSpaceID_XUL)) { + popup.swap(childContent); + break; + } + } + } + } + } else if (!mElement->IsInUncomposedDoc() || + !(popup = document->GetElementById(identifier))) { + // XXXsmaug Should we try to use ShadowRoot::GetElementById in case + // mElement is in shadow DOM? + // + // Use getElementById to obtain the popup content and gracefully fail if + // we didn't find any popup content in the document. + NS_WARNING("GetElementById had some kind of spasm."); + return rv; + } + + // return if no popup was found or the popup is the element itself. + if (!popup || popup == mElement) + return NS_OK; + + // Submenus can't be used as context menus or popups, bug 288763. + // Similar code also in nsXULTooltipListener::GetTooltipFor. + nsIContent* parent = popup->GetParent(); + if (parent) { + nsMenuFrame* menu = do_QueryFrame(parent->GetPrimaryFrame()); + if (menu) + return NS_OK; + } + + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (!pm) + return NS_OK; + + // For left-clicks, if the popup has an position attribute, or both the + // popupanchor and popupalign attributes are used, anchor the popup to the + // element, otherwise just open it at the screen position where the mouse + // was clicked. Context menus always open at the mouse position. + mPopupContent = popup; + if (!mIsContext && + (mPopupContent->HasAttr(kNameSpaceID_None, nsGkAtoms::position) || + (mPopupContent->HasAttr(kNameSpaceID_None, nsGkAtoms::popupanchor) && + mPopupContent->HasAttr(kNameSpaceID_None, nsGkAtoms::popupalign)))) { + pm->ShowPopup(mPopupContent, mElement, EmptyString(), 0, 0, + false, true, false, aEvent); + } + else { + int32_t xPos = 0, yPos = 0; + nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aEvent); + mouseEvent->GetScreenX(&xPos); + mouseEvent->GetScreenY(&yPos); + + pm->ShowPopupAtScreen(mPopupContent, xPos, yPos, mIsContext, aEvent); + } + + return NS_OK; +} diff --git a/dom/xul/nsXULPopupListener.h b/dom/xul/nsXULPopupListener.h new file mode 100644 index 000000000..6ac0e0e54 --- /dev/null +++ b/dom/xul/nsXULPopupListener.h @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * This is the popup listener implementation for popup menus and context menus. + */ + +#ifndef nsXULPopupListener_h___ +#define nsXULPopupListener_h___ + +#include "nsCOMPtr.h" + +#include "mozilla/dom/Element.h" +#include "nsIDOMElement.h" +#include "nsIDOMMouseEvent.h" +#include "nsIDOMEventListener.h" +#include "nsCycleCollectionParticipant.h" + +class nsXULPopupListener : public nsIDOMEventListener +{ +public: + // aElement is the element that the popup is attached to. If aIsContext is + // false, the popup opens on left click on aElement or a descendant. If + // aIsContext is true, the popup is a context menu which opens on a + // context menu event. + nsXULPopupListener(mozilla::dom::Element* aElement, bool aIsContext); + + // nsISupports + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SKIPPABLE_CLASS(nsXULPopupListener) + NS_DECL_NSIDOMEVENTLISTENER + +protected: + virtual ~nsXULPopupListener(void); + + // open the popup. aEvent is the event that triggered the popup such as + // a mouse click and aTargetContent is the target of this event. + virtual nsresult LaunchPopup(nsIDOMEvent* aEvent, nsIContent* aTargetContent); + + // close the popup when the listener goes away + virtual void ClosePopup(); + +private: +#ifndef NS_CONTEXT_MENU_IS_MOUSEUP + // When a context menu is opened, focus the target of the contextmenu event. + nsresult FireFocusOnTargetContent(nsIDOMNode* aTargetNode, bool aIsTouch); +#endif + + // |mElement| is the node to which this listener is attached. + nsCOMPtr<mozilla::dom::Element> mElement; + + // The popup that is getting shown on top of mElement. + nsCOMPtr<nsIContent> mPopupContent; + + // true if a context popup + bool mIsContext; +}; + +#endif // nsXULPopupListener_h___ diff --git a/dom/xul/nsXULPrototypeCache.cpp b/dom/xul/nsXULPrototypeCache.cpp new file mode 100644 index 000000000..84a201d59 --- /dev/null +++ b/dom/xul/nsXULPrototypeCache.cpp @@ -0,0 +1,599 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsXULPrototypeCache.h" + +#include "plstr.h" +#include "nsXULPrototypeDocument.h" +#include "nsIServiceManager.h" +#include "nsIURI.h" + +#include "nsIChromeRegistry.h" +#include "nsIFile.h" +#include "nsIObjectInputStream.h" +#include "nsIObjectOutputStream.h" +#include "nsIObserverService.h" +#include "nsIStringStream.h" +#include "nsIStorageStream.h" + +#include "nsAppDirectoryServiceDefs.h" + +#include "js/TracingAPI.h" + +#include "mozilla/StyleSheetInlines.h" +#include "mozilla/Preferences.h" +#include "mozilla/scache/StartupCache.h" +#include "mozilla/scache/StartupCacheUtils.h" +#include "mozilla/Telemetry.h" + +using namespace mozilla; +using namespace mozilla::scache; + +static bool gDisableXULCache = false; // enabled by default +static const char kDisableXULCachePref[] = "nglayout.debug.disable_xul_cache"; +static const char kXULCacheInfoKey[] = "nsXULPrototypeCache.startupCache"; +static const char kXULCachePrefix[] = "xulcache"; + +//---------------------------------------------------------------------- + +static void +UpdategDisableXULCache() +{ + // Get the value of "nglayout.debug.disable_xul_cache" preference + gDisableXULCache = + Preferences::GetBool(kDisableXULCachePref, gDisableXULCache); + + // Sets the flag if the XUL cache is disabled + if (gDisableXULCache) { + Telemetry::Accumulate(Telemetry::XUL_CACHE_DISABLED, true); + } + +} + +static void +DisableXULCacheChangedCallback(const char* aPref, void* aClosure) +{ + bool wasEnabled = !gDisableXULCache; + UpdategDisableXULCache(); + + if (wasEnabled && gDisableXULCache) { + nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance(); + if (cache) { + // AbortCaching() calls Flush() for us. + cache->AbortCaching(); + } + } +} + +//---------------------------------------------------------------------- + +nsXULPrototypeCache* nsXULPrototypeCache::sInstance = nullptr; + + +nsXULPrototypeCache::nsXULPrototypeCache() +{ +} + + +nsXULPrototypeCache::~nsXULPrototypeCache() +{ + FlushScripts(); +} + + +NS_IMPL_ISUPPORTS(nsXULPrototypeCache, nsIObserver) + +/* static */ nsXULPrototypeCache* +nsXULPrototypeCache::GetInstance() +{ + if (!sInstance) { + NS_ADDREF(sInstance = new nsXULPrototypeCache()); + + UpdategDisableXULCache(); + + Preferences::RegisterCallback(DisableXULCacheChangedCallback, + kDisableXULCachePref); + + nsCOMPtr<nsIObserverService> obsSvc = + mozilla::services::GetObserverService(); + if (obsSvc) { + nsXULPrototypeCache *p = sInstance; + obsSvc->AddObserver(p, "chrome-flush-skin-caches", false); + obsSvc->AddObserver(p, "chrome-flush-caches", false); + obsSvc->AddObserver(p, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); + obsSvc->AddObserver(p, "startupcache-invalidate", false); + } + + } + return sInstance; +} + +//---------------------------------------------------------------------- + +NS_IMETHODIMP +nsXULPrototypeCache::Observe(nsISupports* aSubject, + const char *aTopic, + const char16_t *aData) +{ + if (!strcmp(aTopic, "chrome-flush-skin-caches")) { + FlushSkinFiles(); + } + else if (!strcmp(aTopic, "chrome-flush-caches") || + !strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { + Flush(); + } + else if (!strcmp(aTopic, "startupcache-invalidate")) { + AbortCaching(); + } + else { + NS_WARNING("Unexpected observer topic."); + } + return NS_OK; +} + +nsXULPrototypeDocument* +nsXULPrototypeCache::GetPrototype(nsIURI* aURI) +{ + if (!aURI) + return nullptr; + + nsCOMPtr<nsIURI> uriWithoutRef; + aURI->CloneIgnoringRef(getter_AddRefs(uriWithoutRef)); + + nsXULPrototypeDocument* protoDoc = mPrototypeTable.GetWeak(uriWithoutRef); + if (protoDoc) + return protoDoc; + + nsresult rv = BeginCaching(aURI); + if (NS_FAILED(rv)) + return nullptr; + + // No prototype in XUL memory cache. Spin up the cache Service. + nsCOMPtr<nsIObjectInputStream> ois; + rv = GetInputStream(aURI, getter_AddRefs(ois)); + if (NS_FAILED(rv)) + return nullptr; + + RefPtr<nsXULPrototypeDocument> newProto; + rv = NS_NewXULPrototypeDocument(getter_AddRefs(newProto)); + if (NS_FAILED(rv)) + return nullptr; + + rv = newProto->Read(ois); + if (NS_SUCCEEDED(rv)) { + rv = PutPrototype(newProto); + } else { + newProto = nullptr; + } + + mInputStreamTable.Remove(aURI); + return newProto; +} + +nsresult +nsXULPrototypeCache::PutPrototype(nsXULPrototypeDocument* aDocument) +{ + if (!aDocument->GetURI()) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr<nsIURI> uri; + aDocument->GetURI()->CloneIgnoringRef(getter_AddRefs(uri)); + + // Put() releases any old value and addrefs the new one + mPrototypeTable.Put(uri, aDocument); + + return NS_OK; +} + +nsresult +nsXULPrototypeCache::PutStyleSheet(CSSStyleSheet* aStyleSheet) +{ + nsIURI* uri = aStyleSheet->GetSheetURI(); + + mStyleSheetTable.Put(uri, aStyleSheet); + + return NS_OK; +} + + +JSScript* +nsXULPrototypeCache::GetScript(nsIURI* aURI) +{ + return mScriptTable.Get(aURI); +} + +nsresult +nsXULPrototypeCache::PutScript(nsIURI* aURI, + JS::Handle<JSScript*> aScriptObject) +{ + MOZ_ASSERT(aScriptObject, "Need a non-NULL script"); + +#ifdef DEBUG_BUG_392650 + if (mScriptTable.Get(aURI)) { + nsAutoCString scriptName; + aURI->GetSpec(scriptName); + nsAutoCString message("Loaded script "); + message += scriptName; + message += " twice (bug 392650)"; + NS_WARNING(message.get()); + } +#endif + + mScriptTable.Put(aURI, aScriptObject); + + return NS_OK; +} + +nsresult +nsXULPrototypeCache::PutXBLDocumentInfo(nsXBLDocumentInfo* aDocumentInfo) +{ + nsIURI* uri = aDocumentInfo->DocumentURI(); + + RefPtr<nsXBLDocumentInfo> info; + mXBLDocTable.Get(uri, getter_AddRefs(info)); + if (!info) { + mXBLDocTable.Put(uri, aDocumentInfo); + } + return NS_OK; +} + +void +nsXULPrototypeCache::FlushSkinFiles() +{ + // Flush out skin XBL files from the cache. + for (auto iter = mXBLDocTable.Iter(); !iter.Done(); iter.Next()) { + nsAutoCString str; + iter.Key()->GetPath(str); + if (strncmp(str.get(), "/skin", 5) == 0) { + iter.Remove(); + } + } + + // Now flush out our skin stylesheets from the cache. + for (auto iter = mStyleSheetTable.Iter(); !iter.Done(); iter.Next()) { + nsAutoCString str; + iter.Data()->GetSheetURI()->GetPath(str); + if (strncmp(str.get(), "/skin", 5) == 0) { + iter.Remove(); + } + } + + // Iterate over all the remaining XBL and make sure cached + // scoped skin stylesheets are flushed and refetched by the + // prototype bindings. + for (auto iter = mXBLDocTable.Iter(); !iter.Done(); iter.Next()) { + iter.Data()->FlushSkinStylesheets(); + } +} + +void +nsXULPrototypeCache::FlushScripts() +{ + mScriptTable.Clear(); +} + +void +nsXULPrototypeCache::Flush() +{ + mPrototypeTable.Clear(); + mScriptTable.Clear(); + mStyleSheetTable.Clear(); + mXBLDocTable.Clear(); +} + + +bool +nsXULPrototypeCache::IsEnabled() +{ + return !gDisableXULCache; +} + +void +nsXULPrototypeCache::AbortCaching() +{ +#ifdef DEBUG_brendan + NS_BREAK(); +#endif + + // Flush the XUL cache for good measure, in case we cached a bogus/downrev + // script, somehow. + Flush(); + + // Clear the cache set + mStartupCacheURITable.Clear(); +} + + +nsresult +nsXULPrototypeCache::WritePrototype(nsXULPrototypeDocument* aPrototypeDocument) +{ + nsresult rv = NS_OK, rv2 = NS_OK; + + if (!StartupCache::GetSingleton()) + return NS_OK; + + nsCOMPtr<nsIURI> protoURI = aPrototypeDocument->GetURI(); + + nsCOMPtr<nsIObjectOutputStream> oos; + rv = GetOutputStream(protoURI, getter_AddRefs(oos)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aPrototypeDocument->Write(oos); + NS_ENSURE_SUCCESS(rv, rv); + FinishOutputStream(protoURI); + return NS_FAILED(rv) ? rv : rv2; +} + +nsresult +nsXULPrototypeCache::GetInputStream(nsIURI* uri, nsIObjectInputStream** stream) +{ + nsAutoCString spec(kXULCachePrefix); + nsresult rv = PathifyURI(uri, spec); + if (NS_FAILED(rv)) + return NS_ERROR_NOT_AVAILABLE; + + UniquePtr<char[]> buf; + uint32_t len; + nsCOMPtr<nsIObjectInputStream> ois; + StartupCache* sc = StartupCache::GetSingleton(); + if (!sc) + return NS_ERROR_NOT_AVAILABLE; + + rv = sc->GetBuffer(spec.get(), &buf, &len); + if (NS_FAILED(rv)) + return NS_ERROR_NOT_AVAILABLE; + + rv = NewObjectInputStreamFromBuffer(Move(buf), len, getter_AddRefs(ois)); + NS_ENSURE_SUCCESS(rv, rv); + + mInputStreamTable.Put(uri, ois); + + ois.forget(stream); + return NS_OK; +} + +nsresult +nsXULPrototypeCache::FinishInputStream(nsIURI* uri) { + mInputStreamTable.Remove(uri); + return NS_OK; +} + +nsresult +nsXULPrototypeCache::GetOutputStream(nsIURI* uri, nsIObjectOutputStream** stream) +{ + nsresult rv; + nsCOMPtr<nsIObjectOutputStream> objectOutput; + nsCOMPtr<nsIStorageStream> storageStream; + bool found = mOutputStreamTable.Get(uri, getter_AddRefs(storageStream)); + if (found) { + objectOutput = do_CreateInstance("mozilla.org/binaryoutputstream;1"); + if (!objectOutput) return NS_ERROR_OUT_OF_MEMORY; + nsCOMPtr<nsIOutputStream> outputStream + = do_QueryInterface(storageStream); + objectOutput->SetOutputStream(outputStream); + } else { + rv = NewObjectOutputWrappedStorageStream(getter_AddRefs(objectOutput), + getter_AddRefs(storageStream), + false); + NS_ENSURE_SUCCESS(rv, rv); + mOutputStreamTable.Put(uri, storageStream); + } + objectOutput.forget(stream); + return NS_OK; +} + +nsresult +nsXULPrototypeCache::FinishOutputStream(nsIURI* uri) +{ + nsresult rv; + StartupCache* sc = StartupCache::GetSingleton(); + if (!sc) + return NS_ERROR_NOT_AVAILABLE; + + nsCOMPtr<nsIStorageStream> storageStream; + bool found = mOutputStreamTable.Get(uri, getter_AddRefs(storageStream)); + if (!found) + return NS_ERROR_UNEXPECTED; + nsCOMPtr<nsIOutputStream> outputStream + = do_QueryInterface(storageStream); + outputStream->Close(); + + UniquePtr<char[]> buf; + uint32_t len; + rv = NewBufferFromStorageStream(storageStream, &buf, &len); + NS_ENSURE_SUCCESS(rv, rv); + + if (!mStartupCacheURITable.GetEntry(uri)) { + nsAutoCString spec(kXULCachePrefix); + rv = PathifyURI(uri, spec); + if (NS_FAILED(rv)) + return NS_ERROR_NOT_AVAILABLE; + rv = sc->PutBuffer(spec.get(), buf.get(), len); + if (NS_SUCCEEDED(rv)) { + mOutputStreamTable.Remove(uri); + mStartupCacheURITable.PutEntry(uri); + } + } + + return rv; +} + +// We have data if we're in the middle of writing it or we already +// have it in the cache. +nsresult +nsXULPrototypeCache::HasData(nsIURI* uri, bool* exists) +{ + if (mOutputStreamTable.Get(uri, nullptr)) { + *exists = true; + return NS_OK; + } + nsAutoCString spec(kXULCachePrefix); + nsresult rv = PathifyURI(uri, spec); + if (NS_FAILED(rv)) { + *exists = false; + return NS_OK; + } + UniquePtr<char[]> buf; + uint32_t len; + StartupCache* sc = StartupCache::GetSingleton(); + if (sc) { + rv = sc->GetBuffer(spec.get(), &buf, &len); + } else { + *exists = false; + return NS_OK; + } + *exists = NS_SUCCEEDED(rv); + return NS_OK; +} + +nsresult +nsXULPrototypeCache::BeginCaching(nsIURI* aURI) +{ + nsresult rv, tmp; + + nsAutoCString path; + aURI->GetPath(path); + if (!StringEndsWith(path, NS_LITERAL_CSTRING(".xul"))) + return NS_ERROR_NOT_AVAILABLE; + + StartupCache* startupCache = StartupCache::GetSingleton(); + if (!startupCache) + return NS_ERROR_FAILURE; + + if (gDisableXULCache) + return NS_ERROR_NOT_AVAILABLE; + + // Get the chrome directory to validate against the one stored in the + // cache file, or to store there if we're generating a new file. + nsCOMPtr<nsIFile> chromeDir; + rv = NS_GetSpecialDirectory(NS_APP_CHROME_DIR, getter_AddRefs(chromeDir)); + if (NS_FAILED(rv)) + return rv; + nsAutoCString chromePath; + rv = chromeDir->GetNativePath(chromePath); + if (NS_FAILED(rv)) + return rv; + + // XXXbe we assume the first package's locale is the same as the locale of + // all subsequent packages of cached chrome URIs.... + nsAutoCString package; + rv = aURI->GetHost(package); + if (NS_FAILED(rv)) + return rv; + nsCOMPtr<nsIXULChromeRegistry> chromeReg + = do_GetService(NS_CHROMEREGISTRY_CONTRACTID, &rv); + nsAutoCString locale; + rv = chromeReg->GetSelectedLocale(package, false, locale); + if (NS_FAILED(rv)) + return rv; + + nsAutoCString fileChromePath, fileLocale; + + UniquePtr<char[]> buf; + uint32_t len, amtRead; + nsCOMPtr<nsIObjectInputStream> objectInput; + + rv = startupCache->GetBuffer(kXULCacheInfoKey, &buf, &len); + if (NS_SUCCEEDED(rv)) + rv = NewObjectInputStreamFromBuffer(Move(buf), len, + getter_AddRefs(objectInput)); + + if (NS_SUCCEEDED(rv)) { + rv = objectInput->ReadCString(fileLocale); + tmp = objectInput->ReadCString(fileChromePath); + if (NS_FAILED(tmp)) { + rv = tmp; + } + if (NS_FAILED(rv) || + (!fileChromePath.Equals(chromePath) || + !fileLocale.Equals(locale))) { + // Our cache won't be valid in this case, we'll need to rewrite. + // XXX This blows away work that other consumers (like + // mozJSComponentLoader) have done, need more fine-grained control. + startupCache->InvalidateCache(); + mStartupCacheURITable.Clear(); + rv = NS_ERROR_UNEXPECTED; + } + } else if (rv != NS_ERROR_NOT_AVAILABLE) + // NS_ERROR_NOT_AVAILABLE is normal, usually if there's no cachefile. + return rv; + + if (NS_FAILED(rv)) { + // Either the cache entry was invalid or it didn't exist, so write it now. + nsCOMPtr<nsIObjectOutputStream> objectOutput; + nsCOMPtr<nsIInputStream> inputStream; + nsCOMPtr<nsIStorageStream> storageStream; + rv = NewObjectOutputWrappedStorageStream(getter_AddRefs(objectOutput), + getter_AddRefs(storageStream), + false); + if (NS_SUCCEEDED(rv)) { + rv = objectOutput->WriteStringZ(locale.get()); + tmp = objectOutput->WriteStringZ(chromePath.get()); + if (NS_FAILED(tmp)) { + rv = tmp; + } + tmp = objectOutput->Close(); + if (NS_FAILED(tmp)) { + rv = tmp; + } + tmp = storageStream->NewInputStream(0, getter_AddRefs(inputStream)); + if (NS_FAILED(tmp)) { + rv = tmp; + } + } + + if (NS_SUCCEEDED(rv)) { + uint64_t len64; + rv = inputStream->Available(&len64); + if (NS_SUCCEEDED(rv)) { + if (len64 <= UINT32_MAX) + len = (uint32_t)len64; + else + rv = NS_ERROR_FILE_TOO_BIG; + } + } + + if (NS_SUCCEEDED(rv)) { + buf = MakeUnique<char[]>(len); + rv = inputStream->Read(buf.get(), len, &amtRead); + if (NS_SUCCEEDED(rv) && len == amtRead) + rv = startupCache->PutBuffer(kXULCacheInfoKey, buf.get(), len); + else { + rv = NS_ERROR_UNEXPECTED; + } + } + + // Failed again, just bail. + if (NS_FAILED(rv)) { + startupCache->InvalidateCache(); + mStartupCacheURITable.Clear(); + return NS_ERROR_FAILURE; + } + } + + return NS_OK; +} + +void +nsXULPrototypeCache::MarkInCCGeneration(uint32_t aGeneration) +{ + for (auto iter = mXBLDocTable.Iter(); !iter.Done(); iter.Next()) { + iter.Data()->MarkInCCGeneration(aGeneration); + } + for (auto iter = mPrototypeTable.Iter(); !iter.Done(); iter.Next()) { + iter.Data()->MarkInCCGeneration(aGeneration); + } +} + +void +nsXULPrototypeCache::MarkInGC(JSTracer* aTrc) +{ + for (auto iter = mScriptTable.Iter(); !iter.Done(); iter.Next()) { + JS::Heap<JSScript*>& script = iter.Data(); + JS::TraceEdge(aTrc, &script, "nsXULPrototypeCache script"); + } +} diff --git a/dom/xul/nsXULPrototypeCache.h b/dom/xul/nsXULPrototypeCache.h new file mode 100644 index 000000000..f191c57ed --- /dev/null +++ b/dom/xul/nsXULPrototypeCache.h @@ -0,0 +1,140 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsXULPrototypeCache_h__ +#define nsXULPrototypeCache_h__ + +#include "nsCOMPtr.h" +#include "nsIObserver.h" +#include "nsXBLDocumentInfo.h" +#include "nsJSThingHashtable.h" +#include "nsInterfaceHashtable.h" +#include "nsRefPtrHashtable.h" +#include "nsURIHashKey.h" +#include "nsXULPrototypeDocument.h" +#include "nsIInputStream.h" +#include "nsIStorageStream.h" + +#include "mozilla/scache/StartupCache.h" + +namespace mozilla { +class CSSStyleSheet; +} // namespace mozilla + +/** + * The XUL prototype cache can be used to store and retrieve shared data for + * XUL documents, style sheets, XBL, and scripts. + * + * The cache has two levels: + * 1. In-memory hashtables + * 2. The on-disk cache file. + */ +class nsXULPrototypeCache : public nsIObserver +{ +public: + // nsISupports + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIOBSERVER + + bool IsCached(nsIURI* aURI) { + return GetPrototype(aURI) != nullptr; + } + void AbortCaching(); + + + /** + * Whether the prototype cache is enabled. + */ + bool IsEnabled(); + + /** + * Flush the cache; remove all XUL prototype documents, style + * sheets, and scripts. + */ + void Flush(); + + + // The following methods are used to put and retrive various items into and + // from the cache. + + nsXULPrototypeDocument* GetPrototype(nsIURI* aURI); + nsresult PutPrototype(nsXULPrototypeDocument* aDocument); + + JSScript* GetScript(nsIURI* aURI); + nsresult PutScript(nsIURI* aURI, JS::Handle<JSScript*> aScriptObject); + + nsXBLDocumentInfo* GetXBLDocumentInfo(nsIURI* aURL) { + return mXBLDocTable.GetWeak(aURL); + } + nsresult PutXBLDocumentInfo(nsXBLDocumentInfo* aDocumentInfo); + + /** + * Get a style sheet by URI. If the style sheet is not in the cache, + * returns nullptr. + */ + mozilla::CSSStyleSheet* GetStyleSheet(nsIURI* aURI) { + return mStyleSheetTable.GetWeak(aURI); + } + + /** + * Store a style sheet in the cache. The key, style sheet's URI is obtained + * from the style sheet itself. + */ + nsresult PutStyleSheet(mozilla::CSSStyleSheet* aStyleSheet); + + /** + * Write the XUL prototype document to a cache file. The proto must be + * fully loaded. + */ + nsresult WritePrototype(nsXULPrototypeDocument* aPrototypeDocument); + + /** + * This interface allows partial reads and writes from the buffers in the + * startupCache. + */ + nsresult GetInputStream(nsIURI* aURI, nsIObjectInputStream** objectInput); + nsresult FinishInputStream(nsIURI* aURI); + nsresult GetOutputStream(nsIURI* aURI, nsIObjectOutputStream** objectOutput); + nsresult FinishOutputStream(nsIURI* aURI); + nsresult HasData(nsIURI* aURI, bool* exists); + + static nsXULPrototypeCache* GetInstance(); + static nsXULPrototypeCache* MaybeGetInstance() { return sInstance; } + + static void ReleaseGlobals() + { + NS_IF_RELEASE(sInstance); + } + + void MarkInCCGeneration(uint32_t aGeneration); + void MarkInGC(JSTracer* aTrc); + void FlushScripts(); +protected: + friend nsresult + NS_NewXULPrototypeCache(nsISupports* aOuter, REFNSIID aIID, void** aResult); + + nsXULPrototypeCache(); + virtual ~nsXULPrototypeCache(); + + static nsXULPrototypeCache* sInstance; + + void FlushSkinFiles(); + + nsRefPtrHashtable<nsURIHashKey,nsXULPrototypeDocument> mPrototypeTable; // owns the prototypes + nsRefPtrHashtable<nsURIHashKey,mozilla::CSSStyleSheet> mStyleSheetTable; + nsJSThingHashtable<nsURIHashKey, JSScript*> mScriptTable; + nsRefPtrHashtable<nsURIHashKey,nsXBLDocumentInfo> mXBLDocTable; + + // URIs already written to the startup cache, to prevent double-caching. + nsTHashtable<nsURIHashKey> mStartupCacheURITable; + + nsInterfaceHashtable<nsURIHashKey, nsIStorageStream> mOutputStreamTable; + nsInterfaceHashtable<nsURIHashKey, nsIObjectInputStream> mInputStreamTable; + + // Bootstrap caching service + nsresult BeginCaching(nsIURI* aDocumentURI); +}; + +#endif // nsXULPrototypeCache_h__ diff --git a/dom/xul/nsXULPrototypeDocument.cpp b/dom/xul/nsXULPrototypeDocument.cpp new file mode 100644 index 000000000..01d4794db --- /dev/null +++ b/dom/xul/nsXULPrototypeDocument.cpp @@ -0,0 +1,544 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +#include "nsXULPrototypeDocument.h" +#include "XULDocument.h" + +#include "nsAString.h" +#include "nsIObjectInputStream.h" +#include "nsIObjectOutputStream.h" +#include "nsIPrincipal.h" +#include "nsJSPrincipals.h" +#include "nsIScriptObjectPrincipal.h" +#include "nsIScriptSecurityManager.h" +#include "nsIServiceManager.h" +#include "nsIArray.h" +#include "nsIURI.h" +#include "jsapi.h" +#include "jsfriendapi.h" +#include "nsString.h" +#include "nsIConsoleService.h" +#include "nsIScriptError.h" +#include "nsDOMCID.h" +#include "nsNodeInfoManager.h" +#include "nsContentUtils.h" +#include "nsCCUncollectableMarker.h" +#include "xpcpublic.h" +#include "mozilla/dom/BindingUtils.h" + +using mozilla::dom::DestroyProtoAndIfaceCache; +using mozilla::dom::XULDocument; + +uint32_t nsXULPrototypeDocument::gRefCnt; + +//---------------------------------------------------------------------- +// +// ctors, dtors, n' stuff +// + +nsXULPrototypeDocument::nsXULPrototypeDocument() + : mRoot(nullptr), + mLoaded(false), + mCCGeneration(0), + mGCNumber(0) +{ + ++gRefCnt; +} + + +nsresult +nsXULPrototypeDocument::Init() +{ + mNodeInfoManager = new nsNodeInfoManager(); + return mNodeInfoManager->Init(nullptr); +} + +nsXULPrototypeDocument::~nsXULPrototypeDocument() +{ + if (mRoot) + mRoot->ReleaseSubtree(); +} + +NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULPrototypeDocument) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULPrototypeDocument) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrototypeWaiters) +NS_IMPL_CYCLE_COLLECTION_UNLINK_END +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULPrototypeDocument) + if (nsCCUncollectableMarker::InGeneration(cb, tmp->mCCGeneration)) { + return NS_SUCCESS_INTERRUPTED_TRAVERSE; + } + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfoManager) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrototypeWaiters) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULPrototypeDocument) + NS_INTERFACE_MAP_ENTRY(nsISerializable) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULPrototypeDocument) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULPrototypeDocument) + +NS_IMETHODIMP +NS_NewXULPrototypeDocument(nsXULPrototypeDocument** aResult) +{ + *aResult = nullptr; + RefPtr<nsXULPrototypeDocument> doc = + new nsXULPrototypeDocument(); + + nsresult rv = doc->Init(); + if (NS_FAILED(rv)) { + return rv; + } + + doc.forget(aResult); + return rv; +} + +//---------------------------------------------------------------------- +// +// nsISerializable methods +// + +NS_IMETHODIMP +nsXULPrototypeDocument::Read(nsIObjectInputStream* aStream) +{ + nsresult rv; + + nsCOMPtr<nsISupports> supports; + rv = aStream->ReadObject(true, getter_AddRefs(supports)); + mURI = do_QueryInterface(supports); + + uint32_t count, i; + nsCOMPtr<nsIURI> styleOverlayURI; + + nsresult tmp = aStream->Read32(&count); + if (NS_FAILED(tmp)) { + return tmp; + } + if (NS_FAILED(rv)) { + return rv; + } + + for (i = 0; i < count; ++i) { + tmp = aStream->ReadObject(true, getter_AddRefs(supports)); + if (NS_FAILED(tmp)) { + rv = tmp; + } + styleOverlayURI = do_QueryInterface(supports); + mStyleSheetReferences.AppendObject(styleOverlayURI); + } + + + // nsIPrincipal mNodeInfoManager->mPrincipal + nsCOMPtr<nsIPrincipal> principal; + tmp = aStream->ReadObject(true, getter_AddRefs(supports)); + principal = do_QueryInterface(supports); + if (NS_FAILED(tmp)) { + rv = tmp; + } + // Better safe than sorry.... + mNodeInfoManager->SetDocumentPrincipal(principal); + + mRoot = new nsXULPrototypeElement(); + + // mozilla::dom::NodeInfo table + nsTArray<RefPtr<mozilla::dom::NodeInfo>> nodeInfos; + + tmp = aStream->Read32(&count); + if (NS_FAILED(tmp)) { + rv = tmp; + } + nsAutoString namespaceURI, prefixStr, localName; + bool prefixIsNull; + nsCOMPtr<nsIAtom> prefix; + for (i = 0; i < count; ++i) { + tmp = aStream->ReadString(namespaceURI); + if (NS_FAILED(tmp)) { + rv = tmp; + } + tmp = aStream->ReadBoolean(&prefixIsNull); + if (NS_FAILED(tmp)) { + rv = tmp; + } + if (prefixIsNull) { + prefix = nullptr; + } else { + tmp = aStream->ReadString(prefixStr); + if (NS_FAILED(tmp)) { + rv = tmp; + } + prefix = NS_Atomize(prefixStr); + } + tmp = aStream->ReadString(localName); + if (NS_FAILED(tmp)) { + rv = tmp; + } + + RefPtr<mozilla::dom::NodeInfo> nodeInfo; + // Using UINT16_MAX here as we don't know which nodeinfos will be + // used for attributes and which for elements. And that doesn't really + // matter. + tmp = mNodeInfoManager->GetNodeInfo(localName, prefix, namespaceURI, + UINT16_MAX, + getter_AddRefs(nodeInfo)); + if (NS_FAILED(tmp)) { + rv = tmp; + } + nodeInfos.AppendElement(nodeInfo); + } + + // Document contents + uint32_t type; + while (NS_SUCCEEDED(rv)) { + tmp = aStream->Read32(&type); + if (NS_FAILED(tmp)) { + rv = tmp; + } + + if ((nsXULPrototypeNode::Type)type == nsXULPrototypeNode::eType_PI) { + RefPtr<nsXULPrototypePI> pi = new nsXULPrototypePI(); + + tmp = pi->Deserialize(aStream, this, mURI, &nodeInfos); + if (NS_FAILED(tmp)) { + rv = tmp; + } + tmp = AddProcessingInstruction(pi); + if (NS_FAILED(tmp)) { + rv = tmp; + } + } else if ((nsXULPrototypeNode::Type)type == nsXULPrototypeNode::eType_Element) { + tmp = mRoot->Deserialize(aStream, this, mURI, &nodeInfos); + if (NS_FAILED(tmp)) { + rv = tmp; + } + break; + } else { + NS_NOTREACHED("Unexpected prototype node type"); + rv = NS_ERROR_FAILURE; + break; + } + } + tmp = NotifyLoadDone(); + if (NS_FAILED(tmp)) { + rv = tmp; + } + + return rv; +} + +static nsresult +GetNodeInfos(nsXULPrototypeElement* aPrototype, + nsTArray<RefPtr<mozilla::dom::NodeInfo>>& aArray) +{ + if (aArray.IndexOf(aPrototype->mNodeInfo) == aArray.NoIndex) { + aArray.AppendElement(aPrototype->mNodeInfo); + } + + // Search attributes + uint32_t i; + for (i = 0; i < aPrototype->mNumAttributes; ++i) { + RefPtr<mozilla::dom::NodeInfo> ni; + nsAttrName* name = &aPrototype->mAttributes[i].mName; + if (name->IsAtom()) { + ni = aPrototype->mNodeInfo->NodeInfoManager()-> + GetNodeInfo(name->Atom(), nullptr, kNameSpaceID_None, + nsIDOMNode::ATTRIBUTE_NODE); + } + else { + ni = name->NodeInfo(); + } + + if (aArray.IndexOf(ni) == aArray.NoIndex) { + aArray.AppendElement(ni); + } + } + + // Search children + for (i = 0; i < aPrototype->mChildren.Length(); ++i) { + nsXULPrototypeNode* child = aPrototype->mChildren[i]; + if (child->mType == nsXULPrototypeNode::eType_Element) { + nsresult rv = + GetNodeInfos(static_cast<nsXULPrototypeElement*>(child), aArray); + NS_ENSURE_SUCCESS(rv, rv); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXULPrototypeDocument::Write(nsIObjectOutputStream* aStream) +{ + nsresult rv; + + rv = aStream->WriteCompoundObject(mURI, NS_GET_IID(nsIURI), true); + + uint32_t count; + + count = mStyleSheetReferences.Count(); + nsresult tmp = aStream->Write32(count); + if (NS_FAILED(tmp)) { + rv = tmp; + } + + uint32_t i; + for (i = 0; i < count; ++i) { + tmp = aStream->WriteCompoundObject(mStyleSheetReferences[i], + NS_GET_IID(nsIURI), true); + if (NS_FAILED(tmp)) { + rv = tmp; + } + } + + // nsIPrincipal mNodeInfoManager->mPrincipal + tmp = aStream->WriteObject(mNodeInfoManager->DocumentPrincipal(), + true); + if (NS_FAILED(tmp)) { + rv = tmp; + } + +#ifdef DEBUG + // XXX Worrisome if we're caching things without system principal. + if (!nsContentUtils::IsSystemPrincipal(mNodeInfoManager->DocumentPrincipal())) { + NS_WARNING("Serializing document without system principal"); + } +#endif + + // mozilla::dom::NodeInfo table + nsTArray<RefPtr<mozilla::dom::NodeInfo>> nodeInfos; + if (mRoot) { + tmp = GetNodeInfos(mRoot, nodeInfos); + if (NS_FAILED(tmp)) { + rv = tmp; + } + } + + uint32_t nodeInfoCount = nodeInfos.Length(); + tmp = aStream->Write32(nodeInfoCount); + if (NS_FAILED(tmp)) { + rv = tmp; + } + for (i = 0; i < nodeInfoCount; ++i) { + mozilla::dom::NodeInfo *nodeInfo = nodeInfos[i]; + NS_ENSURE_TRUE(nodeInfo, NS_ERROR_FAILURE); + + nsAutoString namespaceURI; + nodeInfo->GetNamespaceURI(namespaceURI); + tmp = aStream->WriteWStringZ(namespaceURI.get()); + if (NS_FAILED(tmp)) { + rv = tmp; + } + + nsAutoString prefix; + nodeInfo->GetPrefix(prefix); + bool nullPrefix = DOMStringIsNull(prefix); + tmp = aStream->WriteBoolean(nullPrefix); + if (NS_FAILED(tmp)) { + rv = tmp; + } + if (!nullPrefix) { + tmp = aStream->WriteWStringZ(prefix.get()); + if (NS_FAILED(tmp)) { + rv = tmp; + } + } + + nsAutoString localName; + nodeInfo->GetName(localName); + tmp = aStream->WriteWStringZ(localName.get()); + if (NS_FAILED(tmp)) { + rv = tmp; + } + } + + // Now serialize the document contents + count = mProcessingInstructions.Length(); + for (i = 0; i < count; ++i) { + nsXULPrototypePI* pi = mProcessingInstructions[i]; + tmp = pi->Serialize(aStream, this, &nodeInfos); + if (NS_FAILED(tmp)) { + rv = tmp; + } + } + + if (mRoot) { + tmp = mRoot->Serialize(aStream, this, &nodeInfos); + if (NS_FAILED(tmp)) { + rv = tmp; + } + } + + return rv; +} + + +//---------------------------------------------------------------------- +// + +nsresult +nsXULPrototypeDocument::InitPrincipal(nsIURI* aURI, nsIPrincipal* aPrincipal) +{ + NS_ENSURE_ARG_POINTER(aURI); + + mURI = aURI; + mNodeInfoManager->SetDocumentPrincipal(aPrincipal); + return NS_OK; +} + + +nsIURI* +nsXULPrototypeDocument::GetURI() +{ + NS_ASSERTION(mURI, "null URI"); + return mURI; +} + + +nsXULPrototypeElement* +nsXULPrototypeDocument::GetRootElement() +{ + return mRoot; +} + + +void +nsXULPrototypeDocument::SetRootElement(nsXULPrototypeElement* aElement) +{ + mRoot = aElement; +} + +nsresult +nsXULPrototypeDocument::AddProcessingInstruction(nsXULPrototypePI* aPI) +{ + NS_PRECONDITION(aPI, "null ptr"); + if (!mProcessingInstructions.AppendElement(aPI)) { + return NS_ERROR_OUT_OF_MEMORY; + } + return NS_OK; +} + +const nsTArray<RefPtr<nsXULPrototypePI> >& +nsXULPrototypeDocument::GetProcessingInstructions() const +{ + return mProcessingInstructions; +} + +void +nsXULPrototypeDocument::AddStyleSheetReference(nsIURI* aURI) +{ + NS_PRECONDITION(aURI, "null ptr"); + if (!mStyleSheetReferences.AppendObject(aURI)) { + NS_WARNING("mStyleSheetReferences->AppendElement() failed." + "Stylesheet overlay dropped."); + } +} + +const nsCOMArray<nsIURI>& +nsXULPrototypeDocument::GetStyleSheetReferences() const +{ + return mStyleSheetReferences; +} + +NS_IMETHODIMP +nsXULPrototypeDocument::GetHeaderData(nsIAtom* aField, nsAString& aData) const +{ + // XXX Not implemented + aData.Truncate(); + return NS_OK; +} + + +NS_IMETHODIMP +nsXULPrototypeDocument::SetHeaderData(nsIAtom* aField, const nsAString& aData) +{ + // XXX Not implemented + return NS_OK; +} + + + +nsIPrincipal* +nsXULPrototypeDocument::DocumentPrincipal() +{ + NS_PRECONDITION(mNodeInfoManager, "missing nodeInfoManager"); + return mNodeInfoManager->DocumentPrincipal(); +} + +void +nsXULPrototypeDocument::SetDocumentPrincipal(nsIPrincipal* aPrincipal) +{ + mNodeInfoManager->SetDocumentPrincipal(aPrincipal); +} + +void +nsXULPrototypeDocument::MarkInCCGeneration(uint32_t aCCGeneration) +{ + mCCGeneration = aCCGeneration; +} + +nsNodeInfoManager* +nsXULPrototypeDocument::GetNodeInfoManager() +{ + return mNodeInfoManager; +} + + +nsresult +nsXULPrototypeDocument::AwaitLoadDone(XULDocument* aDocument, bool* aResult) +{ + nsresult rv = NS_OK; + + *aResult = mLoaded; + + if (!mLoaded) { + rv = mPrototypeWaiters.AppendElement(aDocument) + ? NS_OK : NS_ERROR_OUT_OF_MEMORY; // addrefs + } + + return rv; +} + + +nsresult +nsXULPrototypeDocument::NotifyLoadDone() +{ + // Call back to each XUL document that raced to start the same + // prototype document load, lost the race, but hit the XUL + // prototype cache because the winner filled the cache with + // the not-yet-loaded prototype object. + + nsresult rv = NS_OK; + + mLoaded = true; + + for (uint32_t i = mPrototypeWaiters.Length(); i > 0; ) { + --i; + // true means that OnPrototypeLoadDone will also + // call ResumeWalk(). + rv = mPrototypeWaiters[i]->OnPrototypeLoadDone(true); + if (NS_FAILED(rv)) break; + } + mPrototypeWaiters.Clear(); + + return rv; +} + +void +nsXULPrototypeDocument::TraceProtos(JSTracer* aTrc, uint32_t aGCNumber) +{ + // Only trace the protos once per GC. + if (mGCNumber == aGCNumber) { + return; + } + + mGCNumber = aGCNumber; + if (mRoot) { + mRoot->TraceAllScripts(aTrc); + } +} diff --git a/dom/xul/nsXULPrototypeDocument.h b/dom/xul/nsXULPrototypeDocument.h new file mode 100644 index 000000000..81772eab3 --- /dev/null +++ b/dom/xul/nsXULPrototypeDocument.h @@ -0,0 +1,143 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsXULPrototypeDocument_h__ +#define nsXULPrototypeDocument_h__ + +#include "js/TracingAPI.h" +#include "mozilla/Attributes.h" +#include "nsCOMArray.h" +#include "nsCOMPtr.h" +#include "nsTArray.h" +#include "nsISerializable.h" +#include "nsCycleCollectionParticipant.h" + +class nsIAtom; +class nsIPrincipal; +class nsIURI; +class nsNodeInfoManager; +class nsXULPrototypeElement; +class nsXULPrototypePI; + +namespace mozilla { +namespace dom { +class XULDocument; +} // namespace dom +} // namespace mozilla + +/** + * A "prototype" document that stores shared document information + * for the XUL cache. + * Among other things, stores the tree of nsXULPrototype* + * objects, from which the real DOM tree is built later in + * XULDocument::ResumeWalk. + */ +class nsXULPrototypeDocument final : public nsISerializable +{ +public: + static nsresult + Create(nsIURI* aURI, nsXULPrototypeDocument** aResult); + + // nsISupports interface + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + + // nsISerializable interface + NS_DECL_NSISERIALIZABLE + + nsresult InitPrincipal(nsIURI* aURI, nsIPrincipal* aPrincipal); + nsIURI* GetURI(); + + /** + * Get/set the root nsXULPrototypeElement of the document. + */ + nsXULPrototypeElement* GetRootElement(); + void SetRootElement(nsXULPrototypeElement* aElement); + + /** + * Add a processing instruction to the prolog. Note that only + * PI nodes are currently stored in a XUL prototype document's + * prolog and that they're handled separately from the rest of + * prototype node tree. + * + * @param aPI an already adrefed PI proto to add. This method takes + * ownership of the passed PI. + */ + nsresult AddProcessingInstruction(nsXULPrototypePI* aPI); + /** + * @note GetProcessingInstructions retains the ownership (the PI + * protos only get deleted when the proto document is deleted) + */ + const nsTArray<RefPtr<nsXULPrototypePI> >& GetProcessingInstructions() const; + + /** + * Access the array of style overlays for this document. + * + * Style overlays are stylesheets that need to be applied to the + * document, but are not referenced from within the document. They + * are currently obtained from the chrome registry via + * nsIXULOverlayProvider::getStyleOverlays.) + */ + void AddStyleSheetReference(nsIURI* aStyleSheet); + const nsCOMArray<nsIURI>& GetStyleSheetReferences() const; + + /** + * Access HTTP header data. + * @note Not implemented. + */ + NS_IMETHOD GetHeaderData(nsIAtom* aField, nsAString& aData) const; + NS_IMETHOD SetHeaderData(nsIAtom* aField, const nsAString& aData); + + nsIPrincipal *DocumentPrincipal(); + void SetDocumentPrincipal(nsIPrincipal *aPrincipal); + + /** + * If current prototype document has not yet finished loading, + * appends aDocument to the list of documents to notify (via + * XULDocument::OnPrototypeLoadDone()) and sets aLoaded to false. + * Otherwise sets aLoaded to true. + */ + nsresult AwaitLoadDone(mozilla::dom::XULDocument* aDocument, bool* aResult); + + /** + * Notifies each document registered via AwaitLoadDone on this + * prototype document that the prototype has finished loading. + * The notification is performed by calling + * nsIXULDocument::OnPrototypeLoadDone on the registered documents. + */ + nsresult NotifyLoadDone(); + + nsNodeInfoManager *GetNodeInfoManager(); + + void MarkInCCGeneration(uint32_t aCCGeneration); + + NS_DECL_CYCLE_COLLECTION_CLASS(nsXULPrototypeDocument) + + void TraceProtos(JSTracer* aTrc, uint32_t aGCNumber); + +protected: + nsCOMPtr<nsIURI> mURI; + RefPtr<nsXULPrototypeElement> mRoot; + nsTArray<RefPtr<nsXULPrototypePI> > mProcessingInstructions; + nsCOMArray<nsIURI> mStyleSheetReferences; + + bool mLoaded; + nsTArray< RefPtr<mozilla::dom::XULDocument> > mPrototypeWaiters; + + RefPtr<nsNodeInfoManager> mNodeInfoManager; + + uint32_t mCCGeneration; + uint32_t mGCNumber; + + nsXULPrototypeDocument(); + virtual ~nsXULPrototypeDocument(); + nsresult Init(); + + friend NS_IMETHODIMP + NS_NewXULPrototypeDocument(nsXULPrototypeDocument** aResult); + + static uint32_t gRefCnt; +}; + +#endif // nsXULPrototypeDocument_h__ diff --git a/dom/xul/templates/crashtests/257752-1-recursion.rdf b/dom/xul/templates/crashtests/257752-1-recursion.rdf new file mode 100644 index 000000000..a6eeb104b --- /dev/null +++ b/dom/xul/templates/crashtests/257752-1-recursion.rdf @@ -0,0 +1,13 @@ +<?xml version="1.0"?> +<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:m="urn:foo#"> + <rdf:Seq about="urn:x-rec:1"> + <rdf:li rdf:resource="urn:x-rec:2"/> + </rdf:Seq> + <rdf:Seq about="urn:x-rec:2"> + <rdf:li rdf:resource="urn:x-rec:3"/> + </rdf:Seq> + <rdf:Seq about="urn:x-rec:3"> + <rdf:li rdf:resource="urn:x-rec:1"/> + </rdf:Seq> +</rdf:RDF> diff --git a/dom/xul/templates/crashtests/257752-1-recursion.xul b/dom/xul/templates/crashtests/257752-1-recursion.xul new file mode 100644 index 000000000..fad5abfb6 --- /dev/null +++ b/dom/xul/templates/crashtests/257752-1-recursion.xul @@ -0,0 +1,28 @@ +<?xml version="1.0"?> + +<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> + +<window id="child-iterate-recurse" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + +<vbox flex="1" style="overflow: auto;"> + +<vbox datasources="257752-1-recursion.rdf" ref="urn:x-rec:1"> + <template> + <rule> + <conditions> + <content uri="?uri"/> + <member container="?uri" child="?child"/> + </conditions> + <action> + <vbox uri="?child" style="border: 1px solid grey; margin: 1em;"> + <label value="hi"/> + </vbox> + </action> + </rule> + </template> +</vbox> + +</vbox> + +</window> diff --git a/dom/xul/templates/crashtests/329884-1.xul b/dom/xul/templates/crashtests/329884-1.xul new file mode 100644 index 000000000..8cc486e27 --- /dev/null +++ b/dom/xul/templates/crashtests/329884-1.xul @@ -0,0 +1,20 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + +<script> + +function rM(q1) { q1.parentNode.removeChild(q1); } + +function init2() +{ + rM(document.getElementById("t")); +} + +window.addEventListener("load", init2, false); + +</script> + +<foo id="t" datasources="1.rdf" /> + +</window> diff --git a/dom/xul/templates/crashtests/330012-1.rdf b/dom/xul/templates/crashtests/330012-1.rdf new file mode 100644 index 000000000..4bda7316a --- /dev/null +++ b/dom/xul/templates/crashtests/330012-1.rdf @@ -0,0 +1,13 @@ +<?xml version="1.0"?> +<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:s="urn:squarefree:a1:"> + <rdf:Description about="urn:root"> + <s:grapes> + <rdf:Bag> + <rdf:li> + <rdf:Description/> + </rdf:li> + </rdf:Bag> + </s:grapes> + </rdf:Description> +</rdf:RDF> diff --git a/dom/xul/templates/crashtests/330012-1.xul b/dom/xul/templates/crashtests/330012-1.xul new file mode 100644 index 000000000..ea797c2cf --- /dev/null +++ b/dom/xul/templates/crashtests/330012-1.xul @@ -0,0 +1,22 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <foo id="foo" datasources="330012-1.rdf" ref="urn:root"> + <template> + <rule> + <conditions> + <content uri="?root"/> + <triple subject="?root" + predicate="urn:squarefree:a1:grapes" + object="?lalala"/> + <member container="?grapes" child="?grape"/> + </conditions> + <action> + <bar uri="?grape"/> + </action> + </rule> + </template> + </foo> + +</window> diff --git a/dom/xul/templates/crashtests/404346-1.xul b/dom/xul/templates/crashtests/404346-1.xul new file mode 100644 index 000000000..e947960da --- /dev/null +++ b/dom/xul/templates/crashtests/404346-1.xul @@ -0,0 +1,7 @@ +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> +<box id="b"> +<box id="a" template="b"/> +<triple/> +</box> +<box datasources="" observes="a" ref="bbb"/> +</window>
\ No newline at end of file diff --git a/dom/xul/templates/crashtests/415019-1.xul b/dom/xul/templates/crashtests/415019-1.xul new file mode 100644 index 000000000..9fb9560f6 --- /dev/null +++ b/dom/xul/templates/crashtests/415019-1.xul @@ -0,0 +1,14 @@ +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <hbox datasources="nosuch.rdf" ref="urn:root"> + <template> + <rule> + <conditions> + <triple subject="?a"/> + </conditions> + <action> + <vbox uri="?b"/> + </action> + </rule> + </template> + </hbox> +</window> diff --git a/dom/xul/templates/crashtests/417840-1.xul b/dom/xul/templates/crashtests/417840-1.xul new file mode 100644 index 000000000..e41af81e9 --- /dev/null +++ b/dom/xul/templates/crashtests/417840-1.xul @@ -0,0 +1 @@ +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" onload="document.getElementById('foo').removeAttribute('ref');"><foo id="foo" datasources="nosuch.rdf" ref="urn:root"><template/></foo></window> diff --git a/dom/xul/templates/crashtests/424418-1.xul b/dom/xul/templates/crashtests/424418-1.xul new file mode 100644 index 000000000..d8565643a --- /dev/null +++ b/dom/xul/templates/crashtests/424418-1.xul @@ -0,0 +1 @@ +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"><hbox datasources="u"/></window> diff --git a/dom/xul/templates/crashtests/crashtests.list b/dom/xul/templates/crashtests/crashtests.list new file mode 100644 index 000000000..ed99a3ae8 --- /dev/null +++ b/dom/xul/templates/crashtests/crashtests.list @@ -0,0 +1,7 @@ +load 257752-1-recursion.xul +load 329884-1.xul +skip-if(winWidget) load 330012-1.xul # bug 742455 +load 404346-1.xul +load 415019-1.xul +load 417840-1.xul +load 424418-1.xul diff --git a/dom/xul/templates/moz.build b/dom/xul/templates/moz.build new file mode 100644 index 000000000..3beb0b7e5 --- /dev/null +++ b/dom/xul/templates/moz.build @@ -0,0 +1,58 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +MOCHITEST_CHROME_MANIFESTS += ['tests/chrome/chrome.ini'] + +XPIDL_SOURCES += [ + 'nsIXULBuilderListener.idl', + 'nsIXULSortService.idl', + 'nsIXULTemplateBuilder.idl', + 'nsIXULTemplateQueryProcessor.idl', + 'nsIXULTemplateResult.idl', + 'nsIXULTemplateRuleFilter.idl', +] + +XPIDL_MODULE = 'xultmpl' + +UNIFIED_SOURCES += [ + 'nsContentSupportMap.cpp', + 'nsContentTestNode.cpp', + 'nsInstantiationNode.cpp', + 'nsRDFBinding.cpp', + 'nsRDFConInstanceTestNode.cpp', + 'nsRDFConMemberTestNode.cpp', + 'nsRDFPropertyTestNode.cpp', + 'nsRDFQuery.cpp', + 'nsResourceSet.cpp', + 'nsRuleNetwork.cpp', + 'nsTemplateMatch.cpp', + 'nsTemplateRule.cpp', + 'nsTreeRows.cpp', + 'nsXMLBinding.cpp', + 'nsXULContentBuilder.cpp', + 'nsXULContentUtils.cpp', + 'nsXULSortService.cpp', + 'nsXULTemplateBuilder.cpp', + 'nsXULTemplateQueryProcessorRDF.cpp', + 'nsXULTemplateQueryProcessorStorage.cpp', + 'nsXULTemplateQueryProcessorXML.cpp', + 'nsXULTemplateResultRDF.cpp', + 'nsXULTemplateResultSetRDF.cpp', + 'nsXULTemplateResultStorage.cpp', + 'nsXULTemplateResultXML.cpp', + 'nsXULTreeBuilder.cpp', +] + +LOCAL_INCLUDES += [ + '/dom/base', + '/dom/xul', + '/layout/xul/tree/', +] + +FINAL_LIBRARY = 'xul' + +if CONFIG['GNU_CXX']: + CXXFLAGS += ['-Wno-error=shadow'] diff --git a/dom/xul/templates/nsContentSupportMap.cpp b/dom/xul/templates/nsContentSupportMap.cpp new file mode 100644 index 000000000..ec10fde74 --- /dev/null +++ b/dom/xul/templates/nsContentSupportMap.cpp @@ -0,0 +1,18 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsContentSupportMap.h" +#include "nsXULElement.h" + +void +nsContentSupportMap::Remove(nsIContent* aElement) +{ + nsIContent* child = aElement; + do { + mMap.Remove(child); + child = child->GetNextNode(aElement); + } while(child); +} + diff --git a/dom/xul/templates/nsContentSupportMap.h b/dom/xul/templates/nsContentSupportMap.h new file mode 100644 index 000000000..aef7de83e --- /dev/null +++ b/dom/xul/templates/nsContentSupportMap.h @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsContentSupportMap_h__ +#define nsContentSupportMap_h__ + +#include "PLDHashTable.h" +#include "nsTemplateMatch.h" + +/** + * The nsContentSupportMap maintains a mapping from a "resource element" + * in the content tree to the nsTemplateMatch that was used to instantiate it. This + * is necessary to allow the XUL content to be built lazily. Specifically, + * when building "resumes" on a partially-built content element, the builder + * will walk upwards in the content tree to find the first element with an + * 'id' attribute. This element is assumed to be the "resource element", + * and allows the content builder to access the nsTemplateMatch (variable assignments + * and rule information). + */ +class nsContentSupportMap { +public: + nsContentSupportMap() : mMap(PLDHashTable::StubOps(), sizeof(Entry)) { } + ~nsContentSupportMap() { } + + nsresult Put(nsIContent* aElement, nsTemplateMatch* aMatch) { + PLDHashEntryHdr* hdr = mMap.Add(aElement, mozilla::fallible); + if (!hdr) + return NS_ERROR_OUT_OF_MEMORY; + + Entry* entry = static_cast<Entry*>(hdr); + NS_ASSERTION(entry->mMatch == nullptr, "over-writing entry"); + entry->mContent = aElement; + entry->mMatch = aMatch; + return NS_OK; + } + + bool Get(nsIContent* aElement, nsTemplateMatch** aMatch) { + PLDHashEntryHdr* hdr = mMap.Search(aElement); + if (!hdr) + return false; + + Entry* entry = static_cast<Entry*>(hdr); + *aMatch = entry->mMatch; + return true; + } + + void Remove(nsIContent* aElement); + + void Clear() { mMap.Clear(); } + +protected: + PLDHashTable mMap; + + struct Entry : public PLDHashEntryHdr { + nsIContent* mContent; + nsTemplateMatch* mMatch; + }; +}; + +#endif diff --git a/dom/xul/templates/nsContentTestNode.cpp b/dom/xul/templates/nsContentTestNode.cpp new file mode 100644 index 000000000..53253a304 --- /dev/null +++ b/dom/xul/templates/nsContentTestNode.cpp @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsContentTestNode.h" +#include "nsIRDFResource.h" +#include "nsIAtom.h" +#include "nsIDOMElement.h" +#include "nsXULContentUtils.h" +#include "nsIXULTemplateResult.h" +#include "nsIXULTemplateBuilder.h" +#include "nsXULTemplateQueryProcessorRDF.h" + +#include "mozilla/Logging.h" + +using mozilla::LogLevel; + +extern mozilla::LazyLogModule gXULTemplateLog; + +nsContentTestNode::nsContentTestNode(nsXULTemplateQueryProcessorRDF* aProcessor, + nsIAtom* aRefVariable) + : TestNode(nullptr), + mProcessor(aProcessor), + mDocument(nullptr), + mRefVariable(aRefVariable), + mTag(nullptr) +{ + if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) { + nsAutoString tag(NS_LITERAL_STRING("(none)")); + if (mTag) + mTag->ToString(tag); + + nsAutoString refvar(NS_LITERAL_STRING("(none)")); + if (aRefVariable) + aRefVariable->ToString(refvar); + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + ("nsContentTestNode[%p]: ref-var=%s tag=%s", + this, NS_ConvertUTF16toUTF8(refvar).get(), + NS_ConvertUTF16toUTF8(tag).get())); + } +} + +nsresult +nsContentTestNode::FilterInstantiations(InstantiationSet& aInstantiations, + bool* aCantHandleYet) const + +{ + if (aCantHandleYet) + *aCantHandleYet = false; + return NS_OK; +} + +nsresult +nsContentTestNode::Constrain(InstantiationSet& aInstantiations) +{ + // contrain the matches to those that have matched in the template builder + + nsIXULTemplateBuilder* builder = mProcessor->GetBuilder(); + if (!builder) { + aInstantiations.Clear(); + return NS_OK; + } + + nsresult rv; + + InstantiationSet::Iterator last = aInstantiations.Last(); + for (InstantiationSet::Iterator inst = aInstantiations.First(); inst != last; ++inst) { + + nsCOMPtr<nsIRDFNode> refValue; + bool hasRefBinding = inst->mAssignments.GetAssignmentFor(mRefVariable, + getter_AddRefs(refValue)); + if (hasRefBinding) { + nsCOMPtr<nsIRDFResource> refResource = do_QueryInterface(refValue); + if (refResource) { + bool generated; + rv = builder->HasGeneratedContent(refResource, mTag, &generated); + if (NS_FAILED(rv)) return rv; + + if (generated) + continue; + } + } + + aInstantiations.Erase(inst--); + } + + return NS_OK; +} diff --git a/dom/xul/templates/nsContentTestNode.h b/dom/xul/templates/nsContentTestNode.h new file mode 100644 index 000000000..ebea5bcf6 --- /dev/null +++ b/dom/xul/templates/nsContentTestNode.h @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsContentTestNode_h__ +#define nsContentTestNode_h__ + +#include "mozilla/Attributes.h" +#include "nscore.h" +#include "nsRuleNetwork.h" +#include "nsIAtom.h" +#include "nsIDOMDocument.h" + +class nsXULTemplateQueryProcessorRDF; + +/** + * The nsContentTestNode is always the top node in a query's rule network. It + * exists so that Constrain can filter out resources that aren't part of a + * result. + */ +class nsContentTestNode : public TestNode +{ +public: + nsContentTestNode(nsXULTemplateQueryProcessorRDF* aProcessor, + nsIAtom* aContentVariable); + + virtual nsresult FilterInstantiations(InstantiationSet& aInstantiations, + bool* aCantHandleYet) const override; + + nsresult + Constrain(InstantiationSet& aInstantiations) override; + + void SetTag(nsIAtom* aTag, nsIDOMDocument* aDocument) + { + mTag = aTag; + mDocument = aDocument; + } + +protected: + nsXULTemplateQueryProcessorRDF *mProcessor; + nsIDOMDocument* mDocument; + nsCOMPtr<nsIAtom> mRefVariable; + nsCOMPtr<nsIAtom> mTag; +}; + +#endif // nsContentTestNode_h__ + diff --git a/dom/xul/templates/nsIXULBuilderListener.idl b/dom/xul/templates/nsIXULBuilderListener.idl new file mode 100644 index 000000000..33ae2b3e8 --- /dev/null +++ b/dom/xul/templates/nsIXULBuilderListener.idl @@ -0,0 +1,28 @@ +/* -*- Mode: idl; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +#include "nsISupports.idl" + +interface nsIXULTemplateBuilder; + +// An nsIXULBuilderListener object is a listener that will be notified +// when a template builder rebuilds its content. +[scriptable, uuid(ac46be8f-c863-4c23-84a2-d0fcc8dfa9f4)] +interface nsIXULBuilderListener: nsISupports { + + /** + * Called before a template builder rebuilds its content. + * @param aBuilder the template builder that rebuilds the content. + */ + void willRebuild(in nsIXULTemplateBuilder aBuilder); + + /** + * Called after a template builder has rebuilt its content. + * @param aBuilder the template builder that has rebuilt the content. + */ + void didRebuild(in nsIXULTemplateBuilder aBuilder); + +}; diff --git a/dom/xul/templates/nsIXULSortService.idl b/dom/xul/templates/nsIXULSortService.idl new file mode 100644 index 000000000..c8eb6a8bb --- /dev/null +++ b/dom/xul/templates/nsIXULSortService.idl @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +interface nsIDOMNode; + +/** + * A service used to sort the contents of a XUL widget. + */ +[scriptable, uuid(F29270C8-3BE5-4046-9B57-945A84DFF132)] +interface nsIXULSortService : nsISupports +{ + const unsigned long SORT_COMPARECASE = 0x0001; + const unsigned long SORT_INTEGER = 0x0100; + + /** + * Sort the contents of the widget containing <code>aNode</code> + * using <code>aSortKey</code> as the comparison key, and + * <code>aSortDirection</code> as the direction. + * + * @param aNode A node in the XUL widget whose children are to be sorted. + * @param aSortKey The value to be used as the comparison key. + * @param aSortHints One or more hints as to how to sort: + * + * ascending: to sort the contents in ascending order + * descending: to sort the contents in descending order + * comparecase: perform case sensitive comparisons + * integer: treat values as integers, non-integers are compared as strings + * twostate: don't allow the natural (unordered state) + */ + void sort(in nsIDOMNode aNode, + in AString aSortKey, + in AString aSortHints); +}; + +%{C++ +nsresult +NS_NewXULSortService(nsIXULSortService **result); +%} diff --git a/dom/xul/templates/nsIXULTemplateBuilder.idl b/dom/xul/templates/nsIXULTemplateBuilder.idl new file mode 100644 index 000000000..755b57e57 --- /dev/null +++ b/dom/xul/templates/nsIXULTemplateBuilder.idl @@ -0,0 +1,409 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "domstubs.idl" + +interface nsIAtom; +interface nsIContent; +interface nsIXULBuilderListener; +interface nsIXULTemplateResult; +interface nsIXULTemplateRuleFilter; +interface nsIXULTemplateQueryProcessor; +interface nsIRDFResource; +interface nsIRDFCompositeDataSource; +interface nsIDOMDataTransfer; + +/** + * A template builder, given an input source of data, a template, and a + * reference point, generates a list of results from the input, and copies + * part of the template for each result. Templates may generate content + * recursively, using the same template, but with the previous iteration's + * results as the reference point. As an example, for an XML datasource the + * initial reference point would be a specific node in the DOM tree and a + * template might generate a list of all child nodes. For the next iteration, + * those children would be used to generate output for their child nodes and + * so forth. + * + * A template builder is attached to a single DOM node; this node is called + * the root node and is expected to contain a XUL template element as a direct + * child. Different template builders may be specialized in the manner in + * which they generate and display the resulting content from the template. + * + * The structure of a template is as follows: + * + * <rootnode datasources="" ref=""> + * <template> + * <queryset> + * <query> + * </query> + * <rule> + * <conditions>...</conditions> + * <bindings>...</bindings> + * <action>...</action> + * </rule> + * </queryset> + * </template> + * </rootnode> + * + * The datasources attribute on the root node is used to identify the source + * of data to be used. The ref attribute is used to specify the reference + * point for the query. Currently, the datasource will either be an + * nsIRDFDataSource or a DOM node. In the future, other datasource types may + * be used. + * + * The <queryset> element contains a single query and one or more <rule> + * elements. There may be more than one <queryset> if multiple queries are + * desired, and this element is optional if only one query is needed -- in + * that case the <query> and <rule>s are allowed to be children of the + * <template> node + * + * The contents of the query are processed by a separate component called a + * query processor. This query processor is expected to use this query to + * generate results when asked by the template builder. The template builder + * then generates output for each result based on the <rule> elements. + * + * This allows the query processor to be specific to a particular kind of + * input data or query syntax, while the template builder remains independent + * of the kind of data being used. Due to this, the query processor will be + * supplied with the datasource and query which the template builder handles + * in an opaque way, while the query processor handles these more + * specifically. + * + * Results implement the nsIXULTemplateResult interface and may be identified + * by an id which must be unique within a given set of query results. + * + * Each query may be accompanied by one or more <rule> elements. These rules + * are evaluated by the template builder for each result produced by the + * query. A rule consists of conditions that cause a rule to be either + * accepted or rejected. The condition syntax allows for common conditional + * handling; additional filtering may be applied by adding a custom filter + * to a rule with the builder's addRuleFilter method. + * + * If a result passes a rule's conditions, this is considered a match, and the + * content within the rule's <action> body is inserted as a sibling of the + * <template>, assuming the template builder creates real DOM content. Only + * one rule will match a result. For a tree builder, for example, the content + * within the action body is used to create the tree rows instead. A matching + * result must have its ruleMatched method called. When a result no longer + * matches, the result's hasBeenRemoved method must be called. + * + * Optionally, the rule may have a <bindings> section which may be used to + * define additional variables to be used within an action body. Each of these + * declared bindings must be supplied to the query processor via its + * addBinding method. The bindings are evaluated after a rule has matched. + * + * Templates may generate content recursively, using the previous iteration's + * results as reference point to invoke the same queries. Since the reference + * point is different, different output will typically be generated. + * + * The reference point nsIXULTemplateResult object for the first iteration is + * determined by calling the query processor's translateRef method using the + * value of the root node's ref attribute. This object may be retrieved later + * via the builder's rootResult property. + * + * For convenience, each reference point as well as all results implement the + * nsIXULTemplateResult interface, allowing the result objects from each + * iteration to be used directly as the reference points for the next + * iteration. + * + * When using multiple queries, each may generate results with the same id. + * More than one of these results may match one of the rules in their + * respective queries, however only the result for the earliest matching query + * in the template becomes the active match and generates output. The + * addResult, removeResult, replaceResult and resultBindingChanged methods may + * be called by the query processor to indicate that the set of valid results + * has changed, such that a different query may match. If a different match + * would become active, the content for the existing match is removed and the + * content for the new match is generated. A query processor is not required + * to provide any support for updating results after they have been generated. + * + * See http://wiki.mozilla.org/XUL:Templates_Plan for details about templates. + */ +[scriptable, uuid(A583B676-5B02-4F9C-A0C9-CB850CB99818)] +interface nsIXULTemplateBuilder : nsISupports +{ + /** + * The root node in the DOM to which this builder is attached. + */ + readonly attribute nsIDOMElement root; + + /** + * The opaque datasource object that is used for the template. This object + * is created by the getDataSource method of the query processor. May be + * null if the datasource has not been loaded yet. Set this attribute to + * use a different datasource and rebuild the template. + * + * For an RDF datasource, this will be the same as the database. For XML + * this will be the nsIDOMNode for the datasource document or node for + * an inline reference (such as #name). Other query processors may use + * other types for the datasource. + */ + attribute nsISupports datasource; + + /** + * The composite datasource that the template builder observes + * and uses to create content. This is used only for RDF queries and is + * maintained for backwards compatibility. It will be the same object as + * the datasource property. For non-RDF queries, it will always be null. + */ + readonly attribute nsIRDFCompositeDataSource database; + + /** + * The virtual result representing the starting reference point, + * determined by calling the query processor's translateRef method + * with the root node's ref attribute as an argument. + */ + readonly attribute nsIXULTemplateResult rootResult; + + /** + * The query processor used to generate results. + */ + [noscript] readonly attribute nsIXULTemplateQueryProcessor queryProcessor; + + /** + * Force the template builder to rebuild its content. All existing content + * will be removed first. The query processor's done() method will be + * invoked during cleanup, followed by its initializeForBuilding method + * when the content is to be regenerated. + * + */ + void rebuild(); + + /** + * Reload any of our RDF datasources that support nsIRDFRemoteDatasource. + * + * @note This is a temporary hack so that remote-XUL authors can + * reload remote datasources. When RDF becomes remote-scriptable, + * this will no longer be necessary. + */ + void refresh(); + + /** + * Inform the template builder that a new result is available. The builder + * will add this result to the set of results. The query node that the + * new result applies to must be specified using the aQueryNode parameter. + * + * The builder will apply the rules associated with the query to the new + * result, unless a result with the same id from an earlier query + * supersedes it, and the result's RuleMatched method will be called if it + * matches. + * + * @param aResult the result to add + * @param aQueryNode the query that the result applies to + * + * @throws NS_ERROR_NULL_POINTER if aResult or aQueryNode are null + */ + void addResult(in nsIXULTemplateResult aResult, in nsIDOMNode aQueryNode); + + /** + * Inform the template builder that a result no longer applies. The builder + * will call the remove content generated for the result, if any. If a different + * query would then match instead, it will become the active match. This + * method will have no effect if the result isn't known to the builder. + * + * @param aResult the result to remove + * + * @throws NS_ERROR_NULL_POINTER if aResult is null + */ + void removeResult(in nsIXULTemplateResult aResult); + + /** + * Inform the template builder that one result should be replaced with + * another. Both the old result (aOldResult) and the new result + * (aNewResult) must have the same id. The query node that the new result + * applies to must be specified using the aQueryNode parameter. + * + * This method is expected to have the same effect as calling both + * removeResult for the old result and addResult for the new result. + * + * @param aOldResult the old result + * @param aNewResult the new result + * @param aQueryNode the query that the new result applies to + * + * @throws NS_ERROR_NULL_POINTER if either argument is null, or + * NS_ERROR_INVALID_ARG if the ids don't match + */ + void replaceResult(in nsIXULTemplateResult aOldResult, + in nsIXULTemplateResult aNewResult, + in nsIDOMNode aQueryNode); + + /** + * Inform the template builder that one or more of the optional bindings + * for a result has changed. In this case, the rules are not reapplied as + * it is expected that the same rule will still apply. The builder will + * resynchronize any variables that are referenced in the action body. + * + * @param aResult the result to change + * + * @throws NS_ERROR_NULL_POINTER if aResult is null + */ + void resultBindingChanged(in nsIXULTemplateResult aResult); + + /** + * Return the result for a given id. Only one such result is returned and + * is always the result with that id associated with the active match. + * This method will return null is there is no result for the id. + * + * @param aId the id to return the result for + */ + nsIXULTemplateResult getResultForId(in AString aId); + + /** + * Retrieve the result corresponding to a generated element, or null is + * there isn't one. + * + * @param aContent element to result the result of + */ + nsIXULTemplateResult getResultForContent(in nsIDOMElement aElement); + + /** + * Returns true if the node has content generated for it. This method is + * intended to be called only by the RDF query processor. If aTag is set, + * the content must have a tag name that matches aTag. aTag may be ignored + * for builders that don't generate real DOM content. + * + * @param aNode node to check + * @param aTag tag that must match + */ + boolean hasGeneratedContent(in nsIRDFResource aNode, in nsIAtom aTag); + + /** + * Adds a rule filter for a given rule, which may be used for specialized + * rule filtering. Any existing filter on the rule is removed. The default + * conditions specified inside the <rule> tag are applied before the + * rule filter is applied, meaning that the filter may be used to further + * filter out results but not reaccept results that have already been + * rejected. + * + * @param aRule the rule to apply the filter to + * @param aFilter the filter to add + */ + void addRuleFilter(in nsIDOMNode aRule, in nsIXULTemplateRuleFilter aFilter); + + /** + * Called to initialize a XUL content builder on a particular root + * element. This element presumably has a ``datasources'' + * attribute, which the builder will parse to set up the template + * builder's datasources. + */ + [noscript] void init(in nsIContent aElement); + + /** + * Invoked lazily by a XUL element that needs its child content built. + * If aForceCreation is true, then the contents of an element will be + * generated even if it is closed. If false, the element will only + * generate its contents if it is open. This behaviour is used with menus. + */ + [noscript] void createContents(in nsIContent aElement, + in boolean aForceCreation); + + /** + * Add a listener to this template builder. The template builder + * holds a strong reference to the listener. + */ + void addListener(in nsIXULBuilderListener aListener); + + /** + * Remove a listener from this template builder. + */ + void removeListener(in nsIXULBuilderListener aListener); +}; + +/** + * nsIXULTreeBuilderObserver + * This interface allows clients of the XULTreeBuilder to define domain + * specific handling of specific nsITreeView methods that + * XULTreeBuilder does not implement. + */ +[scriptable, uuid(57CED9A7-EC0B-4A0E-8AEB-5DA32EBE951C)] +interface nsIXULTreeBuilderObserver : nsISupports +{ + const long DROP_BEFORE = -1; + const long DROP_ON = 0; + const long DROP_AFTER = 1; + /** + * Methods used by the drag feedback code to determine if a drag is allowable at + * the current location. To get the behavior where drops are only allowed on + * items, such as the mailNews folder pane, always return false whe + * the orientation is not DROP_ON. + */ + boolean canDrop(in long index, in long orientation, in nsIDOMDataTransfer dataTransfer); + + /** + * Called when the user drops something on this view. The |orientation| param + * specifies before/on/after the given |row|. + */ + void onDrop(in long row, in long orientation, in nsIDOMDataTransfer dataTransfer); + + /** + * Called when an item is opened or closed. + */ + void onToggleOpenState (in long index); + + /** + * Called when a header is clicked. + */ + void onCycleHeader(in wstring colID, in nsIDOMElement elt); + + /** + * Called when a cell in a non-selectable cycling column (e.g. + * unread/flag/etc.) is clicked. + */ + void onCycleCell(in long row, in wstring colID); + + /** + * Called when selection in the tree changes + */ + void onSelectionChanged(); + + /** + * A command API that can be used to invoke commands on the selection. + * The tree will automatically invoke this method when certain keys + * are pressed. For example, when the DEL key is pressed, performAction + * will be called with the "delete" string. + */ + void onPerformAction(in wstring action); + + /** + * A command API that can be used to invoke commands on a specific row. + */ + void onPerformActionOnRow(in wstring action, in long row); + + /** + * A command API that can be used to invoke commands on a specific cell. + */ + void onPerformActionOnCell(in wstring action, in long row, in wstring colID); +}; + +[scriptable, uuid(06b31b15-ebf5-4e74-a0e2-6bc0a18a3969)] +interface nsIXULTreeBuilder : nsISupports +{ + /** + * Retrieve the RDF resource associated with the specified row. + */ + nsIRDFResource getResourceAtIndex(in long aRowIndex); + + /** + * Retrieve the index associated with specified RDF resource. + */ + long getIndexOfResource(in nsIRDFResource resource); + + /** + * Add a Tree Builder Observer to handle Tree View + * methods that the base builder does not implement. + */ + void addObserver(in nsIXULTreeBuilderObserver aObserver); + + /** + * Remove an Tree Builder Observer. + */ + void removeObserver(in nsIXULTreeBuilderObserver aObserver); + + /** + * Sort the contents of the tree using the specified column. + */ + void sort(in nsIDOMElement aColumnElement); +}; + diff --git a/dom/xul/templates/nsIXULTemplateQueryProcessor.idl b/dom/xul/templates/nsIXULTemplateQueryProcessor.idl new file mode 100644 index 000000000..e064cf3b8 --- /dev/null +++ b/dom/xul/templates/nsIXULTemplateQueryProcessor.idl @@ -0,0 +1,276 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "domstubs.idl" + +interface nsIAtom; +interface nsIArray; +interface nsISimpleEnumerator; +interface nsIXULTemplateResult; +interface nsIXULTemplateRuleFilter; +interface nsIXULTemplateBuilder; + +/** + * A query processor takes a template query and generates results for it given + * a datasource and a reference point. There is a one-to-one relationship + * between a template builder and a query processor. The template builder + * creates the query processor, and there is no other means to retrieve it. + * + * A template query is the contents inside a <query> element within the + * template. The actual syntax is opaque to the template builder and defined + * by a query processor. The query is expected to consist of either text or + * DOM nodes that, when executed by a call to the generateResults method, will + * allow the generation of a list of results. + * + * The template builder will supply two variables, the reference variable and + * the member variable to further indicate what part of the datasource is to + * be examined in addition to the query itself. The reference is always + * a placeholder for the starting point and the member is always a placeholder + * for the end points (the results). + * + * The reference point is important when generating output recursively, as + * the query will be the same for each iteration, however, the reference point + * will differ. + * + * For instance, when examining an XML source, an XML query processor might + * begin at the node referred by the reference variable and end at a list of + * that node's children. + * + * Some queries may not need the reference variable if the syntax or the form + * of the data implies the value. For instance, a datasource that holds a + * table that can only produce one set of results. + * + * The reference variable may be specified in a template by setting the + * "container" attribute on the <template> element to the variable to use. The + * member variable may be specified in a similar way using the "member" + * attribute, or it may be specified in the first <action> body in the + * template as the value of a uri attribute on an element. A breadth-first + * search of the first action is performed to find this element. + * + * If unspecified, the default value of the reference variable is ?uri. + * + * For example, a query might have the following syntax: + * + * (?id, ?name, ?url) from Bookmarks where parentfolder = ?start + * + * This query might generate a result for each bookmark within a given folder. + * The variable ?start would be the reference variable, while the variable ?id + * would be the member variable, since it is the unique value that identifies + * a result. Each result will have the four variables referred to defined for + * it and the values may be retrieved using the result's getBindingFor and + * getBindingObjectFor methods. + * + * The template builder must call initializeForBuilding before the other + * methods, except for translateRef. The builder will then call compileQuery + * for each query in the template to compile the queries. When results need + * to be generated, the builder will call generateResults. The + * initializeForBuilding, compileQuery and addBinding methods may not be + * called after generateResults has been called until the builder indicates + * that the generated output is being removed by calling the done method. + * + * Currently, the datasource supplied to the methods will always be an + * nsIRDFDataSource or a DOM node, and will always be the same one in between + * calls to initializeForBuilding and done. + */ +[scriptable, uuid(C257573F-444F-468A-BA27-DE979DC55FE4)] +interface nsIXULTemplateQueryProcessor : nsISupports +{ + /** + * Retrieve the datasource to use for the query processor. The list of + * datasources in a template is specified using the datasources attribute as + * a space separated list of URIs. This list is processed by the builder and + * supplied to the query processor in the aDataSources array as a list of + * nsIURI objects or nsIDOMNode objects. This method may return an object + * corresponding to these URIs and the builder will supply this object to + * other query processor methods. For example, for an XML source, the + * datasource might be an nsIDOMNode. + * + * All of these URIs are checked by the builder so it is safe to use them, + * however note that a URI that redirects may still needs to be checked to + * ensure that the document containing aRootNode may access it. This is the + * responsibility of the query processor if it needs to load the content of + * the URI. + * + * If the query processor needs to load the datasource asynchronously, it + * may set the aShouldDelayBuilding returned parameter to true to delay + * building the template content, and call the builder's Rebuild method when + * the data is available. + * + * @param aDataSources the list of nsIURI objects and/or nsIDOMNode objects + * @param aRootNode the root node the builder is attached to + * @param aIsTrusted true if the template is in a trusted document + * @param aBuilder the template builder + * @param aShouldDelayBuilding [out] whether the builder should wait to + * build the content or not + * @returns a datasource object + */ + nsISupports getDatasource(in nsIArray aDataSources, + in nsIDOMNode aRootNode, + in boolean aIsTrusted, + in nsIXULTemplateBuilder aBuilder, + out boolean aShouldDelayBuilding); + + /** + * Initialize for query generation. This will be called before the rules are + * processed and whenever the template is rebuilt. This method must be + * called once before any of the other query processor methods except for + * translateRef. + * + * @param aDatasource datasource for the data + * @param aBuilder the template builder + * @param aRootNode the root node the builder is attached to + * + * @throws NS_ERROR_INVALID_ARG if the datasource is not supported or + * NS_ERROR_UNEXPECTED if generateResults has already been called. + */ + void initializeForBuilding(in nsISupports aDatasource, + in nsIXULTemplateBuilder aBuilder, + in nsIDOMNode aRootNode); + + /** + * Called when the template builder is being destroyed so that the query + * processor can clean up any state. The query processor should remove as + * much state as possible, such as results or references to the builder. + * This method will also be called when the template is going to be rebuilt. + */ + void done(); + + /** + * Compile a query from a node. The result of this function will later be + * passed to generateResults for result generation. If null is returned, + * the query will be ignored. + * + * The template builder will call this method once for each query within + * the template, before any results can be generated using generateResults, + * but after initializeForBuilding has been called. This method should not + * be called again for the same query unless the template is rebuilt. + * + * The reference variable may be used by the query processor as a + * placeholder for the reference point, or starting point in the query. + * + * The member variable is determined from the member attribute on the + * template, or from the uri in the first action's rule if that attribute is + * not present. A rule processor may use the member variable as a hint to + * indicate what variable is expected to contain the results. + * + * @param aBuilder the template builder + * @param aQuery <query> node to compile + * @param aRefVariable the reference variable + * @param aMemberVariable the member variable + * + * @returns a compiled query object + */ + nsISupports compileQuery(in nsIXULTemplateBuilder aBuilder, + in nsIDOMNode aQuery, + in nsIAtom aRefVariable, + in nsIAtom aMemberVariable); + + /** + * Generate the results of a query and return them in an enumerator. The + * enumerator must contain nsIXULTemplateResult objects. If there are no + * results, an empty enumerator must be returned. + * + * The datasource will be the same as the one passed to the earlier + * initializeForBuilding method. The context reference (aRef) is a reference + * point used when calculating results. + * + * The value of aQuery must be the result of a previous call to compileQuery + * from this query processor. This method may be called multiple times, + * typically with different values for aRef. + * + * @param aDatasource datasource for the data + * @param aRef context reference value used as a starting point + * @param aQuery the compiled query returned from query compilation + * + * @returns an enumerator of nsIXULTemplateResult objects as the results + * + * @throws NS_ERROR_INVALID_ARG if aQuery is invalid + */ + nsISimpleEnumerator generateResults(in nsISupports aDatasource, + in nsIXULTemplateResult aRef, + in nsISupports aQuery); + + /** + * Add a variable binding for a particular rule. A binding allows an + * additional variable to be set for a result, outside of those defined + * within the query. These bindings are always optional, in that they will + * never affect the results generated. + * + * This function will never be called after generateResults. Any bindings + * that were added should be applied to each result when the result's + * ruleMatched method is called, since the bindings are different for each + * rule. + * + * The reference aRef may be used to determine the reference when + * calculating the value for the binding, for example when a value should + * depend on the value of another variable. + * + * The syntax of the expression aExpr is defined by the query processor. If + * the syntax is invalid, the binding should be ignored. Only fatal errors + * should be thrown, or NS_ERROR_UNEXPECTED if generateResults has already + * been called. + * + * As an example, if the reference aRef is the variable '?count' which + * holds the value 5, and the expression aExpr is the string '+2', the value + * of the variable aVar would be 7, assuming the query processor considers + * the syntax '+2' to mean add two to the reference. + * + * @param aRuleNode rule to add the binding to + * @param aVar variable that will be bound + * @param aRef variable that holds reference value + * @param aExpr expression used to compute the value to assign + */ + void addBinding(in nsIDOMNode aRuleNode, + in nsIAtom aVar, + in nsIAtom aRef, + in AString aExpr); + + /** + * Translate a ref attribute string into a result. This is used as the + * reference point by the template builder when generating the first level + * of content. For recursive generation, the result from the parent + * generation phase will be used directly as the reference so a translation + * is not needed. This allows all levels to be generated using objects that + * all implement the nsIXULTemplateResult interface. + * + * This method may be called before initializeForBuilding, so the + * implementation may use the supplied datasource if it is needed to + * translate the reference. + * + * @param aDatasource datasource for the data + * @param aRefString the ref attribute string + * + * @return the translated ref + */ + nsIXULTemplateResult translateRef(in nsISupports aDatasource, + in AString aRefString); + + /** + * Compare two results to determine their order, used when sorting results. + * This method should return -1 when the left result is less than the right, + * 0 if both are equivalent, and 1 if the left is greater than the right. + * The comparison should only consider the values for the specified + * variable. + * + * If the comparison variable is null, the results may be + * sorted in a natural order, for instance, based on the order the data in + * stored in the datasource. + * + * The sort hints are the flags in nsIXULSortService. + * + * This method must only be called with results that were created by this + * query processor. + * + * @param aLeft the left result to compare + * @param aRight the right result to compare + * @param aVar variable to compare + * + * @param returns -1 if less, 0 if equal, or 1 if greater + */ + int32_t compareResults(in nsIXULTemplateResult aLeft, + in nsIXULTemplateResult aRight, + in nsIAtom aVar, + in unsigned long aSortHints); +}; diff --git a/dom/xul/templates/nsIXULTemplateResult.idl b/dom/xul/templates/nsIXULTemplateResult.idl new file mode 100644 index 000000000..6a8ac2439 --- /dev/null +++ b/dom/xul/templates/nsIXULTemplateResult.idl @@ -0,0 +1,116 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +interface nsIAtom; +interface nsIDOMNode; +interface nsIRDFResource; + +/** + * A single result generated from a template query. Each result is identified + * by an id, which must be unique within the set of results produced from a + * query. The result may optionally be identified by an RDF resource. + * + * Generally, the result and its id will be able to uniquely identify a node + * in the source data, such as an RDF or XML node. In other contexts, such as + * a database query, a result would represent a particular record. + * + * A result is expected to only be created by a query processor. + * + * Each result also contains a set of variable bindings. The value for a + * particular variable may be retrieved using the getBindingFor and + * getBindingObjectFor methods. + */ +[scriptable, uuid(ebea0230-36fa-41b7-8e31-760806057965)] +interface nsIXULTemplateResult : nsISupports +{ + /** + * True if the result represents a container. + */ + readonly attribute boolean isContainer; + + /** + * True if the result represents an empty container. + */ + readonly attribute boolean isEmpty; + + /** + * True if the template builder may use this result as the reference point + * for additional recursive processing of the template. The template builder + * will reprocess the template using this result as the reference point and + * generate output content that is expected to be inserted as children of the + * output generated for this result. If false, child content is not + * processed. This property identifies only the default handling and may be + * overriden by syntax used in the template. + */ + readonly attribute boolean mayProcessChildren; + + /** + * ID of the result. The DOM element created for this result, if any, will + * have its id attribute set to this value. The id must be unique for a + * query. + */ + readonly attribute AString id; + + /** + * Resource for the result, which may be null. If set, the resource uri + * must be the same as the ID property. + */ + readonly attribute nsIRDFResource resource; + + /** + * The type of the object. The predefined value 'separator' may be used + * for separators. Other values may be used for application specific + * purposes. + */ + readonly attribute AString type; + + /** + * Get the string representation of the value of a variable for this + * result. This string will be used in the action body from a template as + * the replacement text. For instance, if the text ?name appears in an + * attribute within the action body, it will be replaced with the result + * of this method. The question mark is considered part of the variable + * name, thus aVar should be ?name and not simply name. + * + * @param aVar the variable to look up + * + * @return the value for the variable or a null string if it has no value + */ + AString getBindingFor(in nsIAtom aVar); + + /** + * Get an object value for a variable such as ?name for this result. + * + * This method may return null for a variable, even if getBindingFor returns + * a non-null value for the same variable. This method is provided as a + * convenience when sorting results. + * + * @param aVar the variable to look up + * + * @return the value for the variable or null if it has no value + */ + nsISupports getBindingObjectFor(in nsIAtom aVar); + + /** + * Indicate that a particular rule of a query has matched and that output + * will be generated for it. Both the query as compiled by the query + * processor's compileQuery method and the XUL <rule> element are supplied. + * The query must always be one that was compiled by the query processor + * that created this result. The <rule> element must always be a child of + * the <query> element that was used to compile the query. + * + * @param aQuery the query that matched + * @param aRuleNode the rule node that matched + */ + void ruleMatched(in nsISupports aQuery, in nsIDOMNode aRuleNode); + + /** + * Indicate that the output for a result has beeen removed and that the + * result is no longer being used by the builder. + */ + void hasBeenRemoved(); +}; diff --git a/dom/xul/templates/nsIXULTemplateRuleFilter.idl b/dom/xul/templates/nsIXULTemplateRuleFilter.idl new file mode 100644 index 000000000..59c88e072 --- /dev/null +++ b/dom/xul/templates/nsIXULTemplateRuleFilter.idl @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "domstubs.idl" + +interface nsISupports; +interface nsIXULTemplateResult; + +/** + * A rule filter may be used to add additional filtering of results to a rule. + * The filter is used to further reject results from matching the template's + * rules, beyond what the template syntax can do itself, thus allowing for + * more complex result filtering. The rule filter is applied after the rule + * syntax within the template. + * + * Only one filter may apply to each rule within the template and may be + * assigned using the template builder's addRuleFilter method. + */ +[scriptable, uuid(819cd1ed-8010-42e1-a8b9-778b726a1ff3)] +interface nsIXULTemplateRuleFilter : nsISupports +{ + /** + * Evaluate a result and return true if the result is accepted by this + * filter, or false if it is rejected. Accepted results will have output + * generated for them for the rule. Rejected results will not, but they + * may still match another rule. + * + * @param aRef the result to examine + * @param aRule the rule node + * + * @return true if the rule matches + */ + boolean match(in nsIXULTemplateResult aRef, in nsIDOMNode aRule); +}; diff --git a/dom/xul/templates/nsInstantiationNode.cpp b/dom/xul/templates/nsInstantiationNode.cpp new file mode 100644 index 000000000..9079d4189 --- /dev/null +++ b/dom/xul/templates/nsInstantiationNode.cpp @@ -0,0 +1,83 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsInstantiationNode.h" +#include "nsTemplateRule.h" +#include "nsXULTemplateQueryProcessorRDF.h" + +#include "mozilla/Logging.h" +extern mozilla::LazyLogModule gXULTemplateLog; + +nsInstantiationNode::nsInstantiationNode(nsXULTemplateQueryProcessorRDF* aProcessor, + nsRDFQuery* aQuery) + : mProcessor(aProcessor), + mQuery(aQuery) +{ + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + ("nsInstantiationNode[%p] query=%p", this, aQuery)); + + MOZ_COUNT_CTOR(nsInstantiationNode); +} + + +nsInstantiationNode::~nsInstantiationNode() +{ + MOZ_COUNT_DTOR(nsInstantiationNode); +} + +nsresult +nsInstantiationNode::Propagate(InstantiationSet& aInstantiations, + bool aIsUpdate, bool& aTakenInstantiations) +{ + // In update mode, iterate through the results and call the template + // builder to update them. In non-update mode, cache them in the processor + // to be used during processing. The results are cached in the processor + // so that the simple rules are only computed once. In this situation, all + // data for all queries are calculated at once. + nsresult rv = NS_OK; + + aTakenInstantiations = false; + + if (aIsUpdate) { + // Iterate through newly added keys to determine which rules fired. + // + // XXXwaterson Unfortunately, this could also lead to retractions; + // e.g., (container ?a ^empty false) could become "unmatched". How + // to track those? + nsCOMPtr<nsIDOMNode> querynode; + mQuery->GetQueryNode(getter_AddRefs(querynode)); + + InstantiationSet::ConstIterator last = aInstantiations.Last(); + for (InstantiationSet::ConstIterator inst = aInstantiations.First(); inst != last; ++inst) { + nsAssignmentSet assignments = inst->mAssignments; + + nsCOMPtr<nsIRDFNode> node; + assignments.GetAssignmentFor(mQuery->mMemberVariable, + getter_AddRefs(node)); + if (node) { + nsCOMPtr<nsIRDFResource> resource = do_QueryInterface(node); + if (resource) { + RefPtr<nsXULTemplateResultRDF> nextresult = + new nsXULTemplateResultRDF(mQuery, *inst, resource); + if (! nextresult) + return NS_ERROR_OUT_OF_MEMORY; + + rv = mProcessor->AddMemoryElements(*inst, nextresult); + if (NS_FAILED(rv)) + return rv; + + mProcessor->GetBuilder()->AddResult(nextresult, querynode); + } + } + } + } + else { + nsresult rv = mQuery->SetCachedResults(mProcessor, aInstantiations); + if (NS_SUCCEEDED(rv)) + aTakenInstantiations = true; + } + + return rv; +} diff --git a/dom/xul/templates/nsInstantiationNode.h b/dom/xul/templates/nsInstantiationNode.h new file mode 100644 index 000000000..5b9c7b9f1 --- /dev/null +++ b/dom/xul/templates/nsInstantiationNode.h @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsInstantiationNode_h__ +#define nsInstantiationNode_h__ + +#include "mozilla/Attributes.h" +#include "nsRuleNetwork.h" +#include "nsRDFQuery.h" + +class nsXULTemplateQueryProcessorRDF; + +/** + * A leaf-level node in the rule network. If any instantiations + * propagate to this node, then we know we've matched a rule. + */ +class nsInstantiationNode : public ReteNode +{ +public: + nsInstantiationNode(nsXULTemplateQueryProcessorRDF* aProcessor, + nsRDFQuery* aRule); + + ~nsInstantiationNode(); + + // "downward" propagations + virtual nsresult Propagate(InstantiationSet& aInstantiations, + bool aIsUpdate, bool& aMatched) override; + +protected: + + nsXULTemplateQueryProcessorRDF* mProcessor; + nsRDFQuery* mQuery; +}; + +#endif // nsInstantiationNode_h__ diff --git a/dom/xul/templates/nsRDFBinding.cpp b/dom/xul/templates/nsRDFBinding.cpp new file mode 100644 index 000000000..120adfa79 --- /dev/null +++ b/dom/xul/templates/nsRDFBinding.cpp @@ -0,0 +1,265 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsXULTemplateQueryProcessorRDF.h" +#include "nsXULTemplateResultRDF.h" +#include "nsRDFBinding.h" + +#ifdef DEBUG +#include "nsXULContentUtils.h" +#endif + +RDFBindingSet::~RDFBindingSet() +{ + while (mFirst) { + RDFBinding* doomed = mFirst; + mFirst = mFirst->mNext; + delete doomed; + } + + MOZ_COUNT_DTOR(RDFBindingSet); +} + +nsresult +RDFBindingSet::AddBinding(nsIAtom* aVar, nsIAtom* aRef, nsIRDFResource* aPredicate) +{ + RDFBinding* newbinding = new RDFBinding(aRef, aPredicate, aVar); + if (mFirst) { + RDFBinding* binding = mFirst; + + while (binding) { + // the binding is dependant on the calculation of a previous binding + if (binding->mSubjectVariable == aVar) + newbinding->mHasDependency = true; + + // if the target variable is already used in a binding, ignore it + // since it won't be useful for anything + if (binding->mTargetVariable == aVar) { + delete newbinding; + return NS_OK; + } + + // add the binding at the end of the list + if (! binding->mNext) { + binding->mNext = newbinding; + break; + } + + binding = binding->mNext; + } + } + else { + mFirst = newbinding; + } + + mCount++; + + return NS_OK; +} + +bool +RDFBindingSet::SyncAssignments(nsIRDFResource* aSubject, + nsIRDFResource* aPredicate, + nsIRDFNode* aTarget, + nsIAtom* aMemberVariable, + nsXULTemplateResultRDF* aResult, + nsBindingValues& aBindingValues) +{ + NS_ASSERTION(aBindingValues.GetBindingSet() == this, + "nsBindingValues not for this RDFBindingSet"); + NS_PRECONDITION(aResult, "Must have result"); + + bool needSync = false; + nsCOMPtr<nsIRDFNode>* valuesArray = aBindingValues.ValuesArray(); + if (!valuesArray) + return false; + + RDFBinding* binding = mFirst; + int32_t count = 0; + + // QI for proper comparisons just to be safe + nsCOMPtr<nsIRDFNode> subjectnode = do_QueryInterface(aSubject); + + // iterate through the bindings looking for ones that would match the RDF + // nodes that were involved in a change + nsCOMPtr<nsIRDFNode> value; + while (binding) { + if (aPredicate == binding->mPredicate) { + // if the source of the binding is the member variable, optimize + if (binding->mSubjectVariable == aMemberVariable) { + valuesArray[count] = aTarget; + needSync = true; + } + else { + aResult->GetAssignment(binding->mSubjectVariable, getter_AddRefs(value)); + if (value == subjectnode) { + valuesArray[count] = aTarget; + needSync = true; + } + } + } + + binding = binding->mNext; + count++; + } + + return needSync; +} + +void +RDFBindingSet::AddDependencies(nsIRDFResource* aSubject, + nsXULTemplateResultRDF* aResult) +{ + NS_PRECONDITION(aResult, "Must have result"); + + // iterate through the bindings and add binding dependencies to the + // processor + + nsXULTemplateQueryProcessorRDF* processor = aResult->GetProcessor(); + if (! processor) + return; + + nsCOMPtr<nsIRDFNode> value; + + RDFBinding* binding = mFirst; + while (binding) { + aResult->GetAssignment(binding->mSubjectVariable, getter_AddRefs(value)); + + nsCOMPtr<nsIRDFResource> valueres = do_QueryInterface(value); + if (valueres) + processor->AddBindingDependency(aResult, valueres); + + binding = binding->mNext; + } +} + +void +RDFBindingSet::RemoveDependencies(nsIRDFResource* aSubject, + nsXULTemplateResultRDF* aResult) +{ + NS_PRECONDITION(aResult, "Must have result"); + + // iterate through the bindings and remove binding dependencies from the + // processor + + nsXULTemplateQueryProcessorRDF* processor = aResult->GetProcessor(); + if (! processor) + return; + + nsCOMPtr<nsIRDFNode> value; + + RDFBinding* binding = mFirst; + while (binding) { + aResult->GetAssignment(binding->mSubjectVariable, getter_AddRefs(value)); + + nsCOMPtr<nsIRDFResource> valueres = do_QueryInterface(value); + if (valueres) + processor->RemoveBindingDependency(aResult, valueres); + + binding = binding->mNext; + } +} + +int32_t +RDFBindingSet::LookupTargetIndex(nsIAtom* aTargetVariable, RDFBinding** aBinding) +{ + int32_t idx = 0; + RDFBinding* binding = mFirst; + + while (binding) { + if (binding->mTargetVariable == aTargetVariable) { + *aBinding = binding; + return idx; + } + idx++; + binding = binding->mNext; + } + + return -1; +} + +nsBindingValues::~nsBindingValues() +{ + ClearBindingSet(); + MOZ_COUNT_DTOR(nsBindingValues); +} + +void +nsBindingValues::ClearBindingSet() +{ + if (mBindings && mValues) { + delete [] mValues; + mValues = nullptr; + } + + mBindings = nullptr; +} + +nsresult +nsBindingValues::SetBindingSet(RDFBindingSet* aBindings) +{ + ClearBindingSet(); + + int32_t count = aBindings->Count(); + if (count) { + mValues = new nsCOMPtr<nsIRDFNode>[count]; + mBindings = aBindings; + } + else { + mValues = nullptr; + } + + return NS_OK; +} + +void +nsBindingValues::GetAssignmentFor(nsXULTemplateResultRDF* aResult, + nsIAtom* aVar, + nsIRDFNode** aValue) +{ + *aValue = nullptr; + + // assignments are calculated lazily when asked for. The only issue is + // when a binding has no value in the RDF graph, it will be checked again + // every time. + + if (mBindings && mValues) { + RDFBinding* binding; + int32_t idx = mBindings->LookupTargetIndex(aVar, &binding); + if (idx >= 0) { + *aValue = mValues[idx]; + if (*aValue) { + NS_ADDREF(*aValue); + } + else { + nsXULTemplateQueryProcessorRDF* processor = aResult->GetProcessor(); + if (! processor) + return; + + nsIRDFDataSource* ds = processor->GetDataSource(); + if (! ds) + return; + + nsCOMPtr<nsIRDFNode> subjectValue; + aResult->GetAssignment(binding->mSubjectVariable, + getter_AddRefs(subjectValue)); + if (subjectValue) { + nsCOMPtr<nsIRDFResource> subject = do_QueryInterface(subjectValue); + ds->GetTarget(subject, binding->mPredicate, true, aValue); + if (*aValue) + mValues[idx] = *aValue; + } + } + } + } +} + +void +nsBindingValues::RemoveDependencies(nsIRDFResource* aSubject, + nsXULTemplateResultRDF* aResult) +{ + if (mBindings) + mBindings->RemoveDependencies(aSubject, aResult); +} diff --git a/dom/xul/templates/nsRDFBinding.h b/dom/xul/templates/nsRDFBinding.h new file mode 100644 index 000000000..92c8b16ca --- /dev/null +++ b/dom/xul/templates/nsRDFBinding.h @@ -0,0 +1,216 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsRDFBinding_h__ +#define nsRDFBinding_h__ + +#include "nsIAtom.h" +#include "nsIRDFResource.h" +#include "nsISupportsImpl.h" + +class nsXULTemplateResultRDF; +class nsBindingValues; + +/* + * Classes related to storing bindings for RDF handling. + */ + +/* + * a <binding> descriptors + */ +class RDFBinding { + +public: + + nsCOMPtr<nsIAtom> mSubjectVariable; + nsCOMPtr<nsIRDFResource> mPredicate; + nsCOMPtr<nsIAtom> mTargetVariable; + + // indicates whether a binding is dependant on the result from a + // previous binding + bool mHasDependency; + + RDFBinding* mNext; + +private: + + friend class RDFBindingSet; + + RDFBinding(nsIAtom* aSubjectVariable, + nsIRDFResource* aPredicate, + nsIAtom* aTargetVariable) + : mSubjectVariable(aSubjectVariable), + mPredicate(aPredicate), + mTargetVariable(aTargetVariable), + mHasDependency(false), + mNext(nullptr) + { + MOZ_COUNT_CTOR(RDFBinding); + } + + ~RDFBinding() + { + MOZ_COUNT_DTOR(RDFBinding); + } +}; + +/* + * a collection of <binding> descriptors. This object is refcounted by + * nsBindingValues objects and the query processor. + */ +class RDFBindingSet final +{ +private: + // Private destructor, to discourage deletion outside of Release(): + ~RDFBindingSet(); + + // the number of bindings + int32_t mCount; + + // pointer to the first binding in a linked list + RDFBinding* mFirst; + +public: + + RDFBindingSet() + : mCount(0), + mFirst(nullptr) + { + MOZ_COUNT_CTOR(RDFBindingSet); + } + + NS_INLINE_DECL_REFCOUNTING(RDFBindingSet) + + int32_t Count() const { return mCount; } + + /* + * Add a binding (aRef -> aPredicate -> aVar) to the set + */ + nsresult + AddBinding(nsIAtom* aVar, nsIAtom* aRef, nsIRDFResource* aPredicate); + + /* + * Return true if the binding set contains a binding which would cause + * the result to need resynchronizing for an RDF triple. The member + * variable may be supplied as an optimization since bindings most + * commonly use the member variable as the subject. If aMemberVariable + * is set, aSubject must be the value of the member variable for the + * result. The supplied binding values aBindingValues must be values + * using this binding set (that is aBindingValues->GetBindingSet() == this) + * + * @param aSubject subject of the RDF triple + * @param aPredicate predicate of the RDF triple + * @param aTarget target of the RDF triple + * @param aMemberVariable member variable for the query for the binding + * @param aResult result to synchronize + * @param aBindingValues the values for the bindings for the result + */ + bool + SyncAssignments(nsIRDFResource* aSubject, + nsIRDFResource* aPredicate, + nsIRDFNode* aTarget, + nsIAtom* aMemberVariable, + nsXULTemplateResultRDF* aResult, + nsBindingValues& aBindingValues); + + /* + * The query processor maintains a map of subjects to an array of results. + * This is used such that when a new assertion is added to the RDF graph, + * the results associated with the subject of that triple may be checked + * to see if their bindings have changed. The AddDependencies method adds + * these subject dependencies to the map. + */ + void + AddDependencies(nsIRDFResource* aSubject, + nsXULTemplateResultRDF* aResult); + + /* + * Remove the results from the dependencies map when results are deleted. + */ + void + RemoveDependencies(nsIRDFResource* aSubject, + nsXULTemplateResultRDF* aResult); + + /* + * The nsBindingValues classes stores an array of values, one for each + * target symbol that could be set by the bindings in the set. + * LookupTargetIndex determines the index into the array for a given + * target symbol. + */ + int32_t + LookupTargetIndex(nsIAtom* aTargetVariable, RDFBinding** aBinding); +}; + +/* + * A set of values of bindings. This object is used once per result. + * This stores a reference to the binding set and an array of node values. + * Since the binding set is used once per query and the values are + * used once per result, we reduce size by only storing the value array's + * length in the binding set. This is possible since the array is always + * a fixed length for a particular binding set. + * + * XXX ndeakin We may want to revisit this later since it makes the code + * more complicated. + */ +class nsBindingValues +{ +protected: + + // the binding set + RefPtr<RDFBindingSet> mBindings; + + /* + * A set of values for variable bindings. To look up a binding value, + * scan through the binding set in mBindings for the right target atom. + * Its index will correspond to the index in this array. The size of this + * array is determined by the RDFBindingSet's Count(). + */ + nsCOMPtr<nsIRDFNode>* mValues; + +public: + + nsBindingValues() + : mBindings(nullptr), + mValues(nullptr) + { + MOZ_COUNT_CTOR(nsBindingValues); + } + + ~nsBindingValues(); + + + /** + * Clear the binding set, to be called when the nsBindingValues is deleted + * or a new binding set is being set. + */ + void ClearBindingSet(); + + RDFBindingSet* GetBindingSet() { return mBindings; } + + /** + * Set the binding set to use. This needs to be called once a rule matches + * since it is then known which bindings will apply. + */ + nsresult SetBindingSet(RDFBindingSet* aBindings); + + nsCOMPtr<nsIRDFNode>* ValuesArray() { return mValues; } + + /* + * Retrieve the assignment for a particular variable + */ + void + GetAssignmentFor(nsXULTemplateResultRDF* aResult, + nsIAtom* aVar, + nsIRDFNode** aValue); + + /* + * Remove depenedencies the bindings have on particular resources + */ + void + RemoveDependencies(nsIRDFResource* aSubject, + nsXULTemplateResultRDF* aResult); +}; + +#endif // nsRDFBinding_h__ diff --git a/dom/xul/templates/nsRDFConInstanceTestNode.cpp b/dom/xul/templates/nsRDFConInstanceTestNode.cpp new file mode 100644 index 000000000..a96809743 --- /dev/null +++ b/dom/xul/templates/nsRDFConInstanceTestNode.cpp @@ -0,0 +1,281 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsIComponentManager.h" +#include "nsIRDFContainer.h" +#include "nsIRDFContainerUtils.h" +#include "nsIServiceManager.h" +#include "nsRDFCID.h" +#include "nsRDFConInstanceTestNode.h" +#include "nsResourceSet.h" + +#include "mozilla/Logging.h" +#include "nsXULContentUtils.h" + +using mozilla::LogLevel; + +extern mozilla::LazyLogModule gXULTemplateLog; + +static const char* +TestToString(nsRDFConInstanceTestNode::Test aTest) { + switch (aTest) { + case nsRDFConInstanceTestNode::eFalse: return "false"; + case nsRDFConInstanceTestNode::eTrue: return "true"; + case nsRDFConInstanceTestNode::eDontCare: return "dontcare"; + } + return "?"; +} + +nsRDFConInstanceTestNode::nsRDFConInstanceTestNode(TestNode* aParent, + nsXULTemplateQueryProcessorRDF* aProcessor, + nsIAtom* aContainerVariable, + Test aContainer, + Test aEmpty) + : nsRDFTestNode(aParent), + mProcessor(aProcessor), + mContainerVariable(aContainerVariable), + mContainer(aContainer), + mEmpty(aEmpty) +{ + if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) { + nsAutoCString props; + + nsResourceSet& containmentProps = aProcessor->ContainmentProperties(); + nsResourceSet::ConstIterator last = containmentProps.Last(); + nsResourceSet::ConstIterator first = containmentProps.First(); + nsResourceSet::ConstIterator iter; + + for (iter = first; iter != last; ++iter) { + if (iter != first) + props += " "; + + const char* str; + iter->GetValueConst(&str); + + props += str; + } + + nsAutoString cvar(NS_LITERAL_STRING("(none)")); + if (mContainerVariable) + mContainerVariable->ToString(cvar); + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + ("nsRDFConInstanceTestNode[%p]: parent=%p member-props=(%s) container-var=%s container=%s empty=%s", + this, + aParent, + props.get(), + NS_ConvertUTF16toUTF8(cvar).get(), + TestToString(aContainer), + TestToString(aEmpty))); + } +} + +nsresult +nsRDFConInstanceTestNode::FilterInstantiations(InstantiationSet& aInstantiations, + bool* aCantHandleYet) const +{ + nsresult rv; + + if (aCantHandleYet) + *aCantHandleYet = false; + + nsCOMPtr<nsIRDFContainerUtils> rdfc + = do_GetService("@mozilla.org/rdf/container-utils;1"); + + if (! rdfc) + return NS_ERROR_FAILURE; + + nsIRDFDataSource* ds = mProcessor->GetDataSource(); + + InstantiationSet::Iterator last = aInstantiations.Last(); + for (InstantiationSet::Iterator inst = aInstantiations.First(); inst != last; ++inst) { + nsCOMPtr<nsIRDFNode> value; + if (! inst->mAssignments.GetAssignmentFor(mContainerVariable, getter_AddRefs(value))) { + NS_ERROR("can't do unbounded container testing"); + return NS_ERROR_UNEXPECTED; + } + + nsCOMPtr<nsIRDFResource> valueres = do_QueryInterface(value); + if (! valueres) { + aInstantiations.Erase(inst--); + continue; + } + + if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) { + const char* container = "(unbound)"; + valueres->GetValueConst(&container); + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + ("nsRDFConInstanceTestNode[%p]::FilterInstantiations() container=[%s]", + this, container)); + } + + nsCOMPtr<nsIRDFContainer> rdfcontainer; + + bool isRDFContainer; + rv = rdfc->IsContainer(ds, valueres, &isRDFContainer); + if (NS_FAILED(rv)) return rv; + + if (mEmpty != eDontCare || mContainer != eDontCare) { + Test empty = eDontCare; + Test container = eDontCare; + + if (isRDFContainer) { + // It's an RDF container. Use the container utilities + // to deduce what's in it. + container = eTrue; + + // XXX should cache the factory + rdfcontainer = do_CreateInstance("@mozilla.org/rdf/container;1", &rv); + if (NS_FAILED(rv)) return rv; + + rv = rdfcontainer->Init(ds, valueres); + if (NS_FAILED(rv)) return rv; + + int32_t count; + rv = rdfcontainer->GetCount(&count); + if (NS_FAILED(rv)) return rv; + + empty = (count == 0) ? eTrue : eFalse; + } else { + empty = eTrue; + container = eFalse; + + // First do the simple check of finding some outward + // arcs; there should be only a few containment arcs, so this can + // save us time from dealing with an iterator later on + nsResourceSet& containmentProps = mProcessor->ContainmentProperties(); + for (nsResourceSet::ConstIterator property = containmentProps.First(); + property != containmentProps.Last(); + ++property) { + nsCOMPtr<nsIRDFNode> target; + rv = ds->GetTarget(valueres, *property, true, getter_AddRefs(target)); + if (NS_FAILED(rv)) return rv; + + if (target != nullptr) { + // bingo. we found one. + empty = eFalse; + container = eTrue; + break; + } + } + + // if we still don't think its a container, but we + // want to know for sure whether it is or not, we need + // to check ArcLabelsOut for potential container arcs. + if (container == eFalse && mContainer != eDontCare) { + nsCOMPtr<nsISimpleEnumerator> arcsout; + rv = ds->ArcLabelsOut(valueres, getter_AddRefs(arcsout)); + if (NS_FAILED(rv)) return rv; + + while (1) { + bool hasmore; + rv = arcsout->HasMoreElements(&hasmore); + if (NS_FAILED(rv)) return rv; + + if (! hasmore) + break; + + nsCOMPtr<nsISupports> isupports; + rv = arcsout->GetNext(getter_AddRefs(isupports)); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr<nsIRDFResource> property = do_QueryInterface(isupports); + NS_ASSERTION(property != nullptr, "not a property"); + if (! property) + return NS_ERROR_UNEXPECTED; + + if (mProcessor->ContainmentProperties().Contains(property)) { + container = eTrue; + break; + } + } + } + } + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + (" empty => %s", + (empty == mEmpty) ? "consistent" : "inconsistent")); + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + (" container => %s", + (container == mContainer) ? "consistent" : "inconsistent")); + + if (((mEmpty == empty) && (mContainer == container)) || + ((mEmpty == eDontCare) && (mContainer == container)) || + ((mContainer == eDontCare) && (mEmpty == empty))) + { + Element* element = + new nsRDFConInstanceTestNode::Element(valueres, container, empty); + inst->AddSupportingElement(element); + } + else { + aInstantiations.Erase(inst--); + } + } + } + + return NS_OK; +} + +bool +nsRDFConInstanceTestNode::CanPropagate(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget, + Instantiation& aInitialBindings) const +{ + nsresult rv; + + bool canpropagate = false; + + nsCOMPtr<nsIRDFContainerUtils> rdfc + = do_GetService("@mozilla.org/rdf/container-utils;1"); + + if (! rdfc) + return false; + + // We can certainly propagate ordinal properties + rv = rdfc->IsOrdinalProperty(aProperty, &canpropagate); + if (NS_FAILED(rv)) return false; + + if (! canpropagate) { + canpropagate = mProcessor->ContainmentProperties().Contains(aProperty); + } + + if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) { + const char* source; + aSource->GetValueConst(&source); + + const char* property; + aProperty->GetValueConst(&property); + + nsAutoString target; + nsXULContentUtils::GetTextForNode(aTarget, target); + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + ("nsRDFConInstanceTestNode[%p]: CanPropagate([%s]==[%s]=>[%s]) => %s", + this, source, property, NS_ConvertUTF16toUTF8(target).get(), + canpropagate ? "true" : "false")); + } + + if (canpropagate) { + aInitialBindings.AddAssignment(mContainerVariable, aSource); + return true; + } + + return false; +} + +void +nsRDFConInstanceTestNode::Retract(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget) const +{ + // XXXwaterson oof. complicated. figure this out. + if (0) { + mProcessor->RetractElement(Element(aSource, mContainer, mEmpty)); + } +} + diff --git a/dom/xul/templates/nsRDFConInstanceTestNode.h b/dom/xul/templates/nsRDFConInstanceTestNode.h new file mode 100644 index 000000000..0ed96909e --- /dev/null +++ b/dom/xul/templates/nsRDFConInstanceTestNode.h @@ -0,0 +1,88 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsRDFConInstanceTestNode_h__ +#define nsRDFConInstanceTestNode_h__ + +#include "mozilla/Attributes.h" +#include "nscore.h" +#include "nsRDFTestNode.h" +#include "nsIRDFResource.h" +#include "nsIRDFDataSource.h" +#include "nsXULTemplateQueryProcessorRDF.h" + +/** + * Rule network node that tests if a resource is an RDF container, or + * uses multi-attributes to ``contain'' other elements. + */ +class nsRDFConInstanceTestNode : public nsRDFTestNode +{ +public: + enum Test { eFalse, eTrue, eDontCare }; + + nsRDFConInstanceTestNode(TestNode* aParent, + nsXULTemplateQueryProcessorRDF* aProcessor, + nsIAtom* aContainerVariable, + Test aContainer, + Test aEmpty); + + virtual nsresult FilterInstantiations(InstantiationSet& aInstantiations, + bool* aCantHandleYet) const override; + + virtual bool + CanPropagate(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget, + Instantiation& aInitialBindings) const override; + + virtual void + Retract(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget) const override; + + + class Element : public MemoryElement { + public: + Element(nsIRDFResource* aContainer, + Test aContainerTest, + Test aEmptyTest) + : mContainer(aContainer), + mContainerTest(aContainerTest), + mEmptyTest(aEmptyTest) { + MOZ_COUNT_CTOR(nsRDFConInstanceTestNode::Element); } + + virtual ~Element() { MOZ_COUNT_DTOR(nsRDFConInstanceTestNode::Element); } + + virtual const char* Type() const override { + return "nsRDFConInstanceTestNode::Element"; } + + virtual PLHashNumber Hash() const override { + return mozilla::HashGeneric(mContainerTest, mEmptyTest, mContainer.get()); + } + + virtual bool Equals(const MemoryElement& aElement) const override { + if (aElement.Type() == Type()) { + const Element& element = static_cast<const Element&>(aElement); + return mContainer == element.mContainer + && mContainerTest == element.mContainerTest + && mEmptyTest == element.mEmptyTest; + } + return false; } + + protected: + nsCOMPtr<nsIRDFResource> mContainer; + Test mContainerTest; + Test mEmptyTest; + }; + +protected: + nsXULTemplateQueryProcessorRDF* mProcessor; + nsCOMPtr<nsIAtom> mContainerVariable; + Test mContainer; + Test mEmpty; +}; + +#endif // nsRDFConInstanceTestNode_h__ + diff --git a/dom/xul/templates/nsRDFConMemberTestNode.cpp b/dom/xul/templates/nsRDFConMemberTestNode.cpp new file mode 100644 index 000000000..0bb96a5b5 --- /dev/null +++ b/dom/xul/templates/nsRDFConMemberTestNode.cpp @@ -0,0 +1,510 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsRDFConMemberTestNode.h" +#include "nsIRDFContainer.h" +#include "nsIRDFContainerUtils.h" +#include "nsRDFCID.h" +#include "nsIServiceManager.h" +#include "nsResourceSet.h" +#include "nsString.h" +#include "nsXULContentUtils.h" + +#include "mozilla/Logging.h" + +using mozilla::LogLevel; + +extern mozilla::LazyLogModule gXULTemplateLog; + +nsRDFConMemberTestNode::nsRDFConMemberTestNode(TestNode* aParent, + nsXULTemplateQueryProcessorRDF* aProcessor, + nsIAtom *aContainerVariable, + nsIAtom *aMemberVariable) + : nsRDFTestNode(aParent), + mProcessor(aProcessor), + mContainerVariable(aContainerVariable), + mMemberVariable(aMemberVariable) +{ + if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) { + nsAutoCString props; + + nsResourceSet& containmentProps = aProcessor->ContainmentProperties(); + nsResourceSet::ConstIterator last = containmentProps.Last(); + nsResourceSet::ConstIterator first = containmentProps.First(); + nsResourceSet::ConstIterator iter; + + for (iter = first; iter != last; ++iter) { + if (iter != first) + props += " "; + + const char* str; + iter->GetValueConst(&str); + + props += str; + } + + nsAutoString cvar(NS_LITERAL_STRING("(none)")); + if (mContainerVariable) + mContainerVariable->ToString(cvar); + + nsAutoString mvar(NS_LITERAL_STRING("(none)")); + if (mMemberVariable) + mMemberVariable->ToString(mvar); + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + ("nsRDFConMemberTestNode[%p]: parent=%p member-props=(%s) container-var=%s member-var=%s", + this, + aParent, + props.get(), + NS_ConvertUTF16toUTF8(cvar).get(), + NS_ConvertUTF16toUTF8(mvar).get())); + } +} + +nsresult +nsRDFConMemberTestNode::FilterInstantiations(InstantiationSet& aInstantiations, + bool* aCantHandleYet) const +{ + // XXX Uh, factor me, please! + nsresult rv; + + if (aCantHandleYet) + *aCantHandleYet = false; + + nsCOMPtr<nsIRDFContainerUtils> rdfc = + do_GetService("@mozilla.org/rdf/container-utils;1"); + + if (! rdfc) + return NS_ERROR_FAILURE; + + nsIRDFDataSource* ds = mProcessor->GetDataSource(); + + InstantiationSet::Iterator last = aInstantiations.Last(); + for (InstantiationSet::Iterator inst = aInstantiations.First(); inst != last; ++inst) { + bool hasContainerBinding; + nsCOMPtr<nsIRDFNode> containerValue; + hasContainerBinding = inst->mAssignments.GetAssignmentFor(mContainerVariable, + getter_AddRefs(containerValue)); + + nsCOMPtr<nsIRDFResource> containerRes = do_QueryInterface(containerValue); + + nsCOMPtr<nsIRDFContainer> rdfcontainer; + + if (hasContainerBinding && containerRes) { + // If we have a container assignment, then see if the + // container is an RDF container (bag, seq, alt), and if + // so, wrap it. + bool isRDFContainer; + rv = rdfc->IsContainer(ds, containerRes, &isRDFContainer); + if (NS_FAILED(rv)) return rv; + + if (isRDFContainer) { + rdfcontainer = do_CreateInstance("@mozilla.org/rdf/container;1", &rv); + if (NS_FAILED(rv)) return rv; + + rv = rdfcontainer->Init(ds, containerRes); + if (NS_FAILED(rv)) return rv; + } + } + + bool hasMemberBinding; + nsCOMPtr<nsIRDFNode> memberValue; + hasMemberBinding = inst->mAssignments.GetAssignmentFor(mMemberVariable, + getter_AddRefs(memberValue)); + + if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) { + const char* container = "(unbound)"; + if (hasContainerBinding) + containerRes->GetValueConst(&container); + + nsAutoString member(NS_LITERAL_STRING("(unbound)")); + if (hasMemberBinding) + nsXULContentUtils::GetTextForNode(memberValue, member); + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + ("nsRDFConMemberTestNode[%p]: FilterInstantiations() container=[%s] member=[%s]", + this, container, NS_ConvertUTF16toUTF8(member).get())); + } + + if (hasContainerBinding && hasMemberBinding) { + // it's a consistency check. see if we have a assignment that is consistent + bool isconsistent = false; + + if (rdfcontainer) { + // RDF containers are easy. Just use the container API. + int32_t index; + rv = rdfcontainer->IndexOf(memberValue, &index); + if (NS_FAILED(rv)) return rv; + + if (index >= 0) + isconsistent = true; + } + + // XXXwaterson oof. if we *are* an RDF container, why do + // we still need to grovel through all the containment + // properties if the thing we're looking for wasn't there? + + if (! isconsistent) { + // Othewise, we'll need to grovel through the + // membership properties to see if we have an + // assertion that indicates membership. + nsResourceSet& containmentProps = mProcessor->ContainmentProperties(); + for (nsResourceSet::ConstIterator property = containmentProps.First(); + property != containmentProps.Last(); + ++property) { + bool hasAssertion; + rv = ds->HasAssertion(containerRes, + *property, + memberValue, + true, + &hasAssertion); + if (NS_FAILED(rv)) return rv; + + if (hasAssertion) { + // it's consistent. leave it in the set and we'll + // run it up to our parent. + isconsistent = true; + break; + } + } + } + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + (" consistency check => %s", isconsistent ? "passed" : "failed")); + + if (isconsistent) { + // Add a memory element to our set-of-support. + Element* element = + new nsRDFConMemberTestNode::Element(containerRes, + memberValue); + inst->AddSupportingElement(element); + } + else { + // it's inconsistent. remove it. + aInstantiations.Erase(inst--); + } + + // We're done, go on to the next instantiation + continue; + } + + if (hasContainerBinding && rdfcontainer) { + // We've got a container assignment, and the container is + // bound to an RDF container. Add each member as a new + // instantiation. + nsCOMPtr<nsISimpleEnumerator> elements; + rv = rdfcontainer->GetElements(getter_AddRefs(elements)); + if (NS_FAILED(rv)) return rv; + + while (1) { + bool hasmore; + rv = elements->HasMoreElements(&hasmore); + if (NS_FAILED(rv)) return rv; + + if (! hasmore) + break; + + nsCOMPtr<nsISupports> isupports; + rv = elements->GetNext(getter_AddRefs(isupports)); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr<nsIRDFNode> node = do_QueryInterface(isupports); + if (! node) + return NS_ERROR_UNEXPECTED; + + if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) { + nsAutoString member; + nsXULContentUtils::GetTextForNode(node, member); + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + (" member => %s", NS_ConvertUTF16toUTF8(member).get())); + } + + Instantiation newinst = *inst; + newinst.AddAssignment(mMemberVariable, node); + + Element* element = + new nsRDFConMemberTestNode::Element(containerRes, node); + newinst.AddSupportingElement(element); + aInstantiations.Insert(inst, newinst); + } + } + + if (hasMemberBinding) { + // Oh, this is so nasty. If we have a member assignment, then + // grovel through each one of our inbound arcs to see if + // any of them are ordinal properties (like an RDF + // container might have). If so, walk it backwards to get + // the container we're in. + nsCOMPtr<nsISimpleEnumerator> arcsin; + rv = ds->ArcLabelsIn(memberValue, getter_AddRefs(arcsin)); + if (NS_FAILED(rv)) return rv; + + while (1) { + nsCOMPtr<nsIRDFResource> property; + + { + bool hasmore; + rv = arcsin->HasMoreElements(&hasmore); + if (NS_FAILED(rv)) return rv; + + if (! hasmore) + break; + + nsCOMPtr<nsISupports> isupports; + rv = arcsin->GetNext(getter_AddRefs(isupports)); + if (NS_FAILED(rv)) return rv; + + property = do_QueryInterface(isupports); + if (! property) + return NS_ERROR_UNEXPECTED; + } + + // Ordinal properties automagically indicate container + // membership as far as we're concerned. Note that + // we're *only* concerned with ordinal properties + // here: the next block will worry about the other + // membership properties. + bool isordinal; + rv = rdfc->IsOrdinalProperty(property, &isordinal); + if (NS_FAILED(rv)) return rv; + + if (isordinal) { + // If we get here, we've found a property that + // indicates container membership leading *into* a + // member node. Find all the people that point to + // it, and call them containers. + nsCOMPtr<nsISimpleEnumerator> sources; + rv = ds->GetSources(property, memberValue, true, + getter_AddRefs(sources)); + if (NS_FAILED(rv)) return rv; + + while (1) { + bool hasmore; + rv = sources->HasMoreElements(&hasmore); + if (NS_FAILED(rv)) return rv; + + if (! hasmore) + break; + + nsCOMPtr<nsISupports> isupports; + rv = sources->GetNext(getter_AddRefs(isupports)); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr<nsIRDFResource> source = do_QueryInterface(isupports); + if (! source) + return NS_ERROR_UNEXPECTED; + + if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) { + const char* container; + source->GetValueConst(&container); + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + (" container => %s", container)); + } + + // Add a new instantiation + Instantiation newinst = *inst; + newinst.AddAssignment(mContainerVariable, source); + + Element* element = + new nsRDFConMemberTestNode::Element(source, + memberValue); + newinst.AddSupportingElement(element); + + aInstantiations.Insert(inst, newinst); + } + } + } + } + + if ((hasContainerBinding && ! hasMemberBinding) || + (! hasContainerBinding && hasMemberBinding)) { + // it's an open ended query on the container or member. go + // through our containment properties to see if anything + // applies. + nsResourceSet& containmentProps = mProcessor->ContainmentProperties(); + for (nsResourceSet::ConstIterator property = containmentProps.First(); + property != containmentProps.Last(); + ++property) { + nsCOMPtr<nsISimpleEnumerator> results; + if (hasContainerBinding) { + rv = ds->GetTargets(containerRes, *property, true, + getter_AddRefs(results)); + } + else { + rv = ds->GetSources(*property, memberValue, true, + getter_AddRefs(results)); + } + if (NS_FAILED(rv)) return rv; + + while (1) { + bool hasmore; + rv = results->HasMoreElements(&hasmore); + if (NS_FAILED(rv)) return rv; + + if (! hasmore) + break; + + nsCOMPtr<nsISupports> isupports; + rv = results->GetNext(getter_AddRefs(isupports)); + if (NS_FAILED(rv)) return rv; + + nsIAtom* variable; + nsCOMPtr<nsIRDFNode> value; + nsCOMPtr<nsIRDFResource> valueRes; + + if (hasContainerBinding) { + variable = mMemberVariable; + + value = do_QueryInterface(isupports); + NS_ASSERTION(value != nullptr, "member is not an nsIRDFNode"); + if (! value) continue; + + if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) { + nsAutoString s; + nsXULContentUtils::GetTextForNode(value, s); + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + (" member => %s", NS_ConvertUTF16toUTF8(s).get())); + } + } + else { + variable = mContainerVariable; + + valueRes = do_QueryInterface(isupports); + NS_ASSERTION(valueRes != nullptr, "container is not an nsIRDFResource"); + if (! valueRes) continue; + + value = valueRes; + + if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) { + const char* s; + valueRes->GetValueConst(&s); + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + (" container => %s", s)); + } + } + + // Copy the original instantiation, and add it to the + // instantiation set with the new assignment that we've + // introduced. Ownership will be transferred to the + Instantiation newinst = *inst; + newinst.AddAssignment(variable, value); + + Element* element; + if (hasContainerBinding) { + element = + new nsRDFConMemberTestNode::Element(containerRes, value); + } + else { + element = + new nsRDFConMemberTestNode::Element(valueRes, memberValue); + } + + if (! element) + return NS_ERROR_OUT_OF_MEMORY; + + newinst.AddSupportingElement(element); + + aInstantiations.Insert(inst, newinst); + } + } + } + + if (! hasContainerBinding && ! hasMemberBinding) { + // Neither container nor member assignment! + if (!aCantHandleYet) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_MEMBER_UNBOUND); + return NS_ERROR_UNEXPECTED; + } + + *aCantHandleYet = true; + return NS_OK; + } + + // finally, remove the "under specified" instantiation. + aInstantiations.Erase(inst--); + } + + return NS_OK; +} + +bool +nsRDFConMemberTestNode::CanPropagate(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget, + Instantiation& aInitialBindings) const +{ + nsresult rv; + + bool canpropagate = false; + + nsCOMPtr<nsIRDFContainerUtils> rdfc = + do_GetService("@mozilla.org/rdf/container-utils;1"); + + if (! rdfc) + return false; + + // We can certainly propagate ordinal properties + rv = rdfc->IsOrdinalProperty(aProperty, &canpropagate); + if (NS_FAILED(rv)) return false; + + if (! canpropagate) { + canpropagate = mProcessor->ContainmentProperties().Contains(aProperty); + } + + if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) { + const char* source; + aSource->GetValueConst(&source); + + const char* property; + aProperty->GetValueConst(&property); + + nsAutoString target; + nsXULContentUtils::GetTextForNode(aTarget, target); + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + ("nsRDFConMemberTestNode[%p]: CanPropagate([%s]==[%s]=>[%s]) => %s", + this, source, property, NS_ConvertUTF16toUTF8(target).get(), + canpropagate ? "true" : "false")); + } + + if (canpropagate) { + aInitialBindings.AddAssignment(mContainerVariable, aSource); + aInitialBindings.AddAssignment(mMemberVariable, aTarget); + return true; + } + + return false; +} + +void +nsRDFConMemberTestNode::Retract(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget) const +{ + bool canretract = false; + + nsCOMPtr<nsIRDFContainerUtils> rdfc = + do_GetService("@mozilla.org/rdf/container-utils;1"); + + if (! rdfc) + return; + + // We can certainly retract ordinal properties + nsresult rv; + rv = rdfc->IsOrdinalProperty(aProperty, &canretract); + if (NS_FAILED(rv)) return; + + if (! canretract) { + canretract = mProcessor->ContainmentProperties().Contains(aProperty); + } + + if (canretract) { + mProcessor->RetractElement(Element(aSource, aTarget)); + } +} diff --git a/dom/xul/templates/nsRDFConMemberTestNode.h b/dom/xul/templates/nsRDFConMemberTestNode.h new file mode 100644 index 000000000..4db2f8983 --- /dev/null +++ b/dom/xul/templates/nsRDFConMemberTestNode.h @@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsRDFConMemberTestNode_h__ +#define nsRDFConMemberTestNode_h__ + +#include "mozilla/Attributes.h" +#include "nscore.h" +#include "nsRDFTestNode.h" +#include "nsIRDFDataSource.h" +#include "nsXULTemplateQueryProcessorRDF.h" + +/** + * Rule network node that test if a resource is a member of an RDF + * container, or is ``contained'' by another resource that refers to + * it using a ``containment'' attribute. + */ +class nsRDFConMemberTestNode : public nsRDFTestNode +{ +public: + nsRDFConMemberTestNode(TestNode* aParent, + nsXULTemplateQueryProcessorRDF* aProcessor, + nsIAtom* aContainerVariable, + nsIAtom* aMemberVariable); + + virtual nsresult FilterInstantiations(InstantiationSet& aInstantiations, + bool* aCantHandleYet) const override; + + virtual bool + CanPropagate(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget, + Instantiation& aInitialBindings) const override; + + virtual void + Retract(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget) const override; + + class Element : public MemoryElement { + public: + Element(nsIRDFResource* aContainer, + nsIRDFNode* aMember) + : mContainer(aContainer), + mMember(aMember) { + MOZ_COUNT_CTOR(nsRDFConMemberTestNode::Element); } + + virtual ~Element() { MOZ_COUNT_DTOR(nsRDFConMemberTestNode::Element); } + + virtual const char* Type() const override { + return "nsRDFConMemberTestNode::Element"; } + + virtual PLHashNumber Hash() const override { + return PLHashNumber(NS_PTR_TO_INT32(mContainer.get())) ^ + (PLHashNumber(NS_PTR_TO_INT32(mMember.get())) >> 12); } + + virtual bool Equals(const MemoryElement& aElement) const override { + if (aElement.Type() == Type()) { + const Element& element = static_cast<const Element&>(aElement); + return mContainer == element.mContainer && mMember == element.mMember; + } + return false; } + + protected: + nsCOMPtr<nsIRDFResource> mContainer; + nsCOMPtr<nsIRDFNode> mMember; + }; + +protected: + nsXULTemplateQueryProcessorRDF* mProcessor; + nsCOMPtr<nsIAtom> mContainerVariable; + nsCOMPtr<nsIAtom> mMemberVariable; +}; + +#endif // nsRDFConMemberTestNode_h__ diff --git a/dom/xul/templates/nsRDFPropertyTestNode.cpp b/dom/xul/templates/nsRDFPropertyTestNode.cpp new file mode 100644 index 000000000..2fa08f2b8 --- /dev/null +++ b/dom/xul/templates/nsRDFPropertyTestNode.cpp @@ -0,0 +1,362 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsRDFPropertyTestNode.h" +#include "nsString.h" +#include "nsXULContentUtils.h" + +#include "mozilla/Logging.h" + +using mozilla::LogLevel; + +extern mozilla::LazyLogModule gXULTemplateLog; +#include "nsIRDFLiteral.h" + +nsRDFPropertyTestNode::nsRDFPropertyTestNode(TestNode* aParent, + nsXULTemplateQueryProcessorRDF* aProcessor, + nsIAtom* aSourceVariable, + nsIRDFResource* aProperty, + nsIAtom* aTargetVariable) + : nsRDFTestNode(aParent), + mProcessor(aProcessor), + mSourceVariable(aSourceVariable), + mSource(nullptr), + mProperty(aProperty), + mTargetVariable(aTargetVariable), + mTarget(nullptr) +{ + if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) { + const char* prop = "(null)"; + if (aProperty) + aProperty->GetValueConst(&prop); + + nsAutoString svar(NS_LITERAL_STRING("(none)")); + if (mSourceVariable) + mSourceVariable->ToString(svar); + + nsAutoString tvar(NS_LITERAL_STRING("(none)")); + if (mTargetVariable) + mTargetVariable->ToString(tvar); + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + ("nsRDFPropertyTestNode[%p]: parent=%p source=%s property=%s target=%s", + this, aParent, NS_ConvertUTF16toUTF8(svar).get(), prop, NS_ConvertUTF16toUTF8(tvar).get())); + } +} + + +nsRDFPropertyTestNode::nsRDFPropertyTestNode(TestNode* aParent, + nsXULTemplateQueryProcessorRDF* aProcessor, + nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIAtom* aTargetVariable) + : nsRDFTestNode(aParent), + mProcessor(aProcessor), + mSourceVariable(nullptr), + mSource(aSource), + mProperty(aProperty), + mTargetVariable(aTargetVariable), + mTarget(nullptr) +{ + if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) { + const char* source = "(null)"; + if (aSource) + aSource->GetValueConst(&source); + + const char* prop = "(null)"; + if (aProperty) + aProperty->GetValueConst(&prop); + + nsAutoString tvar(NS_LITERAL_STRING("(none)")); + if (mTargetVariable) + mTargetVariable->ToString(tvar); + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + ("nsRDFPropertyTestNode[%p]: parent=%p source=%s property=%s target=%s", + this, aParent, source, prop, NS_ConvertUTF16toUTF8(tvar).get())); + } +} + + +nsRDFPropertyTestNode::nsRDFPropertyTestNode(TestNode* aParent, + nsXULTemplateQueryProcessorRDF* aProcessor, + nsIAtom* aSourceVariable, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget) + : nsRDFTestNode(aParent), + mProcessor(aProcessor), + mSourceVariable(aSourceVariable), + mSource(nullptr), + mProperty(aProperty), + mTargetVariable(nullptr), + mTarget(aTarget) +{ + if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) { + nsAutoString svar(NS_LITERAL_STRING("(none)")); + if (mSourceVariable) + mSourceVariable->ToString(svar); + + const char* prop = "(null)"; + if (aProperty) + aProperty->GetValueConst(&prop); + + nsAutoString target; + nsXULContentUtils::GetTextForNode(aTarget, target); + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + ("nsRDFPropertyTestNode[%p]: parent=%p source=%s property=%s target=%s", + this, aParent, NS_ConvertUTF16toUTF8(svar).get(), prop, NS_ConvertUTF16toUTF8(target).get())); + } +} + + +nsresult +nsRDFPropertyTestNode::FilterInstantiations(InstantiationSet& aInstantiations, + bool* aCantHandleYet) const +{ + nsresult rv; + + if (aCantHandleYet) + *aCantHandleYet = false; + + nsIRDFDataSource* ds = mProcessor->GetDataSource(); + + InstantiationSet::Iterator last = aInstantiations.Last(); + for (InstantiationSet::Iterator inst = aInstantiations.First(); inst != last; ++inst) { + bool hasSourceBinding; + nsCOMPtr<nsIRDFResource> sourceRes; + + if (mSource) { + hasSourceBinding = true; + sourceRes = mSource; + } + else { + nsCOMPtr<nsIRDFNode> sourceValue; + hasSourceBinding = inst->mAssignments.GetAssignmentFor(mSourceVariable, + getter_AddRefs(sourceValue)); + sourceRes = do_QueryInterface(sourceValue); + } + + bool hasTargetBinding; + nsCOMPtr<nsIRDFNode> targetValue; + + if (mTarget) { + hasTargetBinding = true; + targetValue = mTarget; + } + else { + hasTargetBinding = inst->mAssignments.GetAssignmentFor(mTargetVariable, + getter_AddRefs(targetValue)); + } + + if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) { + const char* source = "(unbound)"; + if (hasSourceBinding) + sourceRes->GetValueConst(&source); + + nsAutoString target(NS_LITERAL_STRING("(unbound)")); + if (hasTargetBinding) + nsXULContentUtils::GetTextForNode(targetValue, target); + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + ("nsRDFPropertyTestNode[%p]: FilterInstantiations() source=[%s] target=[%s]", + this, source, NS_ConvertUTF16toUTF8(target).get())); + } + + if (hasSourceBinding && hasTargetBinding) { + // it's a consistency check. see if we have a assignment that is consistent + bool hasAssertion; + rv = ds->HasAssertion(sourceRes, mProperty, targetValue, + true, &hasAssertion); + if (NS_FAILED(rv)) return rv; + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + (" consistency check => %s", hasAssertion ? "passed" : "failed")); + + if (hasAssertion) { + // it's consistent. + Element* element = + new nsRDFPropertyTestNode::Element(sourceRes, mProperty, + targetValue); + inst->AddSupportingElement(element); + } + else { + // it's inconsistent. remove it. + aInstantiations.Erase(inst--); + } + } + else if ((hasSourceBinding && ! hasTargetBinding) || + (! hasSourceBinding && hasTargetBinding)) { + // it's an open ended query on the source or + // target. figure out what matches and add as a + // cross-product. + nsCOMPtr<nsISimpleEnumerator> results; + if (hasSourceBinding) { + rv = ds->GetTargets(sourceRes, + mProperty, + true, + getter_AddRefs(results)); + } + else { + rv = ds->GetSources(mProperty, + targetValue, + true, + getter_AddRefs(results)); + if (NS_FAILED(rv)) return rv; + } + + while (1) { + bool hasMore; + rv = results->HasMoreElements(&hasMore); + if (NS_FAILED(rv)) return rv; + + if (! hasMore) + break; + + nsCOMPtr<nsISupports> isupports; + rv = results->GetNext(getter_AddRefs(isupports)); + if (NS_FAILED(rv)) return rv; + + nsIAtom* variable; + nsCOMPtr<nsIRDFNode> value; + + if (hasSourceBinding) { + variable = mTargetVariable; + + value = do_QueryInterface(isupports); + NS_ASSERTION(value != nullptr, "target is not an nsIRDFNode"); + + if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) { + nsAutoString s(NS_LITERAL_STRING("(none found)")); + if (value) + nsXULContentUtils::GetTextForNode(value, s); + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + (" target => %s", NS_ConvertUTF16toUTF8(s).get())); + } + + if (! value) continue; + + targetValue = value; + } + else { + variable = mSourceVariable; + + nsCOMPtr<nsIRDFResource> source = do_QueryInterface(isupports); + NS_ASSERTION(source != nullptr, "source is not an nsIRDFResource"); + + if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) { + const char* s = "(none found)"; + if (source) + source->GetValueConst(&s); + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + (" source => %s", s)); + } + + if (! source) continue; + + value = sourceRes = source; + } + + // Copy the original instantiation, and add it to the + // instantiation set with the new assignment that we've + // introduced. Ownership will be transferred to the + Instantiation newinst = *inst; + newinst.AddAssignment(variable, value); + + Element* element = + new nsRDFPropertyTestNode::Element(sourceRes, mProperty, + targetValue); + newinst.AddSupportingElement(element); + + aInstantiations.Insert(inst, newinst); + } + + // finally, remove the "under specified" instantiation. + aInstantiations.Erase(inst--); + } + else { + if (!aCantHandleYet) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_TRIPLE_UNBOUND); + // Neither source nor target assignment! + return NS_ERROR_UNEXPECTED; + } + + *aCantHandleYet = true; + return NS_OK; + } + } + + return NS_OK; +} + +bool +nsRDFPropertyTestNode::CanPropagate(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget, + Instantiation& aInitialBindings) const +{ + bool result; + + if ((mProperty.get() != aProperty) || + (mSource && mSource.get() != aSource) || + (mTarget && mTarget.get() != aTarget)) { + result = false; + } + else { + if (mSourceVariable) + aInitialBindings.AddAssignment(mSourceVariable, aSource); + + if (mTargetVariable) + aInitialBindings.AddAssignment(mTargetVariable, aTarget); + + result = true; + } + + if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) { + const char* source; + aSource->GetValueConst(&source); + + const char* property; + aProperty->GetValueConst(&property); + + nsAutoString target; + nsXULContentUtils::GetTextForNode(aTarget, target); + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + ("nsRDFPropertyTestNode[%p]: CanPropagate([%s]==[%s]=>[%s]) => %s", + this, source, property, NS_ConvertUTF16toUTF8(target).get(), + result ? "true" : "false")); + } + + return result; +} + +void +nsRDFPropertyTestNode::Retract(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget) const +{ + if (aProperty == mProperty.get()) { + if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) { + const char* source; + aSource->GetValueConst(&source); + + const char* property; + aProperty->GetValueConst(&property); + + nsAutoString target; + nsXULContentUtils::GetTextForNode(aTarget, target); + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + ("nsRDFPropertyTestNode[%p]: Retract([%s]==[%s]=>[%s])", + this, source, property, NS_ConvertUTF16toUTF8(target).get())); + } + + mProcessor->RetractElement(Element(aSource, aProperty, aTarget)); + } +} + diff --git a/dom/xul/templates/nsRDFPropertyTestNode.h b/dom/xul/templates/nsRDFPropertyTestNode.h new file mode 100644 index 000000000..673552a26 --- /dev/null +++ b/dom/xul/templates/nsRDFPropertyTestNode.h @@ -0,0 +1,104 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsRDFPropertyTestNode_h__ +#define nsRDFPropertyTestNode_h__ + +#include "mozilla/Attributes.h" +#include "nscore.h" +#include "nsRDFTestNode.h" +#include "nsIRDFDataSource.h" +#include "nsIRDFResource.h" +#include "nsXULTemplateQueryProcessorRDF.h" + +class nsRDFPropertyTestNode : public nsRDFTestNode +{ +public: + /** + * Both source and target unbound (?source ^property ?target) + */ + nsRDFPropertyTestNode(TestNode* aParent, + nsXULTemplateQueryProcessorRDF* aProcessor, + nsIAtom* aSourceVariable, + nsIRDFResource* aProperty, + nsIAtom* aTargetVariable); + + /** + * Source bound, target unbound (source ^property ?target) + */ + nsRDFPropertyTestNode(TestNode* aParent, + nsXULTemplateQueryProcessorRDF* aProcessor, + nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIAtom* aTargetVariable); + + /** + * Source unbound, target bound (?source ^property target) + */ + nsRDFPropertyTestNode(TestNode* aParent, + nsXULTemplateQueryProcessorRDF* aProcessor, + nsIAtom* aSourceVariable, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget); + + virtual nsresult FilterInstantiations(InstantiationSet& aInstantiations, + bool* aCantHandleYet) const override; + + virtual bool + CanPropagate(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget, + Instantiation& aInitialBindings) const override; + + virtual void + Retract(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget) const override; + + + class Element : public MemoryElement { + public: + Element(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget) + : mSource(aSource), + mProperty(aProperty), + mTarget(aTarget) { + MOZ_COUNT_CTOR(nsRDFPropertyTestNode::Element); } + + virtual ~Element() { MOZ_COUNT_DTOR(nsRDFPropertyTestNode::Element); } + + virtual const char* Type() const override { + return "nsRDFPropertyTestNode::Element"; } + + virtual PLHashNumber Hash() const override { + return mozilla::HashGeneric(mSource.get(), mProperty.get(), mTarget.get()); + } + + virtual bool Equals(const MemoryElement& aElement) const override { + if (aElement.Type() == Type()) { + const Element& element = static_cast<const Element&>(aElement); + return mSource == element.mSource + && mProperty == element.mProperty + && mTarget == element.mTarget; + } + return false; } + + protected: + nsCOMPtr<nsIRDFResource> mSource; + nsCOMPtr<nsIRDFResource> mProperty; + nsCOMPtr<nsIRDFNode> mTarget; + }; + +protected: + nsXULTemplateQueryProcessorRDF* mProcessor; + nsCOMPtr<nsIAtom> mSourceVariable; + nsCOMPtr<nsIRDFResource> mSource; + nsCOMPtr<nsIRDFResource> mProperty; + nsCOMPtr<nsIAtom> mTargetVariable; + nsCOMPtr<nsIRDFNode> mTarget; +}; + +#endif // nsRDFPropertyTestNode_h__ diff --git a/dom/xul/templates/nsRDFQuery.cpp b/dom/xul/templates/nsRDFQuery.cpp new file mode 100644 index 000000000..b4eb706ed --- /dev/null +++ b/dom/xul/templates/nsRDFQuery.cpp @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nscore.h" +#include "nsCOMPtr.h" + +#include "nsXULTemplateQueryProcessorRDF.h" +#include "nsRDFQuery.h" + +NS_IMPL_CYCLE_COLLECTION(nsRDFQuery, mQueryNode) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsRDFQuery) + NS_INTERFACE_MAP_ENTRY(nsITemplateRDFQuery) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsRDFQuery) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsRDFQuery) + +void +nsRDFQuery::Finish() +{ + // the template builder is going away and the query processor likely as + // well. Clear the reference to avoid calling it. + mProcessor = nullptr; + mCachedResults = nullptr; +} + +nsresult +nsRDFQuery::SetCachedResults(nsXULTemplateQueryProcessorRDF* aProcessor, + const InstantiationSet& aInstantiations) +{ + mCachedResults = new nsXULTemplateResultSetRDF(aProcessor, this, &aInstantiations); + return NS_OK; +} + + +void +nsRDFQuery::UseCachedResults(nsISimpleEnumerator** aResults) +{ + *aResults = mCachedResults; + NS_IF_ADDREF(*aResults); + + mCachedResults = nullptr; +} diff --git a/dom/xul/templates/nsRDFQuery.h b/dom/xul/templates/nsRDFQuery.h new file mode 100644 index 000000000..572cce4d3 --- /dev/null +++ b/dom/xul/templates/nsRDFQuery.h @@ -0,0 +1,130 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsRDFQuery_h__ +#define nsRDFQuery_h__ + +#include "nsISimpleEnumerator.h" +#include "nsCycleCollectionParticipant.h" +#include "mozilla/Attributes.h" + +#define NS_ITEMPLATERDFQUERY_IID \ + {0x8929ff60, 0x1c9c, 0x4d87, \ + { 0xac, 0x02, 0x09, 0x14, 0x15, 0x3b, 0x48, 0xc4 }} + +class nsXULTemplateQueryProcessorRDF; + +/** + * A compiled query in the RDF query processor. This interface should not be + * used directly outside of the RDF query processor. + */ +class nsITemplateRDFQuery : public nsISupports +{ +public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_ITEMPLATERDFQUERY_IID) + + // return the processor the query was created from + virtual nsXULTemplateQueryProcessorRDF* Processor() = 0; // not addrefed + + // return the member variable for the query + virtual nsIAtom* GetMemberVariable() = 0; // not addrefed + + // return the <query> node the query was compiled from + virtual void GetQueryNode(nsIDOMNode** aQueryNode) = 0; + + // remove any results that are cached by the query + virtual void ClearCachedResults() = 0; +}; + +class nsRDFQuery final : public nsITemplateRDFQuery +{ + ~nsRDFQuery() { Finish(); } + +public: + + explicit nsRDFQuery(nsXULTemplateQueryProcessorRDF* aProcessor) + : mProcessor(aProcessor), + mSimple(false), + mRoot(nullptr), + mCachedResults(nullptr) + { } + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_CLASS(nsRDFQuery) + + /** + * Retrieve the root node in the rule network + * @return the root node in the rule network + */ + TestNode* GetRoot() { return mRoot; } + + void SetRoot(TestNode* aRoot) { mRoot = aRoot; } + + void GetQueryNode(nsIDOMNode** aQueryNode) override + { + *aQueryNode = mQueryNode; + NS_IF_ADDREF(*aQueryNode); + } + + void SetQueryNode(nsIDOMNode* aQueryNode) + { + mQueryNode = aQueryNode; + } + + // an optimization is used when several queries all use the simple query + // syntax. Since simple queries can only generate one possible set of + // results, they only need to be calculated once and reused for every + // simple query. The results may be cached in the query for this purpose. + // If successful, this method takes ownership of aInstantiations. + nsresult SetCachedResults(nsXULTemplateQueryProcessorRDF* aProcessor, + const InstantiationSet& aInstantiations); + + // grab the cached results, if any, causing the caller to take ownership + // of them. This also has the effect of setting the cached results in this + // nsRDFQuery to null. + void UseCachedResults(nsISimpleEnumerator** aResults); + + // clear the cached results + void ClearCachedResults() override + { + mCachedResults = nullptr; + } + + nsXULTemplateQueryProcessorRDF* Processor() override { return mProcessor; } + + nsIAtom* GetMemberVariable() override { return mMemberVariable; } + + bool IsSimple() { return mSimple; } + + void SetSimple() { mSimple = true; } + + // the reference and member variables for the query + nsCOMPtr<nsIAtom> mRefVariable; + nsCOMPtr<nsIAtom> mMemberVariable; + +protected: + + nsXULTemplateQueryProcessorRDF* mProcessor; + + // true if the query is a simple rule (one with a default query) + bool mSimple; + + /** + * The root node in the network for this query + */ + TestNode *mRoot; + + // the <query> node + nsCOMPtr<nsIDOMNode> mQueryNode; + + // used for simple rules since their results are all determined in one step + nsCOMPtr<nsISimpleEnumerator> mCachedResults; + + void Finish(); +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsITemplateRDFQuery, NS_ITEMPLATERDFQUERY_IID) + +#endif // nsRDFQuery_h__ diff --git a/dom/xul/templates/nsRDFTestNode.h b/dom/xul/templates/nsRDFTestNode.h new file mode 100644 index 000000000..0e450fadf --- /dev/null +++ b/dom/xul/templates/nsRDFTestNode.h @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsRDFTestNode_h__ +#define nsRDFTestNode_h__ + +#include "nsRuleNetwork.h" + +class nsIRDFResource; +class nsIRDFNode; + +/** + * An abstract base class for all of the RDF-related tests. This interface + * allows us to iterate over all of the RDF tests to find the one in the + * network that is apropos for a newly-added assertion. + */ +class nsRDFTestNode : public TestNode +{ +public: + explicit nsRDFTestNode(TestNode* aParent) + : TestNode(aParent) {} + + /** + * Determine whether the node can propagate an assertion + * with the specified source, property, and target. If the + * assertion can be propagated, aInitialBindings will be + * initialized with appropriate variable-to-value assignments + * to allow the rule network to start a constrain and propagate + * search from this node in the network. + * + * @return true if the node can propagate the specified + * assertion. + */ + virtual bool CanPropagate(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget, + Instantiation& aInitialBindings) const = 0; + + /** + * + */ + virtual void Retract(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget) const = 0; +}; + +#endif // nsRDFTestNode_h__ diff --git a/dom/xul/templates/nsResourceSet.cpp b/dom/xul/templates/nsResourceSet.cpp new file mode 100644 index 000000000..64f4aad19 --- /dev/null +++ b/dom/xul/templates/nsResourceSet.cpp @@ -0,0 +1,105 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsResourceSet.h" + +nsResourceSet::nsResourceSet(const nsResourceSet& aResourceSet) + : mResources(nullptr), + mCount(0), + mCapacity(0) +{ + ConstIterator last = aResourceSet.Last(); + for (ConstIterator resource = aResourceSet.First(); resource != last; ++resource) + Add(*resource); +} + + +nsResourceSet& +nsResourceSet::operator=(const nsResourceSet& aResourceSet) +{ + Clear(); + ConstIterator last = aResourceSet.Last(); + for (ConstIterator resource = aResourceSet.First(); resource != last; ++resource) + Add(*resource); + return *this; +} + +nsResourceSet::~nsResourceSet() +{ + MOZ_COUNT_DTOR(nsResourceSet); + Clear(); + delete[] mResources; +} + +nsresult +nsResourceSet::Clear() +{ + while (--mCount >= 0) { + NS_RELEASE(mResources[mCount]); + } + mCount = 0; + return NS_OK; +} + +nsresult +nsResourceSet::Add(nsIRDFResource* aResource) +{ + NS_PRECONDITION(aResource != nullptr, "null ptr"); + if (! aResource) + return NS_ERROR_NULL_POINTER; + + if (Contains(aResource)) + return NS_OK; + + if (mCount >= mCapacity) { + int32_t capacity = mCapacity + 4; + nsIRDFResource** resources = new nsIRDFResource*[capacity]; + for (int32_t i = mCount - 1; i >= 0; --i) + resources[i] = mResources[i]; + + delete[] mResources; + + mResources = resources; + mCapacity = capacity; + } + + mResources[mCount++] = aResource; + NS_ADDREF(aResource); + return NS_OK; +} + +void +nsResourceSet::Remove(nsIRDFResource* aProperty) +{ + bool found = false; + + nsIRDFResource** res = mResources; + nsIRDFResource** limit = mResources + mCount; + while (res < limit) { + if (found) { + *(res - 1) = *res; + } + else if (*res == aProperty) { + NS_RELEASE(*res); + found = true; + } + ++res; + } + + if (found) + --mCount; +} + +bool +nsResourceSet::Contains(nsIRDFResource* aResource) const +{ + for (int32_t i = mCount - 1; i >= 0; --i) { + if (mResources[i] == aResource) + return true; + } + + return false; +} + diff --git a/dom/xul/templates/nsResourceSet.h b/dom/xul/templates/nsResourceSet.h new file mode 100644 index 000000000..1f50538a8 --- /dev/null +++ b/dom/xul/templates/nsResourceSet.h @@ -0,0 +1,82 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef nsResourceSet_h__ +#define nsResourceSet_h__ + +#include "nsIRDFResource.h" + +class nsResourceSet +{ +public: + nsResourceSet() + : mResources(nullptr), + mCount(0), + mCapacity(0) { + MOZ_COUNT_CTOR(nsResourceSet); } + + nsResourceSet(const nsResourceSet& aResourceSet); + + nsResourceSet& operator=(const nsResourceSet& aResourceSet); + + ~nsResourceSet(); + + nsresult Clear(); + nsresult Add(nsIRDFResource* aProperty); + void Remove(nsIRDFResource* aProperty); + + bool Contains(nsIRDFResource* aProperty) const; + +protected: + nsIRDFResource** mResources; + int32_t mCount; + int32_t mCapacity; + +public: + class ConstIterator { + protected: + nsIRDFResource** mCurrent; + + public: + ConstIterator() : mCurrent(nullptr) {} + + ConstIterator(const ConstIterator& aConstIterator) + : mCurrent(aConstIterator.mCurrent) {} + + ConstIterator& operator=(const ConstIterator& aConstIterator) { + mCurrent = aConstIterator.mCurrent; + return *this; } + + ConstIterator& operator++() { + ++mCurrent; + return *this; } + + ConstIterator operator++(int) { + ConstIterator result(*this); + ++mCurrent; + return result; } + + /*const*/ nsIRDFResource* operator*() const { + return *mCurrent; } + + /*const*/ nsIRDFResource* operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN { + return *mCurrent; } + + bool operator==(const ConstIterator& aConstIterator) const { + return mCurrent == aConstIterator.mCurrent; } + + bool operator!=(const ConstIterator& aConstIterator) const { + return mCurrent != aConstIterator.mCurrent; } + + protected: + explicit ConstIterator(nsIRDFResource** aProperty) : mCurrent(aProperty) {} + friend class nsResourceSet; + }; + + ConstIterator First() const { return ConstIterator(mResources); } + ConstIterator Last() const { return ConstIterator(mResources + mCount); } +}; + +#endif // nsResourceSet_h__ + diff --git a/dom/xul/templates/nsRuleNetwork.cpp b/dom/xul/templates/nsRuleNetwork.cpp new file mode 100644 index 000000000..c2cee5bbd --- /dev/null +++ b/dom/xul/templates/nsRuleNetwork.cpp @@ -0,0 +1,428 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + + Implementations for the rule network classes. + + To Do. + + - Constrain() & Propagate() still feel like they are poorly named. + - As do Instantiation and InstantiationSet. + - Make InstantiationSet share and do copy-on-write. + - Make things iterative, instead of recursive. + + */ + +#include "nscore.h" +#include "nsCOMPtr.h" +#include "plhash.h" + +#include "mozilla/Logging.h" + +#include "nsString.h" +#include "nsUnicharUtils.h" +#include "nsXULContentUtils.h" + +#include "nsRuleNetwork.h" +#include "nsXULTemplateResultSetRDF.h" +#include "nsRDFConMemberTestNode.h" +#include "nsRDFPropertyTestNode.h" + +using namespace mozilla; +extern LazyLogModule gXULTemplateLog; + +//---------------------------------------------------------------------- +// +// nsRuleNetwork +// + +nsresult +MemoryElementSet::Add(MemoryElement* aElement) +{ + for (ConstIterator element = First(); element != Last(); ++element) { + if (*element == *aElement) { + // We've already got this element covered. Since Add() + // assumes ownership, and we aren't going to need this, + // just nuke it. + delete aElement; + return NS_OK; + } + } + + List* list = new List; + list->mElement = aElement; + list->mRefCnt = 1; + list->mNext = mElements; + + mElements = list; + + return NS_OK; +} + + +//---------------------------------------------------------------------- + +nsresult +nsAssignmentSet::Add(const nsAssignment& aAssignment) +{ + NS_PRECONDITION(! HasAssignmentFor(aAssignment.mVariable), "variable already bound"); + + // XXXndeakin should this just silently fail? + if (HasAssignmentFor(aAssignment.mVariable)) + return NS_ERROR_UNEXPECTED; + + List* list = new List(aAssignment); + list->mRefCnt = 1; + list->mNext = mAssignments; + + mAssignments = list; + + return NS_OK; +} + +int32_t +nsAssignmentSet::Count() const +{ + int32_t count = 0; + for (ConstIterator assignment = First(); assignment != Last(); ++assignment) + ++count; + + return count; +} + +bool +nsAssignmentSet::HasAssignment(nsIAtom* aVariable, nsIRDFNode* aValue) const +{ + for (ConstIterator assignment = First(); assignment != Last(); ++assignment) { + if (assignment->mVariable == aVariable && assignment->mValue == aValue) + return true; + } + + return false; +} + +bool +nsAssignmentSet::HasAssignmentFor(nsIAtom* aVariable) const +{ + for (ConstIterator assignment = First(); assignment != Last(); ++assignment) { + if (assignment->mVariable == aVariable) + return true; + } + + return false; +} + +bool +nsAssignmentSet::GetAssignmentFor(nsIAtom* aVariable, nsIRDFNode** aValue) const +{ + for (ConstIterator assignment = First(); assignment != Last(); ++assignment) { + if (assignment->mVariable == aVariable) { + *aValue = assignment->mValue; + NS_IF_ADDREF(*aValue); + return true; + } + } + + *aValue = nullptr; + return false; +} + +bool +nsAssignmentSet::Equals(const nsAssignmentSet& aSet) const +{ + if (aSet.mAssignments == mAssignments) + return true; + + // If they have a different number of assignments, then they're different. + if (Count() != aSet.Count()) + return false; + + // XXX O(n^2)! Ugh! + nsCOMPtr<nsIRDFNode> value; + for (ConstIterator assignment = First(); assignment != Last(); ++assignment) { + if (! aSet.GetAssignmentFor(assignment->mVariable, getter_AddRefs(value))) + return false; + + if (assignment->mValue != value) + return false; + } + + return true; +} + +//---------------------------------------------------------------------- + +PLHashNumber +Instantiation::Hash(const void* aKey) +{ + const Instantiation* inst = static_cast<const Instantiation*>(aKey); + + PLHashNumber result = 0; + + nsAssignmentSet::ConstIterator last = inst->mAssignments.Last(); + for (nsAssignmentSet::ConstIterator assignment = inst->mAssignments.First(); + assignment != last; ++assignment) + result ^= assignment->Hash(); + + return result; +} + + +int +Instantiation::Compare(const void* aLeft, const void* aRight) +{ + const Instantiation* left = static_cast<const Instantiation*>(aLeft); + const Instantiation* right = static_cast<const Instantiation*>(aRight); + + return *left == *right; +} + + +//---------------------------------------------------------------------- +// +// InstantiationSet +// + +InstantiationSet::InstantiationSet() +{ + mHead.mPrev = mHead.mNext = &mHead; + MOZ_COUNT_CTOR(InstantiationSet); +} + + +InstantiationSet::InstantiationSet(const InstantiationSet& aInstantiationSet) +{ + mHead.mPrev = mHead.mNext = &mHead; + + // XXX replace with copy-on-write foo + ConstIterator last = aInstantiationSet.Last(); + for (ConstIterator inst = aInstantiationSet.First(); inst != last; ++inst) + Append(*inst); + + MOZ_COUNT_CTOR(InstantiationSet); +} + +InstantiationSet& +InstantiationSet::operator=(const InstantiationSet& aInstantiationSet) +{ + // XXX replace with copy-on-write foo + Clear(); + + ConstIterator last = aInstantiationSet.Last(); + for (ConstIterator inst = aInstantiationSet.First(); inst != last; ++inst) + Append(*inst); + + return *this; +} + + +void +InstantiationSet::Clear() +{ + Iterator inst = First(); + while (inst != Last()) + Erase(inst++); +} + + +InstantiationSet::Iterator +InstantiationSet::Insert(Iterator aIterator, const Instantiation& aInstantiation) +{ + List* newelement = new List(); + if (newelement) { + newelement->mInstantiation = aInstantiation; + + aIterator.mCurrent->mPrev->mNext = newelement; + + newelement->mNext = aIterator.mCurrent; + newelement->mPrev = aIterator.mCurrent->mPrev; + + aIterator.mCurrent->mPrev = newelement; + } + return aIterator; +} + +InstantiationSet::Iterator +InstantiationSet::Erase(Iterator aIterator) +{ + Iterator result = aIterator; + ++result; + aIterator.mCurrent->mNext->mPrev = aIterator.mCurrent->mPrev; + aIterator.mCurrent->mPrev->mNext = aIterator.mCurrent->mNext; + delete aIterator.mCurrent; + return result; +} + + +bool +InstantiationSet::HasAssignmentFor(nsIAtom* aVariable) const +{ + return !Empty() ? First()->mAssignments.HasAssignmentFor(aVariable) : false; +} + +//---------------------------------------------------------------------- +// +// ReteNode +// +// The basic node in the network. +// + +//---------------------------------------------------------------------- +// +// TestNode +// +// to do: +// - FilterInstantiations() is poorly named +// + + +TestNode::TestNode(TestNode* aParent) + : mParent(aParent) +{ +} + +nsresult +TestNode::Propagate(InstantiationSet& aInstantiations, + bool aIsUpdate, bool& aTakenInstantiations) +{ + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + ("TestNode[%p]: Propagate() begin", this)); + + aTakenInstantiations = false; + + nsresult rv = FilterInstantiations(aInstantiations, nullptr); + if (NS_FAILED(rv)) + return rv; + + // if there is more than one child, each will need to be supplied with the + // original set of instantiations from this node, so create a copy in this + // case. If there is only one child, optimize and just pass the + // instantiations along to the child without copying + bool shouldCopy = (mKids.Count() > 1); + + // See the header file for details about how instantiation ownership works. + if (! aInstantiations.Empty()) { + ReteNodeSet::Iterator last = mKids.Last(); + for (ReteNodeSet::Iterator kid = mKids.First(); kid != last; ++kid) { + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + ("TestNode[%p]: Propagate() passing to child %p", this, kid.operator->())); + + // create a copy of the instantiations + if (shouldCopy) { + bool owned = false; + InstantiationSet* instantiations = + new InstantiationSet(aInstantiations); + rv = kid->Propagate(*instantiations, aIsUpdate, owned); + if (!owned) + delete instantiations; + if (NS_FAILED(rv)) + return rv; + } + else { + rv = kid->Propagate(aInstantiations, aIsUpdate, aTakenInstantiations); + if (NS_FAILED(rv)) + return rv; + } + } + } + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + ("TestNode[%p]: Propagate() end", this)); + + return NS_OK; +} + + +nsresult +TestNode::Constrain(InstantiationSet& aInstantiations) +{ + nsresult rv; + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + ("TestNode[%p]: Constrain() begin", this)); + + // if the cantHandleYet flag is set by FilterInstantiations, + // there isn't enough information yet available to fill in. + // For this, continue the constrain all the way to the top + // and then call FilterInstantiations again afterwards. This + // should fill in any missing information. + bool cantHandleYet = false; + rv = FilterInstantiations(aInstantiations, &cantHandleYet); + if (NS_FAILED(rv)) return rv; + + if (mParent && (!aInstantiations.Empty() || cantHandleYet)) { + // if we still have instantiations, or if the instantiations + // could not be filled in yet, then ride 'em on up to the + // parent to narrow them. + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + ("TestNode[%p]: Constrain() passing to parent %p", this, mParent)); + + rv = mParent->Constrain(aInstantiations); + + if (NS_SUCCEEDED(rv) && cantHandleYet) + rv = FilterInstantiations(aInstantiations, nullptr); + } + else { + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + ("TestNode[%p]: Constrain() failed", this)); + + rv = NS_OK; + } + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + ("TestNode[%p]: Constrain() end", this)); + + return rv; +} + + +//---------------------------------------------------------------------- + +ReteNodeSet::ReteNodeSet() + : mNodes(nullptr), mCount(0), mCapacity(0) +{ +} + +ReteNodeSet::~ReteNodeSet() +{ + Clear(); +} + +nsresult +ReteNodeSet::Add(ReteNode* aNode) +{ + NS_PRECONDITION(aNode != nullptr, "null ptr"); + if (! aNode) + return NS_ERROR_NULL_POINTER; + + if (mCount >= mCapacity) { + int32_t capacity = mCapacity + 4; + ReteNode** nodes = new ReteNode*[capacity]; + if (! nodes) + return NS_ERROR_OUT_OF_MEMORY; + + for (int32_t i = mCount - 1; i >= 0; --i) + nodes[i] = mNodes[i]; + + delete[] mNodes; + + mNodes = nodes; + mCapacity = capacity; + } + + mNodes[mCount++] = aNode; + return NS_OK; +} + +nsresult +ReteNodeSet::Clear() +{ + delete[] mNodes; + mNodes = nullptr; + mCount = mCapacity = 0; + return NS_OK; +} diff --git a/dom/xul/templates/nsRuleNetwork.h b/dom/xul/templates/nsRuleNetwork.h new file mode 100644 index 000000000..2b4402722 --- /dev/null +++ b/dom/xul/templates/nsRuleNetwork.h @@ -0,0 +1,861 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + + A rule discrimination network implementation based on ideas from + RETE and TREAT. + + RETE is described in Charles Forgy, "Rete: A Fast Algorithm for the + Many Patterns/Many Objects Match Problem", Artificial Intelligence + 19(1): pp. 17-37, 1982. + + TREAT is described in Daniel P. Miranker, "TREAT: A Better Match + Algorithm for AI Production System Matching", AAAI 1987: pp. 42-47. + + -- + + TO DO: + + . nsAssignmentSet::List objects are allocated by the gallon. We + should make it so that these are always allocated from a pool, + maybe owned by the nsRuleNetwork? + + */ + +#ifndef nsRuleNetwork_h__ +#define nsRuleNetwork_h__ + +#include "mozilla/Attributes.h" +#include "nsCOMPtr.h" +#include "nsCOMArray.h" +#include "nsIAtom.h" +#include "nsIDOMNode.h" +#include "plhash.h" +#include "PLDHashTable.h" +#include "nsIRDFNode.h" + +class nsXULTemplateResultSetRDF; + +//---------------------------------------------------------------------- + +/** + * A memory element that supports an instantiation. A memory element holds a + * set of nodes involved in an RDF test such as <member> or <triple> test. A + * memory element is created when a specific test matches. The query processor + * maintains a map between the memory elements and the results they eventually + * matched. When an assertion is removed from the graph, this map is consulted + * to determine which results will no longer match. + */ +class MemoryElement { +protected: + MemoryElement() { MOZ_COUNT_CTOR(MemoryElement); } + +public: + virtual ~MemoryElement() { MOZ_COUNT_DTOR(MemoryElement); } + + virtual const char* Type() const = 0; + virtual PLHashNumber Hash() const = 0; + virtual bool Equals(const MemoryElement& aElement) const = 0; + + bool operator==(const MemoryElement& aMemoryElement) const { + return Equals(aMemoryElement); + } + + bool operator!=(const MemoryElement& aMemoryElement) const { + return !Equals(aMemoryElement); + } +}; + +//---------------------------------------------------------------------- + +/** + * A collection of memory elements + */ +class MemoryElementSet { +public: + class ConstIterator; + friend class ConstIterator; + +protected: + class List { + public: + List() { MOZ_COUNT_CTOR(MemoryElementSet::List); } + + protected: + ~List() { + MOZ_COUNT_DTOR(MemoryElementSet::List); + delete mElement; + NS_IF_RELEASE(mNext); } + + public: + int32_t AddRef() { return ++mRefCnt; } + + int32_t Release() { + int32_t refcnt = --mRefCnt; + if (refcnt == 0) delete this; + return refcnt; } + + MemoryElement* mElement; + int32_t mRefCnt; + List* mNext; + }; + + List* mElements; + +public: + MemoryElementSet() : mElements(nullptr) { + MOZ_COUNT_CTOR(MemoryElementSet); } + + MemoryElementSet(const MemoryElementSet& aSet) : mElements(aSet.mElements) { + MOZ_COUNT_CTOR(MemoryElementSet); + NS_IF_ADDREF(mElements); } + + MemoryElementSet& operator=(const MemoryElementSet& aSet) { + NS_IF_RELEASE(mElements); + mElements = aSet.mElements; + NS_IF_ADDREF(mElements); + return *this; } + + ~MemoryElementSet() { + MOZ_COUNT_DTOR(MemoryElementSet); + NS_IF_RELEASE(mElements); } + +public: + class ConstIterator { + public: + explicit ConstIterator(List* aElementList) : mCurrent(aElementList) { + NS_IF_ADDREF(mCurrent); } + + ConstIterator(const ConstIterator& aConstIterator) + : mCurrent(aConstIterator.mCurrent) { + NS_IF_ADDREF(mCurrent); } + + ConstIterator& operator=(const ConstIterator& aConstIterator) { + NS_IF_RELEASE(mCurrent); + mCurrent = aConstIterator.mCurrent; + NS_IF_ADDREF(mCurrent); + return *this; } + + ~ConstIterator() { NS_IF_RELEASE(mCurrent); } + + ConstIterator& operator++() { + List* next = mCurrent->mNext; + NS_RELEASE(mCurrent); + mCurrent = next; + NS_IF_ADDREF(mCurrent); + return *this; } + + ConstIterator operator++(int) { + ConstIterator result(*this); + List* next = mCurrent->mNext; + NS_RELEASE(mCurrent); + mCurrent = next; + NS_IF_ADDREF(mCurrent); + return result; } + + const MemoryElement& operator*() const { + return *mCurrent->mElement; } + + const MemoryElement* operator->() const { + return mCurrent->mElement; } + + bool operator==(const ConstIterator& aConstIterator) const { + return mCurrent == aConstIterator.mCurrent; } + + bool operator!=(const ConstIterator& aConstIterator) const { + return mCurrent != aConstIterator.mCurrent; } + + protected: + List* mCurrent; + }; + + ConstIterator First() const { return ConstIterator(mElements); } + ConstIterator Last() const { return ConstIterator(nullptr); } + + // N.B. that the set assumes ownership of the element + nsresult Add(MemoryElement* aElement); +}; + +//---------------------------------------------------------------------- + +/** + * An assignment of a value to a variable + */ +class nsAssignment { +public: + const nsCOMPtr<nsIAtom> mVariable; + nsCOMPtr<nsIRDFNode> mValue; + + nsAssignment(nsIAtom* aVariable, nsIRDFNode* aValue) + : mVariable(aVariable), + mValue(aValue) + { MOZ_COUNT_CTOR(nsAssignment); } + + nsAssignment(const nsAssignment& aAssignment) + : mVariable(aAssignment.mVariable), + mValue(aAssignment.mValue) + { MOZ_COUNT_CTOR(nsAssignment); } + + ~nsAssignment() { MOZ_COUNT_DTOR(nsAssignment); } + + bool operator==(const nsAssignment& aAssignment) const { + return mVariable == aAssignment.mVariable && mValue == aAssignment.mValue; } + + bool operator!=(const nsAssignment& aAssignment) const { + return mVariable != aAssignment.mVariable || mValue != aAssignment.mValue; } + + PLHashNumber Hash() const { + // XXX I have no idea if this hashing function is good or not // XXX change this + PLHashNumber temp = PLHashNumber(NS_PTR_TO_INT32(mValue.get())) >> 2; // strip alignment bits + return (temp & 0xffff) | NS_PTR_TO_INT32(mVariable.get()); } +}; + + +//---------------------------------------------------------------------- + +/** + * A collection of value-to-variable assignments that minimizes + * copying by sharing subsets when possible. + */ +class nsAssignmentSet { +public: + class ConstIterator; + friend class ConstIterator; + +protected: + class List { + public: + explicit List(const nsAssignment& aAssignment) : mAssignment(aAssignment) { + MOZ_COUNT_CTOR(nsAssignmentSet::List); } + + protected: + ~List() { + MOZ_COUNT_DTOR(nsAssignmentSet::List); + NS_IF_RELEASE(mNext); } + + public: + + int32_t AddRef() { return ++mRefCnt; } + + int32_t Release() { + int32_t refcnt = --mRefCnt; + if (refcnt == 0) delete this; + return refcnt; } + + nsAssignment mAssignment; + int32_t mRefCnt; + List* mNext; + }; + + List* mAssignments; + +public: + nsAssignmentSet() + : mAssignments(nullptr) + { MOZ_COUNT_CTOR(nsAssignmentSet); } + + nsAssignmentSet(const nsAssignmentSet& aSet) + : mAssignments(aSet.mAssignments) { + MOZ_COUNT_CTOR(nsAssignmentSet); + NS_IF_ADDREF(mAssignments); } + + nsAssignmentSet& operator=(const nsAssignmentSet& aSet) { + NS_IF_RELEASE(mAssignments); + mAssignments = aSet.mAssignments; + NS_IF_ADDREF(mAssignments); + return *this; } + + ~nsAssignmentSet() { + MOZ_COUNT_DTOR(nsAssignmentSet); + NS_IF_RELEASE(mAssignments); } + +public: + class ConstIterator { + public: + explicit ConstIterator(List* aAssignmentList) : mCurrent(aAssignmentList) { + NS_IF_ADDREF(mCurrent); } + + ConstIterator(const ConstIterator& aConstIterator) + : mCurrent(aConstIterator.mCurrent) { + NS_IF_ADDREF(mCurrent); } + + ConstIterator& operator=(const ConstIterator& aConstIterator) { + NS_IF_RELEASE(mCurrent); + mCurrent = aConstIterator.mCurrent; + NS_IF_ADDREF(mCurrent); + return *this; } + + ~ConstIterator() { NS_IF_RELEASE(mCurrent); } + + ConstIterator& operator++() { + List* next = mCurrent->mNext; + NS_RELEASE(mCurrent); + mCurrent = next; + NS_IF_ADDREF(mCurrent); + return *this; } + + ConstIterator operator++(int) { + ConstIterator result(*this); + List* next = mCurrent->mNext; + NS_RELEASE(mCurrent); + mCurrent = next; + NS_IF_ADDREF(mCurrent); + return result; } + + const nsAssignment& operator*() const { + return mCurrent->mAssignment; } + + const nsAssignment* operator->() const { + return &mCurrent->mAssignment; } + + bool operator==(const ConstIterator& aConstIterator) const { + return mCurrent == aConstIterator.mCurrent; } + + bool operator!=(const ConstIterator& aConstIterator) const { + return mCurrent != aConstIterator.mCurrent; } + + protected: + List* mCurrent; + }; + + ConstIterator First() const { return ConstIterator(mAssignments); } + ConstIterator Last() const { return ConstIterator(nullptr); } + +public: + /** + * Add an assignment to the set + * @param aElement the assigment to add + * @return NS_OK if all is well, NS_ERROR_OUT_OF_MEMORY if memory + * could not be allocated for the addition. + */ + nsresult Add(const nsAssignment& aElement); + + /** + * Determine if the assignment set contains the specified variable + * to value assignment. + * @param aVariable the variable for which to lookup the binding + * @param aValue the value to query + * @return true if aVariable is bound to aValue; false otherwise. + */ + bool HasAssignment(nsIAtom* aVariable, nsIRDFNode* aValue) const; + + /** + * Determine if the assignment set contains the specified assignment + * @param aAssignment the assignment to search for + * @return true if the set contains the assignment, false otherwise. + */ + bool HasAssignment(const nsAssignment& aAssignment) const { + return HasAssignment(aAssignment.mVariable, aAssignment.mValue); } + + /** + * Determine whether the assignment set has an assignment for the + * specified variable. + * @param aVariable the variable to query + * @return true if the assignment set has an assignment for the variable, + * false otherwise. + */ + bool HasAssignmentFor(nsIAtom* aVariable) const; + + /** + * Retrieve the assignment for the specified variable + * @param aVariable the variable to query + * @param aValue an out parameter that will receive the value assigned + * to the variable, if any. + * @return true if the variable has an assignment, false + * if there was no assignment for the variable. + */ + bool GetAssignmentFor(nsIAtom* aVariable, nsIRDFNode** aValue) const; + + /** + * Count the number of assignments in the set + * @return the number of assignments in the set + */ + int32_t Count() const; + + /** + * Determine if the set is empty + * @return true if the assignment set is empty, false otherwise. + */ + bool IsEmpty() const { return mAssignments == nullptr; } + + bool Equals(const nsAssignmentSet& aSet) const; + bool operator==(const nsAssignmentSet& aSet) const { return Equals(aSet); } + bool operator!=(const nsAssignmentSet& aSet) const { return !Equals(aSet); } +}; + + +//---------------------------------------------------------------------- + +/** + * A collection of variable-to-value bindings, with the memory elements + * that support those bindings. Essentially, an instantiation is the + * collection of variables and values assigned to those variables for a single + * result. For each RDF rule in the rule network, each instantiation is + * examined and either extended with additional bindings specified by the RDF + * rule, or removed if the rule doesn't apply (for instance if a node has no + * children). When an instantiation gets to the last node of the rule network, + * which is always an nsInstantiationNode, a result is created for it. + * + * An instantiation object is typically created by "extending" another + * instantiation object. That is, using the copy constructor, and + * adding bindings and support to the instantiation. + */ +class Instantiation +{ +public: + /** + * The variable-to-value bindings + */ + nsAssignmentSet mAssignments; + + /** + * The memory elements that support the bindings. + */ + MemoryElementSet mSupport; + + Instantiation() { MOZ_COUNT_CTOR(Instantiation); } + + Instantiation(const Instantiation& aInstantiation) + : mAssignments(aInstantiation.mAssignments), + mSupport(aInstantiation.mSupport) { + MOZ_COUNT_CTOR(Instantiation); } + + Instantiation& operator=(const Instantiation& aInstantiation) { + mAssignments = aInstantiation.mAssignments; + mSupport = aInstantiation.mSupport; + return *this; } + + ~Instantiation() { MOZ_COUNT_DTOR(Instantiation); } + + /** + * Add the specified variable-to-value assignment to the instantiation's + * set of assignments. + * @param aVariable the variable to which is being assigned + * @param aValue the value that is being assigned + * @return NS_OK if no errors, NS_ERROR_OUT_OF_MEMORY if there + * is not enough memory to perform the operation + */ + nsresult AddAssignment(nsIAtom* aVariable, nsIRDFNode* aValue) { + mAssignments.Add(nsAssignment(aVariable, aValue)); + return NS_OK; } + + /** + * Add a memory element to the set of memory elements that are + * supporting the instantiation + * @param aMemoryElement the memory element to add to the + * instantiation's set of support + * @return NS_OK if no errors occurred, NS_ERROR_OUT_OF_MEMORY + * if there is not enough memory to perform the operation. + */ + nsresult AddSupportingElement(MemoryElement* aMemoryElement) { + mSupport.Add(aMemoryElement); + return NS_OK; } + + bool Equals(const Instantiation& aInstantiation) const { + return mAssignments == aInstantiation.mAssignments; } + + bool operator==(const Instantiation& aInstantiation) const { + return Equals(aInstantiation); } + + bool operator!=(const Instantiation& aInstantiation) const { + return !Equals(aInstantiation); } + + static PLHashNumber Hash(const void* aKey); + static int Compare(const void* aLeft, const void* aRight); +}; + + +//---------------------------------------------------------------------- + +/** + * A collection of intantiations + */ +class InstantiationSet +{ +public: + InstantiationSet(); + InstantiationSet(const InstantiationSet& aInstantiationSet); + InstantiationSet& operator=(const InstantiationSet& aInstantiationSet); + + ~InstantiationSet() { + MOZ_COUNT_DTOR(InstantiationSet); + Clear(); } + + class ConstIterator; + friend class ConstIterator; + + class Iterator; + friend class Iterator; + + friend class nsXULTemplateResultSetRDF; // so it can get to the List + +protected: + class List { + public: + Instantiation mInstantiation; + List* mNext; + List* mPrev; + + List() { MOZ_COUNT_CTOR(InstantiationSet::List); } + ~List() { MOZ_COUNT_DTOR(InstantiationSet::List); } + }; + + List mHead; + +public: + class ConstIterator { + protected: + friend class Iterator; // XXXwaterson so broken. + List* mCurrent; + + public: + explicit ConstIterator(List* aList) : mCurrent(aList) {} + + ConstIterator(const ConstIterator& aConstIterator) + : mCurrent(aConstIterator.mCurrent) {} + + ConstIterator& operator=(const ConstIterator& aConstIterator) { + mCurrent = aConstIterator.mCurrent; + return *this; } + + ConstIterator& operator++() { + mCurrent = mCurrent->mNext; + return *this; } + + ConstIterator operator++(int) { + ConstIterator result(*this); + mCurrent = mCurrent->mNext; + return result; } + + ConstIterator& operator--() { + mCurrent = mCurrent->mPrev; + return *this; } + + ConstIterator operator--(int) { + ConstIterator result(*this); + mCurrent = mCurrent->mPrev; + return result; } + + const Instantiation& operator*() const { + return mCurrent->mInstantiation; } + + const Instantiation* operator->() const { + return &mCurrent->mInstantiation; } + + bool operator==(const ConstIterator& aConstIterator) const { + return mCurrent == aConstIterator.mCurrent; } + + bool operator!=(const ConstIterator& aConstIterator) const { + return mCurrent != aConstIterator.mCurrent; } + }; + + ConstIterator First() const { return ConstIterator(mHead.mNext); } + ConstIterator Last() const { return ConstIterator(const_cast<List*>(&mHead)); } + + class Iterator : public ConstIterator { + public: + explicit Iterator(List* aList) : ConstIterator(aList) {} + + Iterator& operator++() { + mCurrent = mCurrent->mNext; + return *this; } + + Iterator operator++(int) { + Iterator result(*this); + mCurrent = mCurrent->mNext; + return result; } + + Iterator& operator--() { + mCurrent = mCurrent->mPrev; + return *this; } + + Iterator operator--(int) { + Iterator result(*this); + mCurrent = mCurrent->mPrev; + return result; } + + Instantiation& operator*() const { + return mCurrent->mInstantiation; } + + Instantiation* operator->() const { + return &mCurrent->mInstantiation; } + + bool operator==(const ConstIterator& aConstIterator) const { + return mCurrent == aConstIterator.mCurrent; } + + bool operator!=(const ConstIterator& aConstIterator) const { + return mCurrent != aConstIterator.mCurrent; } + + friend class InstantiationSet; + }; + + Iterator First() { return Iterator(mHead.mNext); } + Iterator Last() { return Iterator(&mHead); } + + bool Empty() const { return First() == Last(); } + + Iterator Append(const Instantiation& aInstantiation) { + return Insert(Last(), aInstantiation); } + + Iterator Insert(Iterator aBefore, const Instantiation& aInstantiation); + + Iterator Erase(Iterator aElement); + + void Clear(); + + bool HasAssignmentFor(nsIAtom* aVariable) const; +}; + +//---------------------------------------------------------------------- + +/** + * A abstract base class for all nodes in the rule network + */ +class ReteNode +{ +public: + ReteNode() {} + virtual ~ReteNode() {} + + /** + * Propagate a set of instantiations "down" through the + * network. Each instantiation is a partial set of + * variable-to-value assignments, along with the memory elements + * that support it. + * + * The node must evaluate each instantiation, and either 1) + * extend it with additional assignments and memory-element + * support, or 2) remove it from the set because it is + * inconsistent with the constraints that this node applies. + * + * The node must then pass the resulting instantiation set along + * to any of its children in the network. (In other words, the + * node must recursively call Propagate() on its children. We + * should fix this to make the algorithm interruptable.) + * + * See TestNode::Propagate for details about instantiation set ownership + * + * @param aInstantiations the set of instantiations to propagate + * down through the network. + * @param aIsUpdate true if updating, false for first generation + * @param aTakenInstantiations true if the ownership over aInstantiations + * has been taken from the caller. If false, + * the caller owns it. + * @return NS_OK if no errors occurred. + */ + virtual nsresult Propagate(InstantiationSet& aInstantiations, + bool aIsUpdate, bool& aTakenInstantiations) = 0; +}; + +//---------------------------------------------------------------------- + +/** + * A collection of nodes in the rule network + */ +class ReteNodeSet +{ +public: + ReteNodeSet(); + ~ReteNodeSet(); + + nsresult Add(ReteNode* aNode); + nsresult Clear(); + + class Iterator; + + class ConstIterator { + public: + explicit ConstIterator(ReteNode** aNode) : mCurrent(aNode) {} + + ConstIterator(const ConstIterator& aConstIterator) + : mCurrent(aConstIterator.mCurrent) {} + + ConstIterator& operator=(const ConstIterator& aConstIterator) { + mCurrent = aConstIterator.mCurrent; + return *this; } + + ConstIterator& operator++() { + ++mCurrent; + return *this; } + + ConstIterator operator++(int) { + ConstIterator result(*this); + ++mCurrent; + return result; } + + const ReteNode* operator*() const { + return *mCurrent; } + + const ReteNode* operator->() const { + return *mCurrent; } + + bool operator==(const ConstIterator& aConstIterator) const { + return mCurrent == aConstIterator.mCurrent; } + + bool operator!=(const ConstIterator& aConstIterator) const { + return mCurrent != aConstIterator.mCurrent; } + + protected: + friend class Iterator; // XXXwaterson this is so wrong! + ReteNode** mCurrent; + }; + + ConstIterator First() const { return ConstIterator(mNodes); } + ConstIterator Last() const { return ConstIterator(mNodes + mCount); } + + class Iterator : public ConstIterator { + public: + explicit Iterator(ReteNode** aNode) : ConstIterator(aNode) {} + + Iterator& operator++() { + ++mCurrent; + return *this; } + + Iterator operator++(int) { + Iterator result(*this); + ++mCurrent; + return result; } + + ReteNode* operator*() const { + return *mCurrent; } + + ReteNode* operator->() const { + return *mCurrent; } + + bool operator==(const ConstIterator& aConstIterator) const { + return mCurrent == aConstIterator.mCurrent; } + + bool operator!=(const ConstIterator& aConstIterator) const { + return mCurrent != aConstIterator.mCurrent; } + }; + + Iterator First() { return Iterator(mNodes); } + Iterator Last() { return Iterator(mNodes + mCount); } + + int32_t Count() const { return mCount; } + +protected: + ReteNode** mNodes; + int32_t mCount; + int32_t mCapacity; +}; + +//---------------------------------------------------------------------- + +/** + * A node that applies a test condition to a set of instantiations. + * + * This class provides implementations of Propagate() and Constrain() + * in terms of one simple operation, FilterInstantiations(). A node + * that is a "simple test node" in a rule network should derive from + * this class, and need only implement FilterInstantiations(). + */ +class TestNode : public ReteNode +{ +public: + explicit TestNode(TestNode* aParent); + + /** + * Retrieve the test node's parent + * @return the test node's parent + */ + TestNode* GetParent() const { return mParent; } + + /** + * Calls FilterInstantiations() on the instantiation set, and if + * the resulting set isn't empty, propagates the new set down to + * each of the test node's children. + * + * Note that the caller of Propagate is responsible for deleting + * aInstantiations if necessary as described below. + * + * Propagate may be called in update or non-update mode as indicated + * by the aIsUpdate argument. Non-update mode is used when initially + * generating results, whereas update mode is used when the datasource + * changes and new results might be available. + * + * The last node in a chain of TestNodes is always an nsInstantiationNode. + * In non-update mode, this nsInstantiationNode will cache the results + * in the query using the SetCachedResults method. The query processor + * takes these cached results and creates a nsXULTemplateResultSetRDF + * which is the enumeration returned to the template builder. This + * nsXULTemplateResultSetRDF owns the instantiations and they will be + * deleted when the nsXULTemplateResultSetRDF goes away. + * + * In update mode, the nsInstantiationNode node will iterate over the + * instantiations itself and callback to the builder to update any matches + * and generated content. If no instantiations match, then the builder + * will never be called. + * + * Thus, the difference between update and non-update modes is that in + * update mode, the results and instantiations have been already handled + * whereas in non-update mode they are expected to be returned in an + * nsXULTemplateResultSetRDF for further processing by the builder. + * + * Regardless, aTakenInstantiations will be set to true if the + * ownership over aInstantiations has been transferred to a result set. + * If set to false, the caller is still responsible for aInstantiations. + * aTakenInstantiations will be set properly even if an error occurs. + */ + virtual nsresult Propagate(InstantiationSet& aInstantiations, + bool aIsUpdate, bool& aTakenInstantiations) override; + + /** + * This is called by a child node on its parent to allow the + * parent's constraints to apply to the set of instantiations. + * + * A node must iterate through the set of instantiations, and for + * each instantiation, either 1) extend the instantiation by + * adding variable-to-value assignments and memory element support + * for those assignments, or 2) remove the instantiation because + * it is inconsistent. + * + * The node must then pass the resulting set of instantiations up + * to its parent (by recursive call; we should make this iterative + * & interruptable at some point.) + * + * @param aInstantiations the set of instantiations that must + * be constrained + * @return NS_OK if no errors occurred + */ + virtual nsresult Constrain(InstantiationSet& aInstantiations); + + /** + * Given a set of instantiations, filter out any that are + * inconsistent with the test node's test, and append + * variable-to-value assignments and memory element support for + * those which do pass the test node's test. + * + * @param aInstantiations the set of instantiations to be + * filtered + * @param aCantHandleYet [out] true if the instantiations do not contain + * enough information to constrain the data. May be null if this + * isn't important to the caller. + * @return NS_OK if no errors occurred. + */ + virtual nsresult FilterInstantiations(InstantiationSet& aInstantiations, + bool* aCantHandleYet) const = 0; + //XXX probably better named "ApplyConstraints" or "Discrminiate" or something + + /** + * Add another node as a child of this node. + * @param aNode the node to add. + * @return NS_OK if no errors occur. + */ + nsresult AddChild(ReteNode* aNode) { return mKids.Add(aNode); } + + /** + * Remove all the children of this node + * @return NS_OK if no errors occur. + */ + nsresult RemoveAllChildren() { return mKids.Clear(); } + +protected: + TestNode* mParent; + ReteNodeSet mKids; +}; + +#endif // nsRuleNetwork_h__ diff --git a/dom/xul/templates/nsTemplateMap.h b/dom/xul/templates/nsTemplateMap.h new file mode 100644 index 000000000..cb828b093 --- /dev/null +++ b/dom/xul/templates/nsTemplateMap.h @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsTemplateMap_h__ +#define nsTemplateMap_h__ + +#include "PLDHashTable.h" +#include "nsXULElement.h" + +class nsTemplateMap { +protected: + struct Entry : public PLDHashEntryHdr { + nsIContent* mContent; + nsIContent* mTemplate; + }; + + PLDHashTable mTable; + +public: + nsTemplateMap() : mTable(PLDHashTable::StubOps(), sizeof(Entry)) { } + + ~nsTemplateMap() { } + + void + Put(nsIContent* aContent, nsIContent* aTemplate) { + NS_ASSERTION(!mTable.Search(aContent), "aContent already in map"); + + auto entry = static_cast<Entry*>(mTable.Add(aContent, fallible)); + + if (entry) { + entry->mContent = aContent; + entry->mTemplate = aTemplate; + } + } + + void + Remove(nsIContent* aContent) { + mTable.Remove(aContent); + + for (nsIContent* child = aContent->GetFirstChild(); + child; + child = child->GetNextSibling()) { + Remove(child); + } + } + + + void + GetTemplateFor(nsIContent* aContent, nsIContent** aResult) { + auto entry = static_cast<Entry*>(mTable.Search(aContent)); + if (entry) + NS_IF_ADDREF(*aResult = entry->mTemplate); + else + *aResult = nullptr; + } + + void + Clear() { mTable.Clear(); } +}; + +#endif // nsTemplateMap_h__ + diff --git a/dom/xul/templates/nsTemplateMatch.cpp b/dom/xul/templates/nsTemplateMatch.cpp new file mode 100644 index 000000000..1ba1c9d87 --- /dev/null +++ b/dom/xul/templates/nsTemplateMatch.cpp @@ -0,0 +1,35 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsTemplateMatch.h" +#include "nsTemplateRule.h" + +// static +void +nsTemplateMatch::Destroy(nsTemplateMatch*& aMatch, bool aRemoveResult) +{ + if (aRemoveResult && aMatch->mResult) + aMatch->mResult->HasBeenRemoved(); + ::delete aMatch; + aMatch = nullptr; +} + +nsresult +nsTemplateMatch::RuleMatched(nsTemplateQuerySet* aQuerySet, + nsTemplateRule* aRule, + int16_t aRuleIndex, + nsIXULTemplateResult* aResult) +{ + // assign the rule index, used to indicate that a match is active, and + // so the tree builder can get the right action body to generate + mRuleIndex = aRuleIndex; + + nsCOMPtr<nsIDOMNode> rulenode; + aRule->GetRuleNode(getter_AddRefs(rulenode)); + if (rulenode) + return aResult->RuleMatched(aQuerySet->mCompiledQuery, rulenode); + + return NS_OK; +} diff --git a/dom/xul/templates/nsTemplateMatch.h b/dom/xul/templates/nsTemplateMatch.h new file mode 100644 index 000000000..ded2fe1ab --- /dev/null +++ b/dom/xul/templates/nsTemplateMatch.h @@ -0,0 +1,139 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsTemplateMatch_h__ +#define nsTemplateMatch_h__ + +#include "mozilla/Attributes.h" +#include "nsIContent.h" +#include "nsIXULTemplateQueryProcessor.h" +#include "nsIXULTemplateResult.h" +#include "nsRuleNetwork.h" + +/** + * A match object, where each match object is associated with one result. + * There will be one match list for each unique id generated. However, since + * there are multiple querysets and each may generate results with the same + * id, they are all chained together in a linked list, ordered in the same + * order as the respective <queryset> elements they were generated from. + * A match can be identified by the container and id. The id is retrievable + * from the result. + * + * Only one match per container and id pair is active at a time, but which + * match is active may change as new results are added or removed. When a + * match is active, content is generated for that match. + * + * Matches are stored and owned by the mMatchToMap hash in the template + * builder. + */ + +class nsTemplateRule; +class nsTemplateQuerySet; + +class nsTemplateMatch { +private: + // Hide so that only Create() and Destroy() can be used to + // allocate and deallocate from the heap + void* operator new(size_t) CPP_THROW_NEW { MOZ_ASSERT(0); return nullptr; } + void operator delete(void*, size_t) { MOZ_ASSERT(0); } + +public: + nsTemplateMatch(uint16_t aQuerySetPriority, + nsIXULTemplateResult* aResult, + nsIContent* aContainer) + : mRuleIndex(-1), + mQuerySetPriority(aQuerySetPriority), + mContainer(aContainer), + mResult(aResult), + mNext(nullptr) + { + MOZ_COUNT_CTOR(nsTemplateMatch); + } + + ~nsTemplateMatch() + { + MOZ_COUNT_DTOR(nsTemplateMatch); + } + + static nsTemplateMatch* + Create(uint16_t aQuerySetPriority, + nsIXULTemplateResult* aResult, + nsIContent* aContainer) { + return ::new nsTemplateMatch(aQuerySetPriority, aResult, aContainer); + } + + static void Destroy(nsTemplateMatch*& aMatch, bool aRemoveResult); + + // return true if the the match is active, and has generated output + bool IsActive() { + return mRuleIndex >= 0; + } + + // indicate that a rule is no longer active, used when a query with a + // lower priority has overriden the match + void SetInactive() { + mRuleIndex = -1; + } + + // return matching rule index + int16_t RuleIndex() { + return mRuleIndex; + } + + // return priority of query set + uint16_t QuerySetPriority() { + return mQuerySetPriority; + } + + // return container, not addrefed. May be null. + nsIContent* GetContainer() { + return mContainer; + } + + nsresult RuleMatched(nsTemplateQuerySet* aQuerySet, + nsTemplateRule* aRule, + int16_t aRuleIndex, + nsIXULTemplateResult* aResult); + +private: + + /** + * The index of the rule that matched, or -1 if the match is not active. + */ + int16_t mRuleIndex; + + /** + * The priority of the queryset for this rule + */ + uint16_t mQuerySetPriority; + + /** + * The container the content generated for the match is inside. + */ + nsCOMPtr<nsIContent> mContainer; + +public: + + /** + * The result associated with this match + */ + nsCOMPtr<nsIXULTemplateResult> mResult; + + /** + * Matches are stored in a linked list, in priority order. This first + * match that has a rule set (mRule) is the active match and generates + * content. The next match is owned by the builder, which will delete + * template matches when needed. + */ + nsTemplateMatch *mNext; + +private: + + nsTemplateMatch(const nsTemplateMatch& aMatch) = delete; + void operator=(const nsTemplateMatch& aMatch) = delete; +}; + +#endif // nsTemplateMatch_h__ + diff --git a/dom/xul/templates/nsTemplateRule.cpp b/dom/xul/templates/nsTemplateRule.cpp new file mode 100644 index 000000000..6d82740e3 --- /dev/null +++ b/dom/xul/templates/nsTemplateRule.cpp @@ -0,0 +1,422 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsTemplateRule.h" +#include "nsTemplateMatch.h" +#include "nsXULContentUtils.h" +#include "nsUnicharUtils.h" +#include "nsReadableUtils.h" +#include "nsICollation.h" + +nsTemplateCondition::nsTemplateCondition(nsIAtom* aSourceVariable, + const nsAString& aRelation, + nsIAtom* aTargetVariable, + bool aIgnoreCase, + bool aNegate) + : mSourceVariable(aSourceVariable), + mTargetVariable(aTargetVariable), + mIgnoreCase(aIgnoreCase), + mNegate(aNegate), + mNext(nullptr) +{ + SetRelation(aRelation); + + MOZ_COUNT_CTOR(nsTemplateCondition); +} + +nsTemplateCondition::nsTemplateCondition(nsIAtom* aSourceVariable, + const nsAString& aRelation, + const nsAString& aTargets, + bool aIgnoreCase, + bool aNegate, + bool aIsMultiple) + : mSourceVariable(aSourceVariable), + mIgnoreCase(aIgnoreCase), + mNegate(aNegate), + mNext(nullptr) +{ + SetRelation(aRelation); + + if (aIsMultiple) { + int32_t start = 0, end = 0; + while ((end = aTargets.FindChar(',',start)) >= 0) { + if (end > start) { + mTargetList.AppendElement(Substring(aTargets, start, end - start)); + } + start = end + 1; + } + if (start < int32_t(aTargets.Length())) { + mTargetList.AppendElement(Substring(aTargets, start)); + } + } + else { + mTargetList.AppendElement(aTargets); + } + + MOZ_COUNT_CTOR(nsTemplateCondition); +} + +nsTemplateCondition::nsTemplateCondition(const nsAString& aSource, + const nsAString& aRelation, + nsIAtom* aTargetVariable, + bool aIgnoreCase, + bool aNegate) + : mSource(aSource), + mTargetVariable(aTargetVariable), + mIgnoreCase(aIgnoreCase), + mNegate(aNegate), + mNext(nullptr) +{ + SetRelation(aRelation); + + MOZ_COUNT_CTOR(nsTemplateCondition); +} + +void +nsTemplateCondition::SetRelation(const nsAString& aRelation) +{ + if (aRelation.EqualsLiteral("equals") || aRelation.IsEmpty()) + mRelation = eEquals; + else if (aRelation.EqualsLiteral("less")) + mRelation = eLess; + else if (aRelation.EqualsLiteral("greater")) + mRelation = eGreater; + else if (aRelation.EqualsLiteral("before")) + mRelation = eBefore; + else if (aRelation.EqualsLiteral("after")) + mRelation = eAfter; + else if (aRelation.EqualsLiteral("startswith")) + mRelation = eStartswith; + else if (aRelation.EqualsLiteral("endswith")) + mRelation = eEndswith; + else if (aRelation.EqualsLiteral("contains")) + mRelation = eContains; + else + mRelation = eUnknown; +} + +bool +nsTemplateCondition::CheckMatch(nsIXULTemplateResult* aResult) +{ + bool match = false; + + nsAutoString leftString; + if (mSourceVariable) + aResult->GetBindingFor(mSourceVariable, leftString); + else + leftString.Assign(mSource); + + if (mTargetVariable) { + nsAutoString rightString; + aResult->GetBindingFor(mTargetVariable, rightString); + + match = CheckMatchStrings(leftString, rightString); + } + else { + // iterate over the strings in the target and determine + // whether there is a match. + uint32_t length = mTargetList.Length(); + for (uint32_t t = 0; t < length; t++) { + match = CheckMatchStrings(leftString, mTargetList[t]); + + // stop once a match is found. In negate mode, stop once a + // target does not match. + if (match != mNegate) break; + } + } + + return match; +} + + +bool +nsTemplateCondition::CheckMatchStrings(const nsAString& aLeftString, + const nsAString& aRightString) +{ + bool match = false; + + if (aRightString.IsEmpty()) { + if ((mRelation == eEquals) && aLeftString.IsEmpty()) + match = true; + } + else { + switch (mRelation) { + case eEquals: + if (mIgnoreCase) + match = aLeftString.Equals(aRightString, + nsCaseInsensitiveStringComparator()); + else + match = aLeftString.Equals(aRightString); + break; + + case eLess: + case eGreater: + { + // non-numbers always compare false + nsresult err; + int32_t leftint = PromiseFlatString(aLeftString).ToInteger(&err); + if (NS_SUCCEEDED(err)) { + int32_t rightint = PromiseFlatString(aRightString).ToInteger(&err); + if (NS_SUCCEEDED(err)) { + match = (mRelation == eLess) ? (leftint < rightint) : + (leftint > rightint); + } + } + + break; + } + + case eBefore: + { + nsICollation* collation = nsXULContentUtils::GetCollation(); + if (collation) { + int32_t sortOrder; + collation->CompareString((mIgnoreCase ? + static_cast<int32_t>(nsICollation::kCollationCaseInSensitive) : + static_cast<int32_t>(nsICollation::kCollationCaseSensitive)), + aLeftString, + aRightString, + &sortOrder); + match = (sortOrder < 0); + } + else if (mIgnoreCase) { + match = (Compare(aLeftString, aRightString, + nsCaseInsensitiveStringComparator()) < 0); + } + else { + match = (Compare(aLeftString, aRightString) < 0); + } + break; + } + + case eAfter: + { + nsICollation* collation = nsXULContentUtils::GetCollation(); + if (collation) { + int32_t sortOrder; + collation->CompareString((mIgnoreCase ? + static_cast<int32_t>(nsICollation::kCollationCaseInSensitive) : + static_cast<int32_t>(nsICollation::kCollationCaseSensitive)), + aLeftString, + aRightString, + &sortOrder); + match = (sortOrder > 0); + } + else if (mIgnoreCase) { + match = (Compare(aLeftString, aRightString, + nsCaseInsensitiveStringComparator()) > 0); + } + else { + match = (Compare(aLeftString, aRightString) > 0); + } + break; + } + + case eStartswith: + if (mIgnoreCase) + match = (StringBeginsWith(aLeftString, aRightString, + nsCaseInsensitiveStringComparator())); + else + match = (StringBeginsWith(aLeftString, aRightString)); + break; + + case eEndswith: + if (mIgnoreCase) + match = (StringEndsWith(aLeftString, aRightString, + nsCaseInsensitiveStringComparator())); + else + match = (StringEndsWith(aLeftString, aRightString)); + break; + + case eContains: + { + nsAString::const_iterator start, end; + aLeftString.BeginReading(start); + aLeftString.EndReading(end); + if (mIgnoreCase) + match = CaseInsensitiveFindInReadable(aRightString, start, end); + else + match = FindInReadable(aRightString, start, end); + break; + } + + default: + break; + } + } + + if (mNegate) match = !match; + + return match; +} + +nsTemplateRule::nsTemplateRule(nsIContent* aRuleNode, + nsIContent* aAction, + nsTemplateQuerySet* aQuerySet) + : mQuerySet(aQuerySet), + mAction(aAction), + mBindings(nullptr), + mConditions(nullptr) +{ + MOZ_COUNT_CTOR(nsTemplateRule); + mRuleNode = do_QueryInterface(aRuleNode); +} + +nsTemplateRule::nsTemplateRule(const nsTemplateRule& aOtherRule) + : mQuerySet(aOtherRule.mQuerySet), + mRuleNode(aOtherRule.mRuleNode), + mAction(aOtherRule.mAction), + mBindings(nullptr), + mConditions(nullptr) +{ + MOZ_COUNT_CTOR(nsTemplateRule); +} + +nsTemplateRule::~nsTemplateRule() +{ + MOZ_COUNT_DTOR(nsTemplateRule); + + while (mBindings) { + Binding* doomed = mBindings; + mBindings = mBindings->mNext; + delete doomed; + } + + while (mConditions) { + nsTemplateCondition* cdel = mConditions; + mConditions = mConditions->GetNext(); + delete cdel; + } +} + +nsresult +nsTemplateRule::GetRuleNode(nsIDOMNode** aRuleNode) const +{ + *aRuleNode = mRuleNode; + NS_IF_ADDREF(*aRuleNode); + return NS_OK; +} + +void nsTemplateRule::SetCondition(nsTemplateCondition* aCondition) +{ + while (mConditions) { + nsTemplateCondition* cdel = mConditions; + mConditions = mConditions->GetNext(); + delete cdel; + } + + mConditions = aCondition; +} + +bool +nsTemplateRule::CheckMatch(nsIXULTemplateResult* aResult) const +{ + // check the conditions in the rule first + nsTemplateCondition* condition = mConditions; + while (condition) { + if (!condition->CheckMatch(aResult)) + return false; + + condition = condition->GetNext(); + } + + if (mRuleFilter) { + // if a rule filter was set, check it for a match. If an error occurs, + // assume that the match was acceptable + bool match; + nsresult rv = mRuleFilter->Match(aResult, mRuleNode, &match); + return NS_FAILED(rv) || match; + } + + return true; +} + +bool +nsTemplateRule::HasBinding(nsIAtom* aSourceVariable, + nsAString& aExpr, + nsIAtom* aTargetVariable) const +{ + for (Binding* binding = mBindings; binding != nullptr; binding = binding->mNext) { + if ((binding->mSourceVariable == aSourceVariable) && + (binding->mExpr.Equals(aExpr)) && + (binding->mTargetVariable == aTargetVariable)) + return true; + } + + return false; +} + +nsresult +nsTemplateRule::AddBinding(nsIAtom* aSourceVariable, + nsAString& aExpr, + nsIAtom* aTargetVariable) +{ + NS_PRECONDITION(aSourceVariable != 0, "no source variable!"); + if (! aSourceVariable) + return NS_ERROR_INVALID_ARG; + + NS_PRECONDITION(aTargetVariable != 0, "no target variable!"); + if (! aTargetVariable) + return NS_ERROR_INVALID_ARG; + + NS_ASSERTION(! HasBinding(aSourceVariable, aExpr, aTargetVariable), + "binding added twice"); + + Binding* newbinding = new Binding; + if (! newbinding) + return NS_ERROR_OUT_OF_MEMORY; + + newbinding->mSourceVariable = aSourceVariable; + newbinding->mTargetVariable = aTargetVariable; + newbinding->mParent = nullptr; + + newbinding->mExpr.Assign(aExpr); + + Binding* binding = mBindings; + Binding** link = &mBindings; + + // Insert it at the end, unless we detect that an existing + // binding's source is dependent on the newbinding's target. + // + // XXXwaterson this isn't enough to make sure that we get all of + // the dependencies worked out right, but it'll do for now. For + // example, if you have (ab, bc, cd), and insert them in the order + // (cd, ab, bc), you'll get (bc, cd, ab). The good news is, if the + // person uses a natural ordering when writing the XUL, it'll all + // work out ok. + while (binding) { + if (binding->mSourceVariable == newbinding->mTargetVariable) { + binding->mParent = newbinding; + break; + } + else if (binding->mTargetVariable == newbinding->mSourceVariable) { + newbinding->mParent = binding; + } + + link = &binding->mNext; + binding = binding->mNext; + } + + // Insert the newbinding + *link = newbinding; + newbinding->mNext = binding; + return NS_OK; +} + +nsresult +nsTemplateRule::AddBindingsToQueryProcessor(nsIXULTemplateQueryProcessor* aProcessor) +{ + Binding* binding = mBindings; + + while (binding) { + nsresult rv = aProcessor->AddBinding(mRuleNode, binding->mTargetVariable, + binding->mSourceVariable, binding->mExpr); + if (NS_FAILED(rv)) return rv; + + binding = binding->mNext; + } + + return NS_OK; +} diff --git a/dom/xul/templates/nsTemplateRule.h b/dom/xul/templates/nsTemplateRule.h new file mode 100644 index 000000000..d7821ba29 --- /dev/null +++ b/dom/xul/templates/nsTemplateRule.h @@ -0,0 +1,328 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsTemplateRule_h__ +#define nsTemplateRule_h__ + +#include "nsCOMPtr.h" +#include "nsIAtom.h" +#include "nsIRDFDataSource.h" +#include "nsIRDFResource.h" +#include "nsIContent.h" +#include "nsIDOMNode.h" +#include "nsTArray.h" +#include "nsString.h" +#include "nsIXULTemplateRuleFilter.h" +#include "nsCycleCollectionParticipant.h" + +class nsIXULTemplateQueryProcessor; +class nsTemplateQuerySet; + +class nsTemplateCondition +{ +public: + // relations that may be used in a rule. They may be negated with the + // negate flag. Less and Greater are used for numeric comparisons and + // Before and After are used for string comparisons. For Less, Greater, + // Before, After, Startswith, Endswith, and Contains, the source is + // conceptually on the left of the relation and the target is on the + // right. For example, if the relation is Contains, that means Match if + // the source contains the target. + enum ConditionRelation { + eUnknown, + eEquals, + eLess, + eGreater, + eBefore, + eAfter, + eStartswith, + eEndswith, + eContains + }; + + nsTemplateCondition(nsIAtom* aSourceVariable, + const nsAString& aRelation, + nsIAtom* aTargetVariable, + bool mIgnoreCase, + bool mNegate); + + nsTemplateCondition(nsIAtom* aSourceVariable, + const nsAString& aRelation, + const nsAString& aTargets, + bool mIgnoreCase, + bool mNegate, + bool aIsMultiple); + + nsTemplateCondition(const nsAString& aSource, + const nsAString& aRelation, + nsIAtom* aTargetVariable, + bool mIgnoreCase, + bool mNegate); + + ~nsTemplateCondition() { MOZ_COUNT_DTOR(nsTemplateCondition); } + + nsTemplateCondition* GetNext() { return mNext; } + void SetNext(nsTemplateCondition* aNext) { mNext = aNext; } + + void SetRelation(const nsAString& aRelation); + + bool + CheckMatch(nsIXULTemplateResult* aResult); + + bool + CheckMatchStrings(const nsAString& aLeftString, + const nsAString& aRightString); +protected: + + nsCOMPtr<nsIAtom> mSourceVariable; + nsString mSource; + ConditionRelation mRelation; + nsCOMPtr<nsIAtom> mTargetVariable; + nsTArray<nsString> mTargetList; + bool mIgnoreCase; + bool mNegate; + + nsTemplateCondition* mNext; +}; + +/** + * A rule consists of: + * + * - Conditions, a set of unbound variables with consistency + * constraints that specify the values that each variable can + * assume. The conditions must be completely and consistently + * "bound" for the rule to be considered "matched". + * + * - Bindings, a set of unbound variables with consistency constraints + * that specify the values that each variable can assume. Unlike the + * conditions, the bindings need not be bound for the rule to be + * considered matched. + * + * - Content that should be constructed when the rule is "activated". + * + */ +class nsTemplateRule +{ +public: + nsTemplateRule(nsIContent* aRuleNode, + nsIContent* aAction, + nsTemplateQuerySet* aQuerySet); + /** + * The copy-constructor should only be called from nsTArray when appending + * a new rule, otherwise things break because the copy constructor expects + * mBindings and mConditions to be nullptr. + */ + nsTemplateRule(const nsTemplateRule& aOtherRule); + + ~nsTemplateRule(); + + /** + * Return the <action> node that this rule was constructed from, or its + * logical equivalent for shorthand syntaxes. That is, the parent node of + * the content that should be generated for this rule. + */ + nsIContent* GetAction() const { return mAction; } + + /** + * Return the <rule> content node that this rule was constructed from. + * @param aResult an out parameter, which will contain the rule node + * @return NS_OK if no errors occur. + */ + nsresult GetRuleNode(nsIDOMNode** aResult) const; + + void SetVars(nsIAtom* aRefVariable, nsIAtom* aMemberVariable) + { + mRefVariable = aRefVariable; + mMemberVariable = aMemberVariable; + } + + void SetRuleFilter(nsIXULTemplateRuleFilter* aRuleFilter) + { + mRuleFilter = aRuleFilter; + } + + nsIAtom* GetTag() { return mTag; } + void SetTag(nsIAtom* aTag) { mTag = aTag; } + + nsIAtom* GetMemberVariable() { return mMemberVariable; } + + /** + * Set the first condition for the rule. Other conditions are linked + * to it using the condition's SetNext method. + */ + void SetCondition(nsTemplateCondition* aConditions); + + /** + * Check if the result matches the rule by first looking at the conditions. + * If the results is accepted by the conditions, the rule filter, if any + * was set, is checked. If either check rejects a result, a match cannot + * occur for this rule and result. + */ + bool + CheckMatch(nsIXULTemplateResult* aResult) const; + + /** + * Determine if the rule has the specified binding + */ + bool + HasBinding(nsIAtom* aSourceVariable, + nsAString& aExpr, + nsIAtom* aTargetVariable) const; + + /** + * Add a binding to the rule. A binding consists of an already-bound + * source variable, and the RDF property that should be tested to + * generate a target value. The target value is bound to a target + * variable. + * + * @param aSourceVariable the source variable that will be used in + * the RDF query. + * @param aExpr the expression that will be used in the query. + * @param aTargetVariable the variable whose value will be bound + * to the RDF node that is returned when querying the binding + * @return NS_OK if no errors occur. + */ + nsresult AddBinding(nsIAtom* aSourceVariable, + nsAString& aExpr, + nsIAtom* aTargetVariable); + + /** + * Inform the query processor of the bindings that are set for a rule. + * This should be called after all the bindings for a rule are compiled. + */ + nsresult + AddBindingsToQueryProcessor(nsIXULTemplateQueryProcessor* aProcessor); + + void Traverse(nsCycleCollectionTraversalCallback &cb) const + { + cb.NoteXPCOMChild(mRuleNode); + cb.NoteXPCOMChild(mAction); + } + +protected: + + struct Binding { + nsCOMPtr<nsIAtom> mSourceVariable; + nsCOMPtr<nsIAtom> mTargetVariable; + nsString mExpr; + Binding* mNext; + Binding* mParent; + }; + + // backreference to the query set which owns this rule + nsTemplateQuerySet* mQuerySet; + + // the <rule> node, or the <template> node if there is no <rule> + nsCOMPtr<nsIDOMNode> mRuleNode; + + // the <action> node, or, if there is no <action>, the container node + // which contains the content to generate + nsCOMPtr<nsIContent> mAction; + + // the rule filter set by the builder's SetRuleFilter function + nsCOMPtr<nsIXULTemplateRuleFilter> mRuleFilter; + + // indicates that the rule will only match when generating content + // to be inserted into a container with this tag + nsCOMPtr<nsIAtom> mTag; + + // linked-list of the bindings for the rule, owned by the rule. + Binding* mBindings; + + nsCOMPtr<nsIAtom> mRefVariable; + nsCOMPtr<nsIAtom> mMemberVariable; + + nsTemplateCondition* mConditions; // owned by nsTemplateRule +}; + +/** nsTemplateQuerySet + * + * A single <queryset> which holds the query node and the rules for it. + * All builders have at least one queryset, which may be created with an + * explicit <queryset> tag or implied if the tag is not used. + * + * These queryset objects are created and owned by the builder in its + * mQuerySets array. + */ +class nsTemplateQuerySet +{ +protected: + nsTArray<nsTemplateRule> mRules; + + // a number which increments for each successive queryset. It is stored so + // it can be used as an optimization when updating results so that it is + // known where to insert them into a match. + int32_t mPriority; + +public: + + // <query> node + nsCOMPtr<nsIContent> mQueryNode; + + // compiled opaque query object returned by the query processor's + // CompileQuery call + nsCOMPtr<nsISupports> mCompiledQuery; + + // indicates that the query will only generate content to be inserted into + // a container with this tag + nsCOMPtr<nsIAtom> mTag; + + explicit nsTemplateQuerySet(int32_t aPriority) + : mPriority(aPriority) + { + MOZ_COUNT_CTOR(nsTemplateQuerySet); + } + + ~nsTemplateQuerySet() + { + MOZ_COUNT_DTOR(nsTemplateQuerySet); + } + + int32_t Priority() const + { + return mPriority; + } + + nsIAtom* GetTag() { return mTag; } + void SetTag(nsIAtom* aTag) { mTag = aTag; } + + nsTemplateRule* NewRule(nsIContent* aRuleNode, + nsIContent* aAction, + nsTemplateQuerySet* aQuerySet) + { + // nsTemplateMatch stores the index as a 16-bit value, + // so check to make sure for overflow + if (mRules.Length() == INT16_MAX) + return nullptr; + + return mRules.AppendElement(nsTemplateRule(aRuleNode, aAction, + aQuerySet)); + } + + void RemoveRule(nsTemplateRule *aRule) + { + mRules.RemoveElementAt(aRule - mRules.Elements()); + } + + int16_t RuleCount() const + { + return mRules.Length(); + } + + nsTemplateRule* GetRuleAt(int16_t aIndex) + { + if (uint32_t(aIndex) < mRules.Length()) { + return &mRules[aIndex]; + } + return nullptr; + } + + void Clear() + { + mRules.Clear(); + } +}; + +#endif // nsTemplateRule_h__ diff --git a/dom/xul/templates/nsTreeRows.cpp b/dom/xul/templates/nsTreeRows.cpp new file mode 100644 index 000000000..b77a97213 --- /dev/null +++ b/dom/xul/templates/nsTreeRows.cpp @@ -0,0 +1,482 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsString.h" +#include "nsTreeRows.h" +#include <algorithm> + +nsTreeRows::Subtree* +nsTreeRows::EnsureSubtreeFor(Subtree* aParent, + int32_t aChildIndex) +{ + Subtree* subtree = GetSubtreeFor(aParent, aChildIndex); + + if (! subtree) { + subtree = aParent->mRows[aChildIndex].mSubtree = new Subtree(aParent); + InvalidateCachedRow(); + } + + return subtree; +} + +nsTreeRows::Subtree* +nsTreeRows::GetSubtreeFor(const Subtree* aParent, + int32_t aChildIndex, + int32_t* aSubtreeSize) +{ + NS_PRECONDITION(aParent, "no parent"); + NS_PRECONDITION(aChildIndex >= 0, "bad child index"); + + Subtree* result = nullptr; + + if (aChildIndex < aParent->mCount) + result = aParent->mRows[aChildIndex].mSubtree; + + if (aSubtreeSize) + *aSubtreeSize = result ? result->mSubtreeSize : 0; + + return result; +} + +void +nsTreeRows::RemoveSubtreeFor(Subtree* aParent, int32_t aChildIndex) +{ + NS_PRECONDITION(aParent, "no parent"); + NS_PRECONDITION(aChildIndex >= 0 && aChildIndex < aParent->mCount, "bad child index"); + + Row& row = aParent->mRows[aChildIndex]; + + if (row.mSubtree) { + int32_t subtreeSize = row.mSubtree->GetSubtreeSize(); + + delete row.mSubtree; + row.mSubtree = nullptr; + + for (Subtree* subtree = aParent; subtree != nullptr; subtree = subtree->mParent) + subtree->mSubtreeSize -= subtreeSize; + } + + InvalidateCachedRow(); +} + +nsTreeRows::iterator +nsTreeRows::First() +{ + iterator result; + result.Append(&mRoot, 0); + result.SetRowIndex(0); + return result; +} + +nsTreeRows::iterator +nsTreeRows::Last() +{ + iterator result; + + // Build up a path along the rightmost edge of the tree + Subtree* current = &mRoot; + int32_t count = current->Count(); + do { + int32_t last = count - 1; + result.Append(current, last); + current = count ? GetSubtreeFor(current, last) : nullptr; + } while (current && ((count = current->Count()) != 0)); + + // Now, at the bottom rightmost leaf, advance us one off the end. + result.GetTop().mChildIndex++; + + // Our row index will be the size of the root subree, plus one. + result.SetRowIndex(mRoot.GetSubtreeSize() + 1); + + return result; +} + +nsTreeRows::iterator +nsTreeRows::operator[](int32_t aRow) +{ + // See if we're just lucky, and end up with something + // nearby. (This tends to happen a lot due to the way that we get + // asked for rows n' stuff.) + int32_t last = mLastRow.GetRowIndex(); + if (last != -1) { + if (aRow == last) + return mLastRow; + else if (last + 1 == aRow) + return ++mLastRow; + else if (last - 1 == aRow) + return --mLastRow; + } + + // Nope. Construct a path to the specified index. This is a little + // bit better than O(n), because we can skip over subtrees. (So it + // ends up being approximately linear in the subtree size, instead + // of the entire view size. But, most of the time, big views are + // flat. Oh well.) + iterator result; + Subtree* current = &mRoot; + + int32_t index = 0; + result.SetRowIndex(aRow); + + do { + int32_t subtreeSize; + Subtree* subtree = GetSubtreeFor(current, index, &subtreeSize); + + if (subtreeSize >= aRow) { + result.Append(current, index); + current = subtree; + index = 0; + --aRow; + } + else { + ++index; + aRow -= subtreeSize + 1; + } + } while (aRow >= 0); + + mLastRow = result; + return result; +} + +nsTreeRows::iterator +nsTreeRows::FindByResource(nsIRDFResource* aResource) +{ + // XXX Mmm, scan through the rows one-by-one... + iterator last = Last(); + iterator iter; + + nsresult rv; + nsAutoString resourceid; + bool stringmode = false; + + for (iter = First(); iter != last; ++iter) { + if (!stringmode) { + nsCOMPtr<nsIRDFResource> findres; + rv = iter->mMatch->mResult->GetResource(getter_AddRefs(findres)); + if (NS_FAILED(rv)) return last; + + if (findres == aResource) + break; + + if (! findres) { + const char *uri; + aResource->GetValueConst(&uri); + CopyUTF8toUTF16(uri, resourceid); + + // set stringmode and fall through + stringmode = true; + } + } + + // additional check because previous block could change stringmode + if (stringmode) { + nsAutoString findid; + rv = iter->mMatch->mResult->GetId(findid); + if (NS_FAILED(rv)) return last; + + if (resourceid.Equals(findid)) + break; + } + } + + return iter; +} + +nsTreeRows::iterator +nsTreeRows::Find(nsIXULTemplateResult *aResult) +{ + // XXX Mmm, scan through the rows one-by-one... + iterator last = Last(); + iterator iter; + + for (iter = First(); iter != last; ++iter) { + if (aResult == iter->mMatch->mResult) + break; + } + + return iter; +} + +void +nsTreeRows::Clear() +{ + mRoot.Clear(); + InvalidateCachedRow(); +} + +//---------------------------------------------------------------------- +// +// nsTreeRows::Subtree +// + +nsTreeRows::Subtree::~Subtree() +{ + Clear(); +} + +void +nsTreeRows::Subtree::Clear() +{ + for (int32_t i = mCount - 1; i >= 0; --i) + delete mRows[i].mSubtree; + + delete[] mRows; + + mRows = nullptr; + mCount = mCapacity = mSubtreeSize = 0; +} + +nsTreeRows::iterator +nsTreeRows::Subtree::InsertRowAt(nsTemplateMatch* aMatch, int32_t aIndex) +{ + if (mCount >= mCapacity || aIndex >= mCapacity) { + int32_t newCapacity = std::max(mCapacity * 2, aIndex + 1); + Row* newRows = new Row[newCapacity]; + if (! newRows) + return iterator(); + + for (int32_t i = mCount - 1; i >= 0; --i) + newRows[i] = mRows[i]; + + delete[] mRows; + + mRows = newRows; + mCapacity = newCapacity; + } + + for (int32_t i = mCount - 1; i >= aIndex; --i) + mRows[i + 1] = mRows[i]; + + mRows[aIndex].mMatch = aMatch; + mRows[aIndex].mContainerType = eContainerType_Unknown; + mRows[aIndex].mContainerState = eContainerState_Unknown; + mRows[aIndex].mContainerFill = eContainerFill_Unknown; + mRows[aIndex].mSubtree = nullptr; + ++mCount; + + // Now build an iterator that points to the newly inserted element. + int32_t rowIndex = 0; + iterator result; + result.Push(this, aIndex); + + for ( ; --aIndex >= 0; ++rowIndex) { + // Account for open subtrees in the absolute row index. + const Subtree *subtree = mRows[aIndex].mSubtree; + if (subtree) + rowIndex += subtree->mSubtreeSize; + } + + Subtree *subtree = this; + do { + // Note that the subtree's size has expanded. + ++subtree->mSubtreeSize; + + Subtree *parent = subtree->mParent; + if (! parent) + break; + + // Account for open subtrees in the absolute row index. + int32_t count = parent->Count(); + for (aIndex = 0; aIndex < count; ++aIndex, ++rowIndex) { + const Subtree *child = (*parent)[aIndex].mSubtree; + if (subtree == child) + break; + + if (child) + rowIndex += child->mSubtreeSize; + } + + NS_ASSERTION(aIndex < count, "couldn't find subtree in parent"); + + result.Push(parent, aIndex); + subtree = parent; + ++rowIndex; // One for the parent row. + } while (1); + + result.SetRowIndex(rowIndex); + return result; +} + +void +nsTreeRows::Subtree::RemoveRowAt(int32_t aIndex) +{ + NS_PRECONDITION(aIndex >= 0 && aIndex < Count(), "bad index"); + if (aIndex < 0 || aIndex >= Count()) + return; + + // How big is the subtree we're going to be removing? + int32_t subtreeSize = mRows[aIndex].mSubtree + ? mRows[aIndex].mSubtree->GetSubtreeSize() + : 0; + + ++subtreeSize; + + delete mRows[aIndex].mSubtree; + + for (int32_t i = aIndex + 1; i < mCount; ++i) + mRows[i - 1] = mRows[i]; + + --mCount; + + for (Subtree* subtree = this; subtree != nullptr; subtree = subtree->mParent) + subtree->mSubtreeSize -= subtreeSize; +} + +//---------------------------------------------------------------------- +// +// nsTreeRows::iterator +// + +nsTreeRows::iterator::iterator(const iterator& aIterator) + : mRowIndex(aIterator.mRowIndex), + mLink(aIterator.mLink) +{ +} + +nsTreeRows::iterator& +nsTreeRows::iterator::operator=(const iterator& aIterator) +{ + mRowIndex = aIterator.mRowIndex; + mLink = aIterator.mLink; + return *this; +} + +void +nsTreeRows::iterator::Append(Subtree* aParent, int32_t aChildIndex) +{ + Link *link = mLink.AppendElement(); + if (link) { + link->mParent = aParent; + link->mChildIndex = aChildIndex; + } + else + NS_ERROR("out of memory"); +} + +void +nsTreeRows::iterator::Push(Subtree *aParent, int32_t aChildIndex) +{ + Link *link = mLink.InsertElementAt(0); + if (link) { + link->mParent = aParent; + link->mChildIndex = aChildIndex; + } + else + NS_ERROR("out of memory"); +} + +bool +nsTreeRows::iterator::operator==(const iterator& aIterator) const +{ + if (GetDepth() != aIterator.GetDepth()) + return false; + + if (GetDepth() == 0) + return true; + + return GetTop() == aIterator.GetTop(); +} + +void +nsTreeRows::iterator::Next() +{ + NS_PRECONDITION(GetDepth() > 0, "cannot increment an uninitialized iterator"); + + // Increment the absolute row index + ++mRowIndex; + + Link& top = GetTop(); + + // Is there a child subtree? If so, descend into the child + // subtree. + Subtree* subtree = top.GetRow().mSubtree; + + if (subtree && subtree->Count()) { + Append(subtree, 0); + return; + } + + // Have we exhausted the current subtree? + if (top.mChildIndex >= top.mParent->Count() - 1) { + // Yep. See if we've just iterated path the last element in + // the tree, period. Walk back up the stack, looking for any + // unfinished subtrees. + int32_t unfinished; + for (unfinished = GetDepth() - 2; unfinished >= 0; --unfinished) { + const Link& link = mLink[unfinished]; + if (link.mChildIndex < link.mParent->Count() - 1) + break; + } + + // If there are no unfinished subtrees in the stack, then this + // iterator is exhausted. Leave it in the same state that + // Last() does. + if (unfinished < 0) { + top.mChildIndex++; + return; + } + + // Otherwise, we ran off the end of one of the inner + // subtrees. Pop up to the next unfinished level in the stack. + mLink.SetLength(unfinished + 1); + } + + // Advance to the next child in this subtree + ++(GetTop().mChildIndex); +} + +void +nsTreeRows::iterator::Prev() +{ + NS_PRECONDITION(GetDepth() > 0, "cannot increment an uninitialized iterator"); + + // Decrement the absolute row index + --mRowIndex; + + // Move to the previous child in this subtree + --(GetTop().mChildIndex); + + // Have we exhausted the current subtree? + if (GetTop().mChildIndex < 0) { + // Yep. See if we've just iterated back to the first element + // in the tree, period. Walk back up the stack, looking for + // any unfinished subtrees. + int32_t unfinished; + for (unfinished = GetDepth() - 2; unfinished >= 0; --unfinished) { + const Link& link = mLink[unfinished]; + if (link.mChildIndex >= 0) + break; + } + + // If there are no unfinished subtrees in the stack, then this + // iterator is exhausted. Leave it in the same state that + // First() does. + if (unfinished < 0) + return; + + // Otherwise, we ran off the end of one of the inner + // subtrees. Pop up to the next unfinished level in the stack. + mLink.SetLength(unfinished + 1); + return; + } + + // Is there a child subtree immediately prior to our current + // position? If so, descend into it, grovelling down to the + // deepest, rightmost left edge. + Subtree* parent = GetTop().GetParent(); + int32_t index = GetTop().GetChildIndex(); + + Subtree* subtree = (*parent)[index].mSubtree; + + if (subtree && subtree->Count()) { + do { + index = subtree->Count() - 1; + Append(subtree, index); + + parent = subtree; + subtree = (*parent)[index].mSubtree; + } while (subtree && subtree->Count()); + } +} diff --git a/dom/xul/templates/nsTreeRows.h b/dom/xul/templates/nsTreeRows.h new file mode 100644 index 000000000..801af0226 --- /dev/null +++ b/dom/xul/templates/nsTreeRows.h @@ -0,0 +1,437 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsTreeRows_h__ +#define nsTreeRows_h__ + +#include "nsCOMPtr.h" +#include "nsTArray.h" +#include "PLDHashTable.h" +#include "nsIXULTemplateResult.h" +#include "nsTemplateMatch.h" +#include "nsIRDFResource.h" + + +/** + * This class maintains the state of the XUL tree builder's + * rows. It maps a row number to the nsTemplateMatch object that + * populates the row. + */ +class nsTreeRows +{ +public: + class iterator; + friend class iterator; + + enum Direction { eDirection_Forwards = +1, eDirection_Backwards = -1 }; + + enum ContainerType { + eContainerType_Unknown = 0, + eContainerType_Noncontainer = 1, + eContainerType_Container = 2 + }; + + enum ContainerState { + eContainerState_Unknown = 0, + eContainerState_Open = 1, + eContainerState_Closed = 2 + }; + + enum ContainerFill { + eContainerFill_Unknown = 0, + eContainerFill_Empty = 1, + eContainerFill_Nonempty = 2 + }; + + class Subtree; + + /** + * A row in the tree. Contains the match that the row + * corresponds to, and a pointer to the row's subtree, if there + * are any. + */ + struct Row { + nsTemplateMatch* mMatch; + ContainerType mContainerType : 4; + ContainerState mContainerState : 4; + ContainerFill mContainerFill : 4; + + Subtree* mSubtree; // XXX eventually move to hashtable + }; + + /** + * A subtree in the tree. A subtree contains rows, which may + * contain other subtrees. + */ + class Subtree { + protected: + friend class nsTreeRows; // so that it can access members, for now + + /** + * The parent subtree; null if we're the root + */ + Subtree* mParent; + + /** + * The number of immediate children in this subtree + */ + int32_t mCount; + + /** + * The capacity of the subtree + */ + int32_t mCapacity; + + /** + * The total number of rows in this subtree, recursively + * including child subtrees. + */ + int32_t mSubtreeSize; + + /** + * The array of rows in the subtree + */ + Row* mRows; + + public: + /** + * Creates a subtree with the specified parent. + */ + explicit Subtree(Subtree* aParent) + : mParent(aParent), + mCount(0), + mCapacity(0), + mSubtreeSize(0), + mRows(nullptr) {} + + ~Subtree(); + + /** + * Return the number of immediate child rows in the subtree + */ + int32_t Count() const { return mCount; } + + /** + * Return the number of rows in this subtree, as well as all + * the subtrees it contains. + */ + int32_t GetSubtreeSize() const { return mSubtreeSize; } + + /** + * Retrieve the immediate child row at the specified index. + */ + const Row& operator[](int32_t aIndex) const { + NS_PRECONDITION(aIndex >= 0 && aIndex < mCount, "bad index"); + return mRows[aIndex]; } + + /** + * Retrieve the immediate row at the specified index. + */ + Row& operator[](int32_t aIndex) { + NS_PRECONDITION(aIndex >= 0 && aIndex < mCount, "bad index"); + return mRows[aIndex]; } + + /** + * Remove all rows from the subtree. + */ + void Clear(); + + protected: + /** + * Insert an immediate child row at the specified index. + */ + iterator InsertRowAt(nsTemplateMatch* aMatch, int32_t aIndex); + + /** + * Remove an immediate child row from the specified index. + */ + void RemoveRowAt(int32_t aChildIndex); + }; + + friend class Subtree; + +protected: + /** + * A link in the path through the view's tree. + */ + struct Link { + Subtree* mParent; + int32_t mChildIndex; + + Link& + operator=(const Link& aLink) { + mParent = aLink.mParent; + mChildIndex = aLink.mChildIndex; + return *this; } + + bool + operator==(const Link& aLink) const { + return (mParent == aLink.mParent) + && (mChildIndex == aLink.mChildIndex); } + + Subtree* GetParent() { return mParent; } + const Subtree* GetParent() const { return mParent; } + + int32_t GetChildIndex() const { return mChildIndex; } + + Row& GetRow() { return (*mParent)[mChildIndex]; } + const Row& GetRow() const { return (*mParent)[mChildIndex]; } + }; + +public: + /** + * An iterator that can be used to traverse the tree view. + */ + class iterator { + protected: + int32_t mRowIndex; + AutoTArray<Link, 8> mLink; + + void Next(); + void Prev(); + + friend class Subtree; // so InsertRowAt can initialize us + friend class nsTreeRows; // so nsTreeRows can initialize us + + /** + * Used by operator[]() to initialize an iterator. + */ + void Append(Subtree* aParent, int32_t aChildIndex); + + /** + * Used by InsertRowAt() to initialize an iterator. + */ + void Push(Subtree *aParent, int32_t aChildIndex); + + /** + * Used by operator[]() and InsertRowAt() to initialize an iterator. + */ + void SetRowIndex(int32_t aRowIndex) { mRowIndex = aRowIndex; } + + /** + * Handy accessors to the top element. + */ + Link& GetTop() { return mLink[mLink.Length() - 1]; } + const Link& GetTop() const { return mLink[mLink.Length() - 1]; } + + public: + iterator() : mRowIndex(-1) {} + + iterator(const iterator& aIterator); + iterator& operator=(const iterator& aIterator); + + bool operator==(const iterator& aIterator) const; + + bool operator!=(const iterator& aIterator) const { + return !aIterator.operator==(*this); } + + const Row& operator*() const { return GetTop().GetRow(); } + Row& operator*() { return GetTop().GetRow(); } + + const Row* operator->() const { return &(GetTop().GetRow()); } + Row* operator->() { return &(GetTop().GetRow()); } + + iterator& operator++() { Next(); return *this; } + iterator operator++(int) { iterator temp(*this); Next(); return temp; } + iterator& operator--() { Prev(); return *this; } + iterator operator--(int) { iterator temp(*this); Prev(); return temp; } + + /** + * Return the current parent link + */ + Subtree* GetParent() { return GetTop().GetParent(); } + + const Subtree* GetParent() const { return GetTop().GetParent(); } + + /** + * Return the current child index + */ + int32_t GetChildIndex() const { return GetTop().GetChildIndex(); } + + /** + * Return the depth of the path the iterator is maintaining + * into the tree. + */ + int32_t GetDepth() const { return mLink.Length(); } + + /** + * Return the current row index of the iterator + */ + int32_t GetRowIndex() const { return mRowIndex; } + + /** + * Pop the iterator up a level. + */ + iterator& Pop() { mLink.SetLength(GetDepth() - 1); return *this; } + }; + + /** + * Retrieve the first element in the view + */ + iterator First(); + + /** + * Retrieve (one past) the last element in the view + */ + iterator Last(); + + /** + * Find the row that contains the given resource + */ + iterator FindByResource(nsIRDFResource* aResource); + + /** + * Find the row that contains the result + */ + iterator Find(nsIXULTemplateResult* aResult); + + /** + * Retrieve the ith element in the view + */ + iterator operator[](int32_t aIndex); + + nsTreeRows() : mRoot(nullptr) {} + ~nsTreeRows() {} + + /** + * Ensure that a child subtree exists within the specified parent + * at the specified child index within the parent. (In other + * words, create a subtree if one doesn't already exist.) + */ + Subtree* + EnsureSubtreeFor(Subtree* aParent, int32_t aChildIndex); + + /** + * Ensure that a child subtree exists at the iterator's position. + */ + Subtree* + EnsureSubtreeFor(iterator& aIterator) { + return EnsureSubtreeFor(aIterator.GetParent(), + aIterator.GetChildIndex()); } + + /** + * Get the child subtree for the specified parent at the specified + * child index. Optionally return the child subtree's size. Will + * return `null' if no subtree exists. + */ + Subtree* + GetSubtreeFor(const Subtree* aParent, + int32_t aChildIndex, + int32_t* aSubtreeSize = nullptr); + + /** + * Retrieve the size of the subtree within the specified parent. + */ + int32_t + GetSubtreeSizeFor(const Subtree* aParent, + int32_t aChildIndex) { + int32_t size; + GetSubtreeFor(aParent, aChildIndex, &size); + return size; } + + /** + * Retrieve the size of the subtree within the specified parent. + */ + int32_t + GetSubtreeSizeFor(const iterator& aIterator) { + int32_t size; + GetSubtreeFor(aIterator.GetParent(), aIterator.GetChildIndex(), &size); + return size; } + + /** + * Remove the specified subtree for a row, leaving the row itself + * intact. + */ + void + RemoveSubtreeFor(Subtree* aParent, int32_t aChildIndex); + + /** + * Remove the specified subtree for a row, leaving the row itself + * intact. + */ + void + RemoveSubtreeFor(iterator& aIterator) { + RemoveSubtreeFor(aIterator.GetParent(), aIterator.GetChildIndex()); } + + /** + * Remove the specified row from the view + */ + int32_t + RemoveRowAt(iterator& aIterator) { + iterator temp = aIterator--; + Subtree* parent = temp.GetParent(); + parent->RemoveRowAt(temp.GetChildIndex()); + InvalidateCachedRow(); + return parent->Count(); } + + /** + * Insert a new match into the view + */ + iterator + InsertRowAt(nsTemplateMatch* aMatch, Subtree* aSubtree, int32_t aChildIndex) { + InvalidateCachedRow(); + return aSubtree->InsertRowAt(aMatch, aChildIndex); } + + /** + * Raw access to the rows; e.g., for sorting. + */ + Row* + GetRowsFor(Subtree* aSubtree) { return aSubtree->mRows; } + + /** + * Remove all of the rows + */ + void Clear(); + + /** + * Return the total number of rows in the tree view. + */ + int32_t Count() const { return mRoot.GetSubtreeSize(); } + + /** + * Retrieve the root subtree + */ + Subtree* GetRoot() { return &mRoot; } + + /** + * Set the root resource for the view + */ + void SetRootResource(nsIRDFResource* aResource) { + mRootResource = aResource; } + + /** + * Retrieve the root resource for the view + */ + nsIRDFResource* GetRootResource() { + return mRootResource.get(); } + + /** + * Invalidate the cached row; e.g., because the view has changed + * in a way that would corrupt the iterator. + */ + void + InvalidateCachedRow() { mLastRow = iterator(); } + +protected: + /** + * The root subtree. + */ + Subtree mRoot; + + /** + * The root resource for the view + */ + nsCOMPtr<nsIRDFResource> mRootResource; + + /** + * The last row that was asked for by operator[]. By remembering + * this, we can usually avoid the O(n) search through the row + * array to find the row at the specified index. + */ + iterator mLastRow; +}; + + +#endif // nsTreeRows_h__ diff --git a/dom/xul/templates/nsXMLBinding.cpp b/dom/xul/templates/nsXMLBinding.cpp new file mode 100644 index 000000000..9c1965ce2 --- /dev/null +++ b/dom/xul/templates/nsXMLBinding.cpp @@ -0,0 +1,118 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsXULTemplateQueryProcessorXML.h" +#include "nsXULTemplateResultXML.h" +#include "nsXMLBinding.h" +#include "mozilla/ErrorResult.h" +#include "mozilla/dom/XPathResult.h" + +using namespace mozilla; +using namespace mozilla::dom; + +nsXMLBindingSet::~nsXMLBindingSet() +{} + +void +nsXMLBindingSet::AddBinding(nsIAtom* aVar, nsAutoPtr<XPathExpression>&& aExpr) +{ + nsAutoPtr<nsXMLBinding> newbinding(new nsXMLBinding(aVar, Move(aExpr))); + + if (mFirst) { + nsXMLBinding* binding = mFirst; + + while (binding) { + // if the target variable is already used in a binding, ignore it + // since it won't be useful for anything + if (binding->mVar == aVar) + return; + + // add the binding at the end of the list + if (!binding->mNext) { + binding->mNext = newbinding; + return; + } + + binding = binding->mNext; + } + } + else { + mFirst = newbinding; + } +} + +int32_t +nsXMLBindingSet::LookupTargetIndex(nsIAtom* aTargetVariable, + nsXMLBinding** aBinding) +{ + int32_t idx = 0; + nsXMLBinding* binding = mFirst; + + while (binding) { + if (binding->mVar == aTargetVariable) { + *aBinding = binding; + return idx; + } + idx++; + binding = binding->mNext; + } + + *aBinding = nullptr; + return -1; +} + +XPathResult* +nsXMLBindingValues::GetAssignmentFor(nsXULTemplateResultXML* aResult, + nsXMLBinding* aBinding, + int32_t aIndex, + uint16_t aType) +{ + XPathResult* value = mValues.SafeElementAt(aIndex); + if (value) { + return value; + } + + nsINode* contextNode = aResult->Node(); + if (!contextNode) { + return nullptr; + } + + mValues.EnsureLengthAtLeast(aIndex + 1); + + ErrorResult ignored; + mValues[aIndex] = aBinding->mExpr->Evaluate(*contextNode, aType, nullptr, + ignored); + + return mValues[aIndex]; +} + +nsINode* +nsXMLBindingValues::GetNodeAssignmentFor(nsXULTemplateResultXML* aResult, + nsXMLBinding* aBinding, + int32_t aIndex) +{ + XPathResult* result = GetAssignmentFor(aResult, aBinding, aIndex, + XPathResult::FIRST_ORDERED_NODE_TYPE); + + ErrorResult rv; + return result ? result->GetSingleNodeValue(rv) : nullptr; +} + +void +nsXMLBindingValues::GetStringAssignmentFor(nsXULTemplateResultXML* aResult, + nsXMLBinding* aBinding, + int32_t aIndex, + nsAString& aValue) +{ + XPathResult* result = GetAssignmentFor(aResult, aBinding, aIndex, + XPathResult::STRING_TYPE); + + if (result) { + ErrorResult rv; + result->GetStringValue(aValue, rv); + } else { + aValue.Truncate(); + } +} diff --git a/dom/xul/templates/nsXMLBinding.h b/dom/xul/templates/nsXMLBinding.h new file mode 100644 index 000000000..e72813598 --- /dev/null +++ b/dom/xul/templates/nsXMLBinding.h @@ -0,0 +1,137 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsXMLBinding_h__ +#define nsXMLBinding_h__ + +#include "nsAutoPtr.h" +#include "nsIAtom.h" +#include "mozilla/Attributes.h" +#include "mozilla/dom/XPathExpression.h" + +class nsINode; +class nsXULTemplateResultXML; +class nsXMLBindingValues; +namespace mozilla { +namespace dom { +class XPathResult; +} // namespace dom +} // namespace mozilla + +/** + * Classes related to storing bindings for XML handling. + */ + +/** + * a <binding> description + */ +struct nsXMLBinding { + nsCOMPtr<nsIAtom> mVar; + nsAutoPtr<mozilla::dom::XPathExpression> mExpr; + + nsAutoPtr<nsXMLBinding> mNext; + + nsXMLBinding(nsIAtom* aVar, nsAutoPtr<mozilla::dom::XPathExpression>&& aExpr) + : mVar(aVar), mExpr(aExpr), mNext(nullptr) + { + MOZ_COUNT_CTOR(nsXMLBinding); + } + + ~nsXMLBinding() + { + MOZ_COUNT_DTOR(nsXMLBinding); + } +}; + +/** + * a collection of <binding> descriptors. This object is refcounted by + * nsXMLBindingValues objects and the query processor. + */ +class nsXMLBindingSet final +{ + ~nsXMLBindingSet(); + +public: + // pointer to the first binding in a linked list + nsAutoPtr<nsXMLBinding> mFirst; + + NS_INLINE_DECL_REFCOUNTING(nsXMLBindingSet); + + /** + * Add a binding to the set + */ + void + AddBinding(nsIAtom* aVar, nsAutoPtr<mozilla::dom::XPathExpression>&& aExpr); + + /** + * The nsXMLBindingValues class stores an array of values, one for each + * target symbol that could be set by the bindings in the set. + * LookupTargetIndex determines the index into the array for a given + * target symbol. + */ + int32_t + LookupTargetIndex(nsIAtom* aTargetVariable, nsXMLBinding** aBinding); +}; + +/** + * a set of values of bindings. This object is used once per result. + */ +class nsXMLBindingValues +{ +protected: + + // the binding set + RefPtr<nsXMLBindingSet> mBindings; + + /** + * A set of values for variable bindings. To look up a binding value, + * scan through the binding set in mBindings for the right target atom. + * Its index will correspond to the index in this array. + */ + nsTArray<RefPtr<mozilla::dom::XPathResult> > mValues; + +public: + + nsXMLBindingValues() { MOZ_COUNT_CTOR(nsXMLBindingValues); } + ~nsXMLBindingValues() { MOZ_COUNT_DTOR(nsXMLBindingValues); } + + nsXMLBindingSet* GetBindingSet() { return mBindings; } + + void SetBindingSet(nsXMLBindingSet* aBindings) { mBindings = aBindings; } + + int32_t + LookupTargetIndex(nsIAtom* aTargetVariable, nsXMLBinding** aBinding) + { + return mBindings ? + mBindings->LookupTargetIndex(aTargetVariable, aBinding) : -1; + } + + /** + * Retrieve the assignment for a particular variable + * + * aResult the result generated from the template + * aBinding the binding looked up using LookupTargetIndex + * aIndex the index of the assignment to retrieve + * aType the type of result expected + */ + mozilla::dom::XPathResult* + GetAssignmentFor(nsXULTemplateResultXML* aResult, + nsXMLBinding* aBinding, + int32_t idx, + uint16_t type); + + nsINode* + GetNodeAssignmentFor(nsXULTemplateResultXML* aResult, + nsXMLBinding* aBinding, + int32_t idx); + + void + GetStringAssignmentFor(nsXULTemplateResultXML* aResult, + nsXMLBinding* aBinding, + int32_t idx, + nsAString& aValue); +}; + +#endif // nsXMLBinding_h__ diff --git a/dom/xul/templates/nsXULContentBuilder.cpp b/dom/xul/templates/nsXULContentBuilder.cpp new file mode 100644 index 000000000..71c285cc4 --- /dev/null +++ b/dom/xul/templates/nsXULContentBuilder.cpp @@ -0,0 +1,1976 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ArrayUtils.h" + +#include "nsContentCID.h" +#include "nsIDocument.h" +#include "nsIDOMNodeList.h" +#include "nsIDOMXULDocument.h" +#include "mozilla/dom/NodeInfo.h" +#include "nsIServiceManager.h" +#include "nsIXULDocument.h" + +#include "nsContentSupportMap.h" +#include "nsRDFConMemberTestNode.h" +#include "nsRDFPropertyTestNode.h" +#include "nsXULSortService.h" +#include "nsTemplateRule.h" +#include "nsTemplateMap.h" +#include "nsTArray.h" +#include "nsXPIDLString.h" +#include "nsGkAtoms.h" +#include "nsXULContentUtils.h" +#include "nsXULElement.h" +#include "nsXULTemplateBuilder.h" +#include "nsNodeInfoManager.h" +#include "nsContentCreatorFunctions.h" +#include "nsContentUtils.h" +#include "nsAttrName.h" +#include "nsNodeUtils.h" +#include "mozAutoDocUpdate.h" +#include "nsTextNode.h" +#include "mozilla/dom/Element.h" + +#include "PLDHashTable.h" +#include "rdf.h" + +using namespace mozilla; +using namespace mozilla::dom; + +//---------------------------------------------------------------------- +// +// Return values for EnsureElementHasGenericChild() +// +#define NS_ELEMENT_GOT_CREATED NS_RDF_NO_VALUE +#define NS_ELEMENT_WAS_THERE NS_OK + +//---------------------------------------------------------------------- +// +// nsXULContentBuilder +// + +/** + * The content builder generates DOM nodes from a template. The actual content + * generation is done entirely inside BuildContentFromTemplate. + * + * Content generation is centered around the generation node (the node with + * uri="?member" on it). Nodes above the generation node are unique and + * generated only once. BuildContentFromTemplate will be passed the unique + * flag as an argument for content at this point and will recurse until it + * finds the generation node. + * + * Once the generation node has been found, the results for that content node + * are added to the content map, stored in mContentSupportMap. + * + * If recursion is allowed, generation continues, where the generation node + * becomes the container to insert into. + */ +class nsXULContentBuilder : public nsXULTemplateBuilder +{ +public: + // nsIXULTemplateBuilder interface + NS_IMETHOD CreateContents(nsIContent* aElement, bool aForceCreation) override; + + NS_IMETHOD HasGeneratedContent(nsIRDFResource* aResource, + nsIAtom* aTag, + bool* aGenerated) override; + + NS_IMETHOD GetResultForContent(nsIDOMElement* aContent, + nsIXULTemplateResult** aResult) override; + + // nsIMutationObserver interface + NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED + NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED + +protected: + friend nsresult + NS_NewXULContentBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult); + + nsXULContentBuilder(); + + void Traverse(nsCycleCollectionTraversalCallback& aCb) const override + { + mSortState.Traverse(aCb); + } + + virtual void Uninit(bool aIsFinal) override; + + // Implementation methods + nsresult + OpenContainer(nsIContent* aElement); + + nsresult + CloseContainer(nsIContent* aElement); + + /** + * Build content from a template for a given result. This will be called + * recursively or on demand and will be called for every node in the + * generated content tree. + */ + nsresult + BuildContentFromTemplate(nsIContent *aTemplateNode, + nsIContent *aResourceNode, + nsIContent *aRealNode, + bool aIsUnique, + bool aIsSelfReference, + nsIXULTemplateResult* aChild, + bool aNotify, + nsTemplateMatch* aMatch, + nsIContent** aContainer, + int32_t* aNewIndexInContainer); + + /** + * Copy the attributes from the template node to the node generated + * from it, performing any substitutions. + * + * @param aTemplateNode node within template + * @param aRealNode generated node to set attibutes upon + * @param aResult result to look up variable->value bindings in + * @param aNotify true to notify of DOM changes + */ + nsresult + CopyAttributesToElement(nsIContent* aTemplateNode, + nsIContent* aRealNode, + nsIXULTemplateResult* aResult, + bool aNotify); + + /** + * Add any necessary persistent attributes (persist="...") from the + * local store to a generated node. + * + * @param aTemplateNode node within template + * @param aRealNode generated node to set persisted attibutes upon + * @param aResult result to look up variable->value bindings in + */ + nsresult + AddPersistentAttributes(Element* aTemplateNode, + nsIXULTemplateResult* aResult, + nsIContent* aRealNode); + + /** + * Recalculate any attributes that have variable references. This will + * be called when a binding has been changed to update the attributes. + * The attributes are copied from the node aTemplateNode in the template + * to the generated node aRealNode, using the values from the result + * aResult. This method will operate recursively. + * + * @param aTemplateNode node within template + * @param aRealNode generated node to set attibutes upon + * @param aResult result to look up variable->value bindings in + */ + nsresult + SynchronizeUsingTemplate(nsIContent *aTemplateNode, + nsIContent* aRealNode, + nsIXULTemplateResult* aResult); + + /** + * Remove the generated node aContent from the DOM and the hashtables + * used by the content builder. + */ + nsresult + RemoveMember(nsIContent* aContent); + + /** + * Create the appropriate generated content for aElement, by calling + * CreateContainerContents. + * + * @param aElement element to generate content inside + * @param aForceCreation true to force creation for closed items such as menus + */ + nsresult + CreateTemplateAndContainerContents(nsIContent* aElement, + bool aForceCreation); + + /** + * Generate the results for a template, by calling + * CreateContainerContentsForQuerySet for each queryset. + * + * @param aElement element to generate content inside + * @param aResult reference point for query + * @param aForceCreation true to force creation for closed items such as menus + * @param aNotify true to notify of DOM changes as each element is inserted + * @param aNotifyAtEnd notify at the end of all DOM changes + */ + nsresult + CreateContainerContents(nsIContent* aElement, + nsIXULTemplateResult* aResult, + bool aForceCreation, + bool aNotify, + bool aNotifyAtEnd); + + /** + * Generate the results for a query. + * + * @param aElement element to generate content inside + * @param aResult reference point for query + * @param aNotify true to notify of DOM changes + * @param aContainer container content was added inside + * @param aNewIndexInContainer index with container in which content was added + */ + nsresult + CreateContainerContentsForQuerySet(nsIContent* aElement, + nsIXULTemplateResult* aResult, + bool aNotify, + nsTemplateQuerySet* aQuerySet, + nsIContent** aContainer, + int32_t* aNewIndexInContainer); + + /** + * Check if an element with a particular tag exists with a container. + * If it is not present, append a new element with that tag into the + * container. + * + * @param aParent parent container + * @param aNameSpaceID namespace of tag to locate or create + * @param aTag tag to locate or create + * @param aNotify true to notify of DOM changes + * @param aResult set to the found or created node. + */ + nsresult + EnsureElementHasGenericChild(nsIContent* aParent, + int32_t aNameSpaceID, + nsIAtom* aTag, + bool aNotify, + nsIContent** aResult); + + bool + IsOpen(nsIContent* aElement); + + nsresult + RemoveGeneratedContent(nsIContent* aElement); + + nsresult + GetElementsForResult(nsIXULTemplateResult* aResult, + nsCOMArray<nsIContent>& aElements); + + nsresult + CreateElement(int32_t aNameSpaceID, + nsIAtom* aTag, + Element** aResult); + + /** + * Set the container and empty attributes on a node. If + * aIgnoreNonContainers is true, then the element is not changed + * for non-containers. Otherwise, the container attribute will be set to + * false. + * + * @param aElement element to set attributes on + * @param aResult result to use to determine state of attributes + * @param aIgnoreNonContainers true to not change for non-containers + * @param aNotify true to notify of DOM changes + */ + nsresult + SetContainerAttrs(nsIContent *aElement, + nsIXULTemplateResult* aResult, + bool aIgnoreNonContainers, + bool aNotify); + + virtual nsresult + RebuildAll() override; + + // GetInsertionLocations, ReplaceMatch and SynchronizeResult are inherited + // from nsXULTemplateBuilder + + /** + * Return true if the result can be inserted into the template as + * generated content. For the content builder, aLocations will be set + * to the list of containers where the content should be inserted. + */ + virtual bool + GetInsertionLocations(nsIXULTemplateResult* aOldResult, + nsCOMArray<nsIContent>** aLocations) override; + + /** + * Remove the content associated with aOldResult which no longer matches, + * and/or generate content for a new match. + */ + virtual nsresult + ReplaceMatch(nsIXULTemplateResult* aOldResult, + nsTemplateMatch* aNewMatch, + nsTemplateRule* aNewMatchRule, + void *aContext) override; + + /** + * Synchronize a result bindings with the generated content for that + * result. This will be called as a result of the template builder's + * ResultBindingChanged method. + */ + virtual nsresult + SynchronizeResult(nsIXULTemplateResult* aResult) override; + + /** + * Compare a result to a content node. If the generated content for the + * result should come before aContent, set aSortOrder to -1. If it should + * come after, set sortOrder to 1. If both are equal, set to 0. + */ + nsresult + CompareResultToNode(nsIXULTemplateResult* aResult, nsIContent* aContent, + int32_t* aSortOrder); + + /** + * Insert a generated node into the container where it should go according + * to the current sort. aNode is the generated content node and aResult is + * the result for the generated node. + */ + nsresult + InsertSortedNode(nsIContent* aContainer, + nsIContent* aNode, + nsIXULTemplateResult* aResult, + bool aNotify); + + /** + * Maintains a mapping between elements in the DOM and the matches + * that they support. + */ + nsContentSupportMap mContentSupportMap; + + /** + * Maintains a mapping from an element in the DOM to the template + * element that it was created from. + */ + nsTemplateMap mTemplateMap; + + /** + * Information about the currently active sort + */ + nsSortState mSortState; +}; + +nsresult +NS_NewXULContentBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult) +{ + NS_PRECONDITION(aOuter == nullptr, "no aggregation"); + if (aOuter) + return NS_ERROR_NO_AGGREGATION; + + nsresult rv; + nsXULContentBuilder* result = new nsXULContentBuilder(); + NS_ADDREF(result); // stabilize + + rv = result->InitGlobals(); + + if (NS_SUCCEEDED(rv)) + rv = result->QueryInterface(aIID, aResult); + + NS_RELEASE(result); + return rv; +} + +nsXULContentBuilder::nsXULContentBuilder() +{ + mSortState.initialized = false; +} + +void +nsXULContentBuilder::Uninit(bool aIsFinal) +{ + if (! aIsFinal && mRoot) { + nsresult rv = RemoveGeneratedContent(mRoot); + if (NS_FAILED(rv)) + return; + } + + // Nuke the content support map completely. + mContentSupportMap.Clear(); + mTemplateMap.Clear(); + + mSortState.initialized = false; + + nsXULTemplateBuilder::Uninit(aIsFinal); +} + +nsresult +nsXULContentBuilder::BuildContentFromTemplate(nsIContent *aTemplateNode, + nsIContent *aResourceNode, + nsIContent *aRealNode, + bool aIsUnique, + bool aIsSelfReference, + nsIXULTemplateResult* aChild, + bool aNotify, + nsTemplateMatch* aMatch, + nsIContent** aContainer, + int32_t* aNewIndexInContainer) +{ + // This is the mother lode. Here is where we grovel through an + // element in the template, copying children from the template + // into the "real" content tree, performing substitution as we go + // by looking stuff up using the results. + // + // |aTemplateNode| is the element in the "template tree", whose + // children we will duplicate and move into the "real" content + // tree. + // + // |aResourceNode| is the element in the "real" content tree that + // has the "id" attribute set to an result's id. This is + // not directly used here, but rather passed down to the XUL + // sort service to perform container-level sort. + // + // |aRealNode| is the element in the "real" content tree to which + // the new elements will be copied. + // + // |aIsUnique| is set to "true" so long as content has been + // "unique" (or "above" the resource element) so far in the + // template. + // + // |aIsSelfReference| should be set to "true" for cases where + // the reference and member variables are the same, indicating + // that the generated node is the same as the reference point, + // so generation should not recurse, or else an infinite loop + // would occur. + // + // |aChild| is the result for which we are building content. + // + // |aNotify| is set to "true" if content should be constructed + // "noisily"; that is, whether the document observers should be + // notified when new content is added to the content model. + // + // |aContainer| is an out parameter that will be set to the first + // container element in the "real" content tree to which content + // was appended. + // + // |aNewIndexInContainer| is an out parameter that will be set to + // the index in aContainer at which new content is first + // constructed. + // + // If |aNotify| is "false", then |aContainer| and + // |aNewIndexInContainer| are used to determine where in the + // content model new content is constructed. This allows a single + // notification to be propagated to document observers. + // + + nsresult rv; + + if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) { + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + ("nsXULContentBuilder::BuildContentFromTemplate (is unique: %d)", + aIsUnique)); + + nsAutoString id; + aChild->GetId(id); + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + ("Tags: [Template: %s Resource: %s Real: %s] for id %s", + nsAtomCString(aTemplateNode->NodeInfo()->NameAtom()).get(), + nsAtomCString(aResourceNode->NodeInfo()->NameAtom()).get(), + nsAtomCString(aRealNode->NodeInfo()->NameAtom()).get(), NS_ConvertUTF16toUTF8(id).get())); + } + + // Iterate through all of the template children, constructing + // "real" content model nodes for each "template" child. + for (nsIContent* tmplKid = aTemplateNode->GetFirstChild(); + tmplKid; + tmplKid = tmplKid->GetNextSibling()) { + + int32_t nameSpaceID = tmplKid->GetNameSpaceID(); + + // Check whether this element is the generation element. The generation + // element is the element that is cookie-cutter copied once for each + // different result specified by |aChild|. + // + // Nodes that appear -above- the generation element + // (that is, are ancestors of the generation element in the + // content model) are unique across all values of |aChild|, + // and are created only once. + // + // Nodes that appear -below- the generation element (that is, + // are descendants of the generation element in the content + // model), are cookie-cutter copied for each distinct value of + // |aChild|. + // + // For example, in a <tree> template: + // + // <tree> + // <template> + // <treechildren> [1] + // <treeitem uri="rdf:*"> [2] + // <treerow> [3] + // <treecell value="rdf:urn:foo" /> [4] + // <treecell value="rdf:urn:bar" /> [5] + // </treerow> + // </treeitem> + // </treechildren> + // </template> + // </tree> + // + // The <treeitem> element [2] is the generation element. This + // element, and all of its descendants ([3], [4], and [5]) + // will be duplicated for each different |aChild|. + // It's ancestor <treechildren> [1] is unique, and + // will only be created -once-, no matter how many <treeitem>s + // are created below it. + // + // isUnique will be true for nodes above the generation element, + // isGenerationElement will be true for the generation element, + // and both will be false for descendants + bool isGenerationElement = false; + bool isUnique = aIsUnique; + + // We identify the resource element by presence of a + // "uri='rdf:*'" attribute. (We also support the older + // "uri='...'" syntax.) + if (tmplKid->HasAttr(kNameSpaceID_None, nsGkAtoms::uri) && aMatch->IsActive()) { + isGenerationElement = true; + isUnique = false; + } + + MOZ_ASSERT_IF(isGenerationElement, tmplKid->IsElement()); + + nsIAtom *tag = tmplKid->NodeInfo()->NameAtom(); + + if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) { + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + ("xultemplate[%p] building %s %s %s", + this, nsAtomCString(tag).get(), + (isGenerationElement ? "[resource]" : ""), + (isUnique ? "[unique]" : ""))); + } + + // Set to true if the child we're trying to create now + // already existed in the content model. + bool realKidAlreadyExisted = false; + + nsCOMPtr<nsIContent> realKid; + if (isUnique) { + // The content is "unique"; that is, we haven't descended + // far enough into the template to hit the generation + // element yet. |EnsureElementHasGenericChild()| will + // conditionally create the element iff it isn't there + // already. + rv = EnsureElementHasGenericChild(aRealNode, nameSpaceID, tag, aNotify, getter_AddRefs(realKid)); + if (NS_FAILED(rv)) + return rv; + + if (rv == NS_ELEMENT_WAS_THERE) { + realKidAlreadyExisted = true; + } + else { + // Potentially remember the index of this element as the first + // element that we've generated. Note that we remember + // this -before- we recurse! + if (aContainer && !*aContainer) { + *aContainer = aRealNode; + NS_ADDREF(*aContainer); + + uint32_t indx = aRealNode->GetChildCount(); + + // Since EnsureElementHasGenericChild() added us, make + // sure to subtract one for our real index. + *aNewIndexInContainer = indx - 1; + } + } + + // Recurse until we get to the resource element. Since + // -we're- unique, assume that our child will be + // unique. The check for the "resource" element at the top + // of the function will trip this to |false| as soon as we + // encounter it. + rv = BuildContentFromTemplate(tmplKid, aResourceNode, realKid, true, + aIsSelfReference, aChild, aNotify, aMatch, + aContainer, aNewIndexInContainer); + + if (NS_FAILED(rv)) + return rv; + } + else if (isGenerationElement) { + // It's the "resource" element. Create a new element using + // the namespace ID and tag from the template element. + nsCOMPtr<Element> element; + rv = CreateElement(nameSpaceID, tag, getter_AddRefs(element)); + if (NS_FAILED(rv)) + return rv; + realKid = element.forget(); + + // Add the resource element to the content support map so + // we can remove the match based on the content node later. + mContentSupportMap.Put(realKid, aMatch); + + // Assign the element an 'id' attribute using result's id + nsAutoString id; + rv = aChild->GetId(id); + if (NS_FAILED(rv)) + return rv; + + rv = realKid->SetAttr(kNameSpaceID_None, nsGkAtoms::id, id, false); + if (NS_FAILED(rv)) + return rv; + + // Set up the element's 'container' and 'empty' attributes. + SetContainerAttrs(realKid, aChild, true, false); + } + else if (tag == nsGkAtoms::textnode && + nameSpaceID == kNameSpaceID_XUL) { + // <xul:text value="..."> is replaced by text of the + // actual value of the 'rdf:resource' attribute for the + // given node. + // SynchronizeUsingTemplate contains code used to update textnodes, + // so make sure to modify both when changing this + char16_t attrbuf[128]; + nsFixedString attrValue(attrbuf, ArrayLength(attrbuf), 0); + tmplKid->GetAttr(kNameSpaceID_None, nsGkAtoms::value, attrValue); + if (!attrValue.IsEmpty()) { + nsAutoString value; + rv = SubstituteText(aChild, attrValue, value); + if (NS_FAILED(rv)) return rv; + + RefPtr<nsTextNode> content = + new nsTextNode(mRoot->NodeInfo()->NodeInfoManager()); + + content->SetText(value, false); + + rv = aRealNode->AppendChildTo(content, aNotify); + if (NS_FAILED(rv)) return rv; + + // XXX Don't bother remembering text nodes as the + // first element we've generated? + } + } + else if (tmplKid->IsNodeOfType(nsINode::eTEXT)) { + nsCOMPtr<nsIDOMNode> tmplTextNode = do_QueryInterface(tmplKid); + if (!tmplTextNode) { + NS_ERROR("textnode not implementing nsIDOMNode??"); + return NS_ERROR_FAILURE; + } + nsCOMPtr<nsIDOMNode> clonedNode; + tmplTextNode->CloneNode(false, 1, getter_AddRefs(clonedNode)); + nsCOMPtr<nsIContent> clonedContent = do_QueryInterface(clonedNode); + if (!clonedContent) { + NS_ERROR("failed to clone textnode"); + return NS_ERROR_FAILURE; + } + rv = aRealNode->AppendChildTo(clonedContent, aNotify); + if (NS_FAILED(rv)) return rv; + } + else { + // It's just a generic element. Create it! + nsCOMPtr<Element> element; + rv = CreateElement(nameSpaceID, tag, getter_AddRefs(element)); + if (NS_FAILED(rv)) return rv; + realKid = element.forget(); + } + + if (realKid && !realKidAlreadyExisted) { + // Potentially remember the index of this element as the + // first element that we've generated. + if (aContainer && !*aContainer) { + *aContainer = aRealNode; + NS_ADDREF(*aContainer); + + uint32_t indx = aRealNode->GetChildCount(); + + // Since we haven't inserted any content yet, our new + // index in the container will be the current count of + // elements in the container. + *aNewIndexInContainer = indx; + } + + // Remember the template kid from which we created the + // real kid. This allows us to sync back up with the + // template to incrementally build content. + mTemplateMap.Put(realKid, tmplKid); + + rv = CopyAttributesToElement(tmplKid, realKid, aChild, false); + if (NS_FAILED(rv)) return rv; + + // Add any persistent attributes + if (isGenerationElement) { + rv = AddPersistentAttributes(tmplKid->AsElement(), aChild, + realKid); + if (NS_FAILED(rv)) return rv; + } + + // the unique content recurses up above. Also, don't recurse if + // this is a self reference (a reference to the same resource) + // or we'll end up regenerating the same content. + if (!aIsSelfReference && !isUnique) { + // this call creates the content inside the generation node, + // for example the label below: + // <vbox uri="?"> + // <label value="?title"/> + // </vbox> + rv = BuildContentFromTemplate(tmplKid, aResourceNode, realKid, false, + false, aChild, false, aMatch, + nullptr /* don't care */, + nullptr /* don't care */); + if (NS_FAILED(rv)) return rv; + + if (isGenerationElement) { + // build the next level of children + rv = CreateContainerContents(realKid, aChild, false, + false, false); + if (NS_FAILED(rv)) return rv; + } + } + + // We'll _already_ have added the unique elements; but if + // it's -not- unique, then use the XUL sort service now to + // append the element to the content model. + if (! isUnique) { + rv = NS_ERROR_UNEXPECTED; + + if (isGenerationElement) + rv = InsertSortedNode(aRealNode, realKid, aChild, aNotify); + + if (NS_FAILED(rv)) { + rv = aRealNode->AppendChildTo(realKid, aNotify); + NS_ASSERTION(NS_SUCCEEDED(rv), "unable to insert element"); + } + } + } + } + + return NS_OK; +} + +nsresult +nsXULContentBuilder::CopyAttributesToElement(nsIContent* aTemplateNode, + nsIContent* aRealNode, + nsIXULTemplateResult* aResult, + bool aNotify) +{ + nsresult rv; + + // Copy all attributes from the template to the new element + uint32_t numAttribs = aTemplateNode->GetAttrCount(); + + for (uint32_t attr = 0; attr < numAttribs; attr++) { + const nsAttrName* name = aTemplateNode->GetAttrNameAt(attr); + int32_t attribNameSpaceID = name->NamespaceID(); + // Hold a strong reference here so that the atom doesn't go away + // during UnsetAttr. + nsCOMPtr<nsIAtom> attribName = name->LocalName(); + + // XXXndeakin ignore namespaces until bug 321182 is fixed + if (attribName != nsGkAtoms::id && attribName != nsGkAtoms::uri) { + // Create a buffer here, because there's a chance that an + // attribute in the template is going to be an RDF URI, which is + // usually longish. + char16_t attrbuf[128]; + nsFixedString attribValue(attrbuf, ArrayLength(attrbuf), 0); + aTemplateNode->GetAttr(attribNameSpaceID, attribName, attribValue); + if (!attribValue.IsEmpty()) { + nsAutoString value; + rv = SubstituteText(aResult, attribValue, value); + if (NS_FAILED(rv)) + return rv; + + // if the string is empty after substitutions, remove the + // attribute + if (!value.IsEmpty()) { + rv = aRealNode->SetAttr(attribNameSpaceID, + attribName, + name->GetPrefix(), + value, + aNotify); + } + else { + rv = aRealNode->UnsetAttr(attribNameSpaceID, + attribName, + aNotify); + } + + if (NS_FAILED(rv)) + return rv; + } + } + } + + return NS_OK; +} + +nsresult +nsXULContentBuilder::AddPersistentAttributes(Element* aTemplateNode, + nsIXULTemplateResult* aResult, + nsIContent* aRealNode) +{ + if (!mRoot) + return NS_OK; + + nsCOMPtr<nsIRDFResource> resource; + nsresult rv = GetResultResource(aResult, getter_AddRefs(resource)); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoString attribute, persist; + aTemplateNode->GetAttr(kNameSpaceID_None, nsGkAtoms::persist, persist); + + while (!persist.IsEmpty()) { + attribute.Truncate(); + + int32_t offset = persist.FindCharInSet(" ,"); + if (offset > 0) { + persist.Left(attribute, offset); + persist.Cut(0, offset + 1); + } + else { + attribute = persist; + persist.Truncate(); + } + + attribute.Trim(" "); + + if (attribute.IsEmpty()) + break; + + nsCOMPtr<nsIAtom> tag; + int32_t nameSpaceID; + + RefPtr<mozilla::dom::NodeInfo> ni = + aTemplateNode->GetExistingAttrNameFromQName(attribute); + if (ni) { + tag = ni->NameAtom(); + nameSpaceID = ni->NamespaceID(); + } + else { + tag = NS_Atomize(attribute); + NS_ENSURE_TRUE(tag, NS_ERROR_OUT_OF_MEMORY); + + nameSpaceID = kNameSpaceID_None; + } + + nsCOMPtr<nsIRDFResource> property; + rv = nsXULContentUtils::GetResource(nameSpaceID, tag, getter_AddRefs(property)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIRDFNode> target; + rv = mDB->GetTarget(resource, property, true, getter_AddRefs(target)); + NS_ENSURE_SUCCESS(rv, rv); + + if (! target) + continue; + + nsCOMPtr<nsIRDFLiteral> value = do_QueryInterface(target); + NS_ASSERTION(value != nullptr, "unable to stomach that sort of node"); + if (! value) + continue; + + const char16_t* valueStr; + rv = value->GetValueConst(&valueStr); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aRealNode->SetAttr(nameSpaceID, tag, nsDependentString(valueStr), + false); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +nsresult +nsXULContentBuilder::SynchronizeUsingTemplate(nsIContent* aTemplateNode, + nsIContent* aRealElement, + nsIXULTemplateResult* aResult) +{ + // check all attributes on the template node; if they reference a resource, + // update the equivalent attribute on the content node + nsresult rv; + rv = CopyAttributesToElement(aTemplateNode, aRealElement, aResult, true); + if (NS_FAILED(rv)) + return rv; + + uint32_t count = aTemplateNode->GetChildCount(); + + for (uint32_t loop = 0; loop < count; ++loop) { + nsIContent *tmplKid = aTemplateNode->GetChildAt(loop); + + if (! tmplKid) + break; + + nsIContent *realKid = aRealElement->GetChildAt(loop); + if (! realKid) + break; + + // check for text nodes and update them accordingly. + // This code is similar to that in BuildContentFromTemplate + if (tmplKid->NodeInfo()->Equals(nsGkAtoms::textnode, + kNameSpaceID_XUL)) { + char16_t attrbuf[128]; + nsFixedString attrValue(attrbuf, ArrayLength(attrbuf), 0); + tmplKid->GetAttr(kNameSpaceID_None, nsGkAtoms::value, attrValue); + if (!attrValue.IsEmpty()) { + nsAutoString value; + rv = SubstituteText(aResult, attrValue, value); + if (NS_FAILED(rv)) return rv; + realKid->SetText(value, true); + } + } + + rv = SynchronizeUsingTemplate(tmplKid, realKid, aResult); + if (NS_FAILED(rv)) return rv; + } + + return NS_OK; +} + +nsresult +nsXULContentBuilder::RemoveMember(nsIContent* aContent) +{ + nsCOMPtr<nsIContent> parent = aContent->GetParent(); + if (parent) { + int32_t pos = parent->IndexOf(aContent); + + NS_ASSERTION(pos >= 0, "parent doesn't think this child has an index"); + if (pos < 0) return NS_OK; + + // Note: RemoveChildAt sets |child|'s document to null so that + // it'll get knocked out of the XUL doc's resource-to-element + // map. + parent->RemoveChildAt(pos, true); + } + + // Remove from the content support map. + mContentSupportMap.Remove(aContent); + + // Remove from the template map + mTemplateMap.Remove(aContent); + + return NS_OK; +} + +nsresult +nsXULContentBuilder::CreateTemplateAndContainerContents(nsIContent* aElement, + bool aForceCreation) +{ + // Generate both 1) the template content for the current element, + // and 2) recursive subcontent (if the current element refers to a + // container result). + + MOZ_LOG(gXULTemplateLog, LogLevel::Info, + ("nsXULContentBuilder::CreateTemplateAndContainerContents start - flags: %d", + mFlags)); + + if (! mQueryProcessor) + return NS_OK; + + // for the root element, get the ref attribute and generate content + if (aElement == mRoot) { + if (! mRootResult) { + nsAutoString ref; + mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::ref, ref); + + if (! ref.IsEmpty()) { + nsresult rv = mQueryProcessor->TranslateRef(mDataSource, ref, + getter_AddRefs(mRootResult)); + if (NS_FAILED(rv)) + return rv; + } + } + + if (mRootResult) { + CreateContainerContents(aElement, mRootResult, aForceCreation, + false, true); + } + } + else if (!(mFlags & eDontRecurse)) { + // The content map will contain the generation elements (the ones that + // are given ids) and only those elements, so get the reference point + // from the corresponding match. + nsTemplateMatch *match = nullptr; + if (mContentSupportMap.Get(aElement, &match)) + CreateContainerContents(aElement, match->mResult, aForceCreation, + false, true); + } + + MOZ_LOG(gXULTemplateLog, LogLevel::Info, + ("nsXULContentBuilder::CreateTemplateAndContainerContents end")); + + return NS_OK; +} + +nsresult +nsXULContentBuilder::CreateContainerContents(nsIContent* aElement, + nsIXULTemplateResult* aResult, + bool aForceCreation, + bool aNotify, + bool aNotifyAtEnd) +{ + if (!aForceCreation && !IsOpen(aElement)) + return NS_OK; + + // don't generate children if recursion or child processing isn't allowed + if (aResult != mRootResult) { + if (mFlags & eDontRecurse) + return NS_OK; + + bool mayProcessChildren; + nsresult rv = aResult->GetMayProcessChildren(&mayProcessChildren); + if (NS_FAILED(rv) || !mayProcessChildren) + return rv; + } + + nsCOMPtr<nsIRDFResource> refResource; + GetResultResource(aResult, getter_AddRefs(refResource)); + if (! refResource) + return NS_ERROR_FAILURE; + + // Avoid re-entrant builds for the same resource. + if (IsActivated(refResource)) + return NS_OK; + + ActivationEntry entry(refResource, &mTop); + + // Compile the rules now, if they haven't been already. + if (! mQueriesCompiled) { + nsresult rv = CompileQueries(); + if (NS_FAILED(rv)) + return rv; + } + + if (mQuerySets.Length() == 0) + return NS_OK; + + // See if the element's templates contents have been generated: + // this prevents a re-entrant call from triggering another + // generation. + nsXULElement *xulcontent = nsXULElement::FromContent(aElement); + if (xulcontent) { + if (xulcontent->GetTemplateGenerated()) + return NS_OK; + + // Now mark the element's contents as being generated so that + // any re-entrant calls don't trigger an infinite recursion. + xulcontent->SetTemplateGenerated(); + } + + int32_t newIndexInContainer = -1; + nsIContent* container = nullptr; + + int32_t querySetCount = mQuerySets.Length(); + + for (int32_t r = 0; r < querySetCount; r++) { + nsTemplateQuerySet* queryset = mQuerySets[r]; + + nsIAtom* tag = queryset->GetTag(); + if (tag && tag != aElement->NodeInfo()->NameAtom()) + continue; + + CreateContainerContentsForQuerySet(aElement, aResult, aNotify, queryset, + &container, &newIndexInContainer); + } + + if (aNotifyAtEnd && container) { + MOZ_AUTO_DOC_UPDATE(container->GetUncomposedDoc(), UPDATE_CONTENT_MODEL, + true); + nsNodeUtils::ContentAppended(container, + container->GetChildAt(newIndexInContainer), + newIndexInContainer); + } + + NS_IF_RELEASE(container); + + return NS_OK; +} + +nsresult +nsXULContentBuilder::CreateContainerContentsForQuerySet(nsIContent* aElement, + nsIXULTemplateResult* aResult, + bool aNotify, + nsTemplateQuerySet* aQuerySet, + nsIContent** aContainer, + int32_t* aNewIndexInContainer) +{ + if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) { + nsAutoString id; + aResult->GetId(id); + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + ("nsXULContentBuilder::CreateContainerContentsForQuerySet start for ref %s\n", + NS_ConvertUTF16toUTF8(id).get())); + } + + if (! mQueryProcessor) + return NS_OK; + + nsCOMPtr<nsISimpleEnumerator> results; + nsresult rv = mQueryProcessor->GenerateResults(mDataSource, aResult, + aQuerySet->mCompiledQuery, + getter_AddRefs(results)); + if (NS_FAILED(rv) || !results) + return rv; + + bool hasMoreResults; + rv = results->HasMoreElements(&hasMoreResults); + + for (; NS_SUCCEEDED(rv) && hasMoreResults; + rv = results->HasMoreElements(&hasMoreResults)) { + nsCOMPtr<nsISupports> nr; + rv = results->GetNext(getter_AddRefs(nr)); + if (NS_FAILED(rv)) + return rv; + + nsCOMPtr<nsIXULTemplateResult> nextresult = do_QueryInterface(nr); + if (!nextresult) + return NS_ERROR_UNEXPECTED; + + nsCOMPtr<nsIRDFResource> resultid; + rv = GetResultResource(nextresult, getter_AddRefs(resultid)); + if (NS_FAILED(rv)) + return rv; + + if (!resultid) + continue; + + nsTemplateMatch *newmatch = + nsTemplateMatch::Create(aQuerySet->Priority(), + nextresult, aElement); + if (!newmatch) + return NS_ERROR_OUT_OF_MEMORY; + + // check if there is already an existing match. If so, a previous + // query already generated content so the match is just added to the + // end of the set of matches. + + bool generateContent = true; + + nsTemplateMatch* prevmatch = nullptr; + nsTemplateMatch* existingmatch = nullptr; + nsTemplateMatch* removematch = nullptr; + if (mMatchMap.Get(resultid, &existingmatch)){ + // check if there is an existing match that matched a rule + while (existingmatch) { + // break out once we've reached a query in the list with a + // higher priority, as the new match list is sorted by + // priority, and the new match should be inserted here + int32_t priority = existingmatch->QuerySetPriority(); + if (priority > aQuerySet->Priority()) + break; + + // skip over non-matching containers + if (existingmatch->GetContainer() == aElement) { + // if the same priority is already found, replace it. This can happen + // when a container is removed and readded + if (priority == aQuerySet->Priority()) { + removematch = existingmatch; + break; + } + + if (existingmatch->IsActive()) + generateContent = false; + } + + prevmatch = existingmatch; + existingmatch = existingmatch->mNext; + } + } + + if (removematch) { + // remove the generated content for the existing match + rv = ReplaceMatch(removematch->mResult, nullptr, nullptr, aElement); + if (NS_FAILED(rv)) + return rv; + + if (mFlags & eLoggingEnabled) + OutputMatchToLog(resultid, removematch, false); + } + + if (generateContent) { + // find the rule that matches. If none match, the content does not + // need to be generated + + int16_t ruleindex; + nsTemplateRule* matchedrule = nullptr; + rv = DetermineMatchedRule(aElement, nextresult, aQuerySet, + &matchedrule, &ruleindex); + if (NS_FAILED(rv)) { + nsTemplateMatch::Destroy(newmatch, false); + return rv; + } + + if (matchedrule) { + rv = newmatch->RuleMatched(aQuerySet, matchedrule, + ruleindex, nextresult); + if (NS_FAILED(rv)) { + nsTemplateMatch::Destroy(newmatch, false); + return rv; + } + + // Grab the template node + nsCOMPtr<nsIContent> action = matchedrule->GetAction(); + BuildContentFromTemplate(action, aElement, aElement, true, + mRefVariable == matchedrule->GetMemberVariable(), + nextresult, aNotify, newmatch, + aContainer, aNewIndexInContainer); + } + } + + if (mFlags & eLoggingEnabled) + OutputMatchToLog(resultid, newmatch, true); + + if (prevmatch) { + prevmatch->mNext = newmatch; + } + else { + mMatchMap.Put(resultid, newmatch); + } + + if (removematch) { + newmatch->mNext = removematch->mNext; + nsTemplateMatch::Destroy(removematch, true); + } + else { + newmatch->mNext = existingmatch; + } + } + + return rv; +} + +nsresult +nsXULContentBuilder::EnsureElementHasGenericChild(nsIContent* parent, + int32_t nameSpaceID, + nsIAtom* tag, + bool aNotify, + nsIContent** result) +{ + nsresult rv; + + rv = nsXULContentUtils::FindChildByTag(parent, nameSpaceID, tag, result); + if (NS_FAILED(rv)) + return rv; + + if (rv == NS_RDF_NO_VALUE) { + // we need to construct a new child element. + nsCOMPtr<Element> element; + + rv = CreateElement(nameSpaceID, tag, getter_AddRefs(element)); + if (NS_FAILED(rv)) + return rv; + + // XXX Note that the notification ensures we won't batch insertions! This could be bad! - Dave + rv = parent->AppendChildTo(element, aNotify); + if (NS_FAILED(rv)) + return rv; + + element.forget(result); + return NS_ELEMENT_GOT_CREATED; + } + else { + return NS_ELEMENT_WAS_THERE; + } +} + +bool +nsXULContentBuilder::IsOpen(nsIContent* aElement) +{ + // Determine if this is a <treeitem> or <menu> element + + // XXXhyatt Use the XBL service to obtain a base tag. + if (aElement->IsAnyOfXULElements(nsGkAtoms::menu, + nsGkAtoms::menubutton, + nsGkAtoms::toolbarbutton, + nsGkAtoms::button, + nsGkAtoms::treeitem)) + return aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::open, + nsGkAtoms::_true, eCaseMatters); + return true; +} + +nsresult +nsXULContentBuilder::RemoveGeneratedContent(nsIContent* aElement) +{ + // Keep a queue of "ungenerated" elements that we have to probe + // for generated content. + AutoTArray<nsIContent*, 8> ungenerated; + if (ungenerated.AppendElement(aElement) == nullptr) + return NS_ERROR_OUT_OF_MEMORY; + + uint32_t count; + while (0 != (count = ungenerated.Length())) { + // Pull the next "ungenerated" element off the queue. + uint32_t last = count - 1; + nsCOMPtr<nsIContent> element = ungenerated[last]; + ungenerated.RemoveElementAt(last); + + uint32_t i = element->GetChildCount(); + + while (i-- > 0) { + nsCOMPtr<nsIContent> child = element->GetChildAt(i); + + // Optimize for the <template> element, because we *know* + // it won't have any generated content: there's no reason + // to even check this subtree. + // XXX should this check |child| rather than |element|? Otherwise + // it should be moved outside the inner loop. Bug 297290. + if (element->NodeInfo()->Equals(nsGkAtoms::_template, + kNameSpaceID_XUL) || + !element->IsElement()) + continue; + + // If the element is in the template map, then we + // assume it's been generated and nuke it. + nsCOMPtr<nsIContent> tmpl; + mTemplateMap.GetTemplateFor(child, getter_AddRefs(tmpl)); + + if (! tmpl) { + // No 'template' attribute, so this must not have been + // generated. We'll need to examine its kids. + if (ungenerated.AppendElement(child) == nullptr) + return NS_ERROR_OUT_OF_MEMORY; + continue; + } + + // If we get here, it's "generated". Bye bye! + element->RemoveChildAt(i, true); + + // Remove this and any children from the content support map. + mContentSupportMap.Remove(child); + + // Remove from the template map + mTemplateMap.Remove(child); + } + } + + return NS_OK; +} + +nsresult +nsXULContentBuilder::GetElementsForResult(nsIXULTemplateResult* aResult, + nsCOMArray<nsIContent>& aElements) +{ + // if the root has been removed from the document, just return + // since there won't be any generated content any more + nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(mRoot->GetComposedDoc()); + if (! xuldoc) + return NS_OK; + + nsAutoString id; + aResult->GetId(id); + + xuldoc->GetElementsForID(id, aElements); + + return NS_OK; +} + +nsresult +nsXULContentBuilder::CreateElement(int32_t aNameSpaceID, + nsIAtom* aTag, + Element** aResult) +{ + nsCOMPtr<nsIDocument> doc = mRoot->GetComposedDoc(); + NS_ASSERTION(doc != nullptr, "not initialized"); + if (! doc) + return NS_ERROR_NOT_INITIALIZED; + + RefPtr<mozilla::dom::NodeInfo> nodeInfo = + doc->NodeInfoManager()->GetNodeInfo(aTag, nullptr, aNameSpaceID, + nsIDOMNode::ELEMENT_NODE); + + return NS_NewElement(aResult, nodeInfo.forget(), NOT_FROM_PARSER); +} + +nsresult +nsXULContentBuilder::SetContainerAttrs(nsIContent *aElement, + nsIXULTemplateResult* aResult, + bool aIgnoreNonContainers, + bool aNotify) +{ + NS_PRECONDITION(aResult != nullptr, "null ptr"); + if (! aResult) + return NS_ERROR_NULL_POINTER; + + bool iscontainer; + aResult->GetIsContainer(&iscontainer); + + if (aIgnoreNonContainers && !iscontainer) + return NS_OK; + + NS_NAMED_LITERAL_STRING(true_, "true"); + NS_NAMED_LITERAL_STRING(false_, "false"); + + const nsAString& newcontainer = + iscontainer ? true_ : false_; + + aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::container, + newcontainer, aNotify); + + if (iscontainer && !(mFlags & eDontTestEmpty)) { + bool isempty; + aResult->GetIsEmpty(&isempty); + + const nsAString& newempty = + (iscontainer && isempty) ? true_ : false_; + + aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::empty, + newempty, aNotify); + } + + return NS_OK; +} + + +//---------------------------------------------------------------------- +// +// nsIXULTemplateBuilder methods +// + +NS_IMETHODIMP +nsXULContentBuilder::CreateContents(nsIContent* aElement, bool aForceCreation) +{ + NS_PRECONDITION(aElement != nullptr, "null ptr"); + if (! aElement) + return NS_ERROR_NULL_POINTER; + + // don't build contents for closed elements. aForceCreation will be true + // when a menu is about to be opened, so the content should be built anyway. + if (!aForceCreation && !IsOpen(aElement)) + return NS_OK; + + return CreateTemplateAndContainerContents(aElement, aForceCreation); +} + +NS_IMETHODIMP +nsXULContentBuilder::HasGeneratedContent(nsIRDFResource* aResource, + nsIAtom* aTag, + bool* aGenerated) +{ + *aGenerated = false; + NS_ENSURE_TRUE(mRoot, NS_ERROR_NOT_INITIALIZED); + NS_ENSURE_STATE(mRootResult); + + nsCOMPtr<nsIRDFResource> rootresource; + nsresult rv = mRootResult->GetResource(getter_AddRefs(rootresource)); + if (NS_FAILED(rv)) + return rv; + + // the root resource is always acceptable + if (aResource == rootresource) { + if (!aTag || mRoot->NodeInfo()->NameAtom() == aTag) + *aGenerated = true; + } + else { + const char* uri; + aResource->GetValueConst(&uri); + + NS_ConvertUTF8toUTF16 refID(uri); + + // just return if the node is no longer in a document + nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(mRoot->GetComposedDoc()); + if (! xuldoc) + return NS_OK; + + nsCOMArray<nsIContent> elements; + xuldoc->GetElementsForID(refID, elements); + + uint32_t cnt = elements.Count(); + + for (int32_t i = int32_t(cnt) - 1; i >= 0; --i) { + nsCOMPtr<nsIContent> content = elements.SafeObjectAt(i); + + do { + nsTemplateMatch* match; + if (content == mRoot || mContentSupportMap.Get(content, &match)) { + // If we've got a tag, check it to ensure we're consistent. + if (!aTag || content->NodeInfo()->NameAtom() == aTag) { + *aGenerated = true; + return NS_OK; + } + } + + content = content->GetParent(); + } while (content); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXULContentBuilder::GetResultForContent(nsIDOMElement* aElement, + nsIXULTemplateResult** aResult) +{ + NS_ENSURE_ARG_POINTER(aElement); + NS_ENSURE_ARG_POINTER(aResult); + + nsCOMPtr<nsIContent> content = do_QueryInterface(aElement); + if (content == mRoot) { + *aResult = mRootResult; + } + else { + nsTemplateMatch *match = nullptr; + if (mContentSupportMap.Get(content, &match)) + *aResult = match->mResult; + else + *aResult = nullptr; + } + + NS_IF_ADDREF(*aResult); + return NS_OK; +} + +//---------------------------------------------------------------------- +// +// nsIDocumentObserver methods +// + +void +nsXULContentBuilder::AttributeChanged(nsIDocument* aDocument, + Element* aElement, + int32_t aNameSpaceID, + nsIAtom* aAttribute, + int32_t aModType, + const nsAttrValue* aOldValue) +{ + nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); + + // Handle "open" and "close" cases. We do this handling before + // we've notified the observer, so that content is already created + // for the frame system to walk. + if (aElement->GetNameSpaceID() == kNameSpaceID_XUL && + aAttribute == nsGkAtoms::open) { + // We're on a XUL tag, and an ``open'' attribute changed. + if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::open, + nsGkAtoms::_true, eCaseMatters)) + OpenContainer(aElement); + else + CloseContainer(aElement); + } + + if ((aNameSpaceID == kNameSpaceID_XUL) && + ((aAttribute == nsGkAtoms::sort) || + (aAttribute == nsGkAtoms::sortDirection) || + (aAttribute == nsGkAtoms::sortResource) || + (aAttribute == nsGkAtoms::sortResource2))) + mSortState.initialized = false; + + // Pass along to the generic template builder. + nsXULTemplateBuilder::AttributeChanged(aDocument, aElement, aNameSpaceID, + aAttribute, aModType, aOldValue); +} + +void +nsXULContentBuilder::NodeWillBeDestroyed(const nsINode* aNode) +{ + nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); + // Break circular references + mContentSupportMap.Clear(); + + nsXULTemplateBuilder::NodeWillBeDestroyed(aNode); +} + + +//---------------------------------------------------------------------- +// +// nsXULTemplateBuilder methods +// + +bool +nsXULContentBuilder::GetInsertionLocations(nsIXULTemplateResult* aResult, + nsCOMArray<nsIContent>** aLocations) +{ + *aLocations = nullptr; + + nsAutoString ref; + nsresult rv = aResult->GetBindingFor(mRefVariable, ref); + if (NS_FAILED(rv)) + return false; + + nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(mRoot->GetComposedDoc()); + if (! xuldoc) + return false; + + *aLocations = new nsCOMArray<nsIContent>; + NS_ENSURE_TRUE(*aLocations, false); + + xuldoc->GetElementsForID(ref, **aLocations); + uint32_t count = (*aLocations)->Count(); + + bool found = false; + + for (uint32_t t = 0; t < count; t++) { + nsCOMPtr<nsIContent> content = (*aLocations)->SafeObjectAt(t); + + nsTemplateMatch* refmatch; + if (content == mRoot || mContentSupportMap.Get(content, &refmatch)) { + // See if we've built the container contents for "content" + // yet. If not, we don't need to build any content. This + // happens, for example, if we receive an assertion on a + // closed folder in a tree widget or on a menu that hasn't + // yet been opened. + nsXULElement *xulcontent = nsXULElement::FromContent(content); + if (!xulcontent || xulcontent->GetTemplateGenerated()) { + found = true; + continue; + } + } + + // clear the item in the list since we don't want to insert there + (*aLocations)->ReplaceObjectAt(nullptr, t); + } + + return found; +} + +nsresult +nsXULContentBuilder::ReplaceMatch(nsIXULTemplateResult* aOldResult, + nsTemplateMatch* aNewMatch, + nsTemplateRule* aNewMatchRule, + void *aContext) + +{ + nsresult rv; + nsIContent* content = static_cast<nsIContent*>(aContext); + + // update the container attributes for the match + if (content) { + nsAutoString ref; + if (aNewMatch) + rv = aNewMatch->mResult->GetBindingFor(mRefVariable, ref); + else + rv = aOldResult->GetBindingFor(mRefVariable, ref); + if (NS_FAILED(rv)) + return rv; + + if (!ref.IsEmpty()) { + nsCOMPtr<nsIXULTemplateResult> refResult; + rv = GetResultForId(ref, getter_AddRefs(refResult)); + if (NS_FAILED(rv)) + return rv; + + if (refResult) + SetContainerAttrs(content, refResult, false, true); + } + } + + if (aOldResult) { + nsCOMArray<nsIContent> elements; + rv = GetElementsForResult(aOldResult, elements); + if (NS_FAILED(rv)) + return rv; + + uint32_t count = elements.Count(); + + for (int32_t e = int32_t(count) - 1; e >= 0; --e) { + nsCOMPtr<nsIContent> child = elements.SafeObjectAt(e); + + nsTemplateMatch* match; + if (mContentSupportMap.Get(child, &match)) { + if (content == match->GetContainer()) + RemoveMember(child); + } + } + } + + if (aNewMatch) { + nsCOMPtr<nsIContent> action = aNewMatchRule->GetAction(); + return BuildContentFromTemplate(action, content, content, true, + mRefVariable == aNewMatchRule->GetMemberVariable(), + aNewMatch->mResult, true, aNewMatch, + nullptr, nullptr); + } + + return NS_OK; +} + + +nsresult +nsXULContentBuilder::SynchronizeResult(nsIXULTemplateResult* aResult) +{ + nsCOMArray<nsIContent> elements; + GetElementsForResult(aResult, elements); + + uint32_t cnt = elements.Count(); + + for (int32_t i = int32_t(cnt) - 1; i >= 0; --i) { + nsCOMPtr<nsIContent> element = elements.SafeObjectAt(i); + + nsTemplateMatch* match; + if (! mContentSupportMap.Get(element, &match)) + continue; + + nsCOMPtr<nsIContent> templateNode; + mTemplateMap.GetTemplateFor(element, getter_AddRefs(templateNode)); + + NS_ASSERTION(templateNode, "couldn't find template node for element"); + if (! templateNode) + continue; + + // this node was created by a XUL template, so update it accordingly + SynchronizeUsingTemplate(templateNode, element, aResult); + } + + return NS_OK; +} + +//---------------------------------------------------------------------- +// +// Implementation methods +// + +nsresult +nsXULContentBuilder::OpenContainer(nsIContent* aElement) +{ + if (aElement != mRoot) { + if (mFlags & eDontRecurse) + return NS_OK; + + bool rightBuilder = false; + + nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(aElement->GetComposedDoc()); + if (! xuldoc) + return NS_OK; + + // See if we're responsible for this element + nsIContent* content = aElement; + do { + nsCOMPtr<nsIXULTemplateBuilder> builder; + xuldoc->GetTemplateBuilderFor(content, getter_AddRefs(builder)); + if (builder) { + if (builder == this) + rightBuilder = true; + break; + } + + content = content->GetParent(); + } while (content); + + if (! rightBuilder) + return NS_OK; + } + + CreateTemplateAndContainerContents(aElement, false); + + return NS_OK; +} + +nsresult +nsXULContentBuilder::CloseContainer(nsIContent* aElement) +{ + return NS_OK; +} + +nsresult +nsXULContentBuilder::RebuildAll() +{ + NS_ENSURE_TRUE(mRoot, NS_ERROR_NOT_INITIALIZED); + + // Bail out early if we are being torn down. + nsCOMPtr<nsIDocument> doc = mRoot->GetComposedDoc(); + if (!doc) + return NS_OK; + + if (mQueriesCompiled) + Uninit(false); + + nsresult rv = CompileQueries(); + if (NS_FAILED(rv)) + return rv; + + if (mQuerySets.Length() == 0) + return NS_OK; + + nsXULElement *xulcontent = nsXULElement::FromContent(mRoot); + if (xulcontent) + xulcontent->ClearTemplateGenerated(); + + // Now, regenerate both the template- and container-generated + // contents for the current element... + CreateTemplateAndContainerContents(mRoot, false); + + return NS_OK; +} + +/**** Sorting Methods ****/ + +nsresult +nsXULContentBuilder::CompareResultToNode(nsIXULTemplateResult* aResult, + nsIContent* aContent, + int32_t* aSortOrder) +{ + NS_ASSERTION(aSortOrder, "CompareResultToNode: null out param aSortOrder"); + + *aSortOrder = 0; + + nsTemplateMatch *match = nullptr; + if (!mContentSupportMap.Get(aContent, &match)) { + *aSortOrder = mSortState.sortStaticsLast ? -1 : 1; + return NS_OK; + } + + if (!mQueryProcessor) + return NS_OK; + + if (mSortState.direction == nsSortState_natural) { + // sort in natural order + nsresult rv = mQueryProcessor->CompareResults(aResult, match->mResult, + nullptr, mSortState.sortHints, + aSortOrder); + NS_ENSURE_SUCCESS(rv, rv); + } + else { + // iterate over each sort key and compare. If the nodes are equal, + // continue to compare using the next sort key. If not equal, stop. + int32_t length = mSortState.sortKeys.Count(); + for (int32_t t = 0; t < length; t++) { + nsresult rv = mQueryProcessor->CompareResults(aResult, match->mResult, + mSortState.sortKeys[t], + mSortState.sortHints, aSortOrder); + NS_ENSURE_SUCCESS(rv, rv); + + if (*aSortOrder) + break; + } + } + + // flip the sort order if performing a descending sorting + if (mSortState.direction == nsSortState_descending) + *aSortOrder = -*aSortOrder; + + return NS_OK; +} + +nsresult +nsXULContentBuilder::InsertSortedNode(nsIContent* aContainer, + nsIContent* aNode, + nsIXULTemplateResult* aResult, + bool aNotify) +{ + nsresult rv; + + if (!mSortState.initialized) { + nsAutoString sort, sortDirection, sortHints; + mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::sort, sort); + mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::sortDirection, sortDirection); + mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::sorthints, sortHints); + sortDirection += ' '; + sortDirection += sortHints; + rv = XULSortServiceImpl::InitializeSortState(mRoot, aContainer, + sort, sortDirection, &mSortState); + NS_ENSURE_SUCCESS(rv, rv); + } + + // when doing a natural sort, items will typically be sorted according to + // the order they appear in the datasource. For RDF, cache whether the + // reference parent is an RDF Seq. That way, the items can be sorted in the + // order they are in the Seq. + mSortState.isContainerRDFSeq = false; + if (mSortState.direction == nsSortState_natural) { + nsCOMPtr<nsISupports> ref; + nsresult rv = aResult->GetBindingObjectFor(mRefVariable, getter_AddRefs(ref)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIRDFResource> container = do_QueryInterface(ref); + + if (container) { + rv = gRDFContainerUtils->IsSeq(mDB, container, &mSortState.isContainerRDFSeq); + NS_ENSURE_SUCCESS(rv, rv); + } + } + + bool childAdded = false; + uint32_t numChildren = aContainer->GetChildCount(); + + if (mSortState.direction != nsSortState_natural || + (mSortState.direction == nsSortState_natural && mSortState.isContainerRDFSeq)) + { + // because numChildren gets modified + int32_t realNumChildren = numChildren; + nsIContent *child = nullptr; + + // rjc says: determine where static XUL ends and generated XUL/RDF begins + int32_t staticCount = 0; + + nsAutoString staticValue; + aContainer->GetAttr(kNameSpaceID_None, nsGkAtoms::staticHint, staticValue); + if (!staticValue.IsEmpty()) + { + // found "static" XUL element count hint + nsresult strErr = NS_OK; + staticCount = staticValue.ToInteger(&strErr); + if (NS_FAILED(strErr)) + staticCount = 0; + } else { + // compute the "static" XUL element count + for (nsIContent* child = aContainer->GetFirstChild(); + child; + child = child->GetNextSibling()) { + + if (nsContentUtils::HasNonEmptyAttr(child, kNameSpaceID_None, + nsGkAtoms::_template)) + break; + else + ++staticCount; + } + + if (mSortState.sortStaticsLast) { + // indicate that static XUL comes after RDF-generated content by + // making negative + staticCount = -staticCount; + } + + // save the "static" XUL element count hint + nsAutoString valueStr; + valueStr.AppendInt(staticCount); + aContainer->SetAttr(kNameSpaceID_None, nsGkAtoms::staticHint, valueStr, false); + } + + if (staticCount <= 0) { + numChildren += staticCount; + staticCount = 0; + } else if (staticCount > (int32_t)numChildren) { + staticCount = numChildren; + numChildren -= staticCount; + } + + // figure out where to insert the node when a sort order is being imposed + if (numChildren > 0) { + nsIContent *temp; + int32_t direction; + + // rjc says: The following is an implementation of a fairly optimal + // binary search insertion sort... with interpolation at either end-point. + + if (mSortState.lastWasFirst) { + child = aContainer->GetChildAt(staticCount); + temp = child; + rv = CompareResultToNode(aResult, temp, &direction); + if (direction < 0) { + aContainer->InsertChildAt(aNode, staticCount, aNotify); + childAdded = true; + } else + mSortState.lastWasFirst = false; + } else if (mSortState.lastWasLast) { + child = aContainer->GetChildAt(realNumChildren - 1); + temp = child; + rv = CompareResultToNode(aResult, temp, &direction); + if (direction > 0) { + aContainer->InsertChildAt(aNode, realNumChildren, aNotify); + childAdded = true; + } else + mSortState.lastWasLast = false; + } + + int32_t left = staticCount + 1, right = realNumChildren, x; + while (!childAdded && right >= left) { + x = (left + right) / 2; + child = aContainer->GetChildAt(x - 1); + temp = child; + + rv = CompareResultToNode(aResult, temp, &direction); + if ((x == left && direction < 0) || + (x == right && direction >= 0) || + left == right) + { + int32_t thePos = (direction > 0 ? x : x - 1); + aContainer->InsertChildAt(aNode, thePos, aNotify); + childAdded = true; + + mSortState.lastWasFirst = (thePos == staticCount); + mSortState.lastWasLast = (thePos >= realNumChildren); + + break; + } + if (direction < 0) + right = x - 1; + else + left = x + 1; + } + } + } + + // if the child hasn't been inserted yet, just add it at the end. Note + // that an append isn't done as there may be static content afterwards. + if (!childAdded) + aContainer->InsertChildAt(aNode, numChildren, aNotify); + + return NS_OK; +} diff --git a/dom/xul/templates/nsXULContentUtils.cpp b/dom/xul/templates/nsXULContentUtils.cpp new file mode 100644 index 000000000..5e128b42e --- /dev/null +++ b/dom/xul/templates/nsXULContentUtils.cpp @@ -0,0 +1,366 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * + * This Original Code has been modified by IBM Corporation. + * Modifications made by IBM described herein are + * Copyright (c) International Business Machines + * Corporation, 2000 + * + * Modifications to Mozilla code or documentation + * identified per MPL Section 3.3 + * + * Date Modified by Description of modification + * 03/27/2000 IBM Corp. Added PR_CALLBACK for Optlink + * use in OS2 + */ + + +/* + + A package of routines shared by the XUL content code. + + */ + +#include "mozilla/ArrayUtils.h" + +#include "nsCOMPtr.h" +#include "nsIContent.h" +#include "nsIDocument.h" +#include "nsIDOMElement.h" +#include "nsIDOMXULCommandDispatcher.h" +#include "nsIDOMXULDocument.h" +#include "nsIRDFNode.h" +#include "nsIRDFService.h" +#include "nsIServiceManager.h" +#include "nsIURL.h" +#include "nsXULContentUtils.h" +#include "nsLayoutCID.h" +#include "nsNameSpaceManager.h" +#include "nsRDFCID.h" +#include "nsString.h" +#include "nsXPIDLString.h" +#include "nsGkAtoms.h" +#include "mozilla/Logging.h" +#include "prtime.h" +#include "rdf.h" +#include "nsContentUtils.h" +#include "nsIDateTimeFormat.h" +#include "nsIScriptableDateFormat.h" +#include "nsICollation.h" +#include "nsCollationCID.h" +#include "nsILocale.h" +#include "nsILocaleService.h" +#include "nsIConsoleService.h" +#include "nsEscape.h" + +using namespace mozilla; + +//------------------------------------------------------------------------ + +nsIRDFService* nsXULContentUtils::gRDF; +nsIDateTimeFormat* nsXULContentUtils::gFormat; +nsICollation *nsXULContentUtils::gCollation; + +extern LazyLogModule gXULTemplateLog; + +#define XUL_RESOURCE(ident, uri) nsIRDFResource* nsXULContentUtils::ident +#define XUL_LITERAL(ident, val) nsIRDFLiteral* nsXULContentUtils::ident +#include "nsXULResourceList.h" +#undef XUL_RESOURCE +#undef XUL_LITERAL + +//------------------------------------------------------------------------ +// Constructors n' stuff +// + +nsresult +nsXULContentUtils::Init() +{ + static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID); + nsresult rv = CallGetService(kRDFServiceCID, &gRDF); + if (NS_FAILED(rv)) { + return rv; + } + +#define XUL_RESOURCE(ident, uri) \ + PR_BEGIN_MACRO \ + rv = gRDF->GetResource(NS_LITERAL_CSTRING(uri), &(ident)); \ + if (NS_FAILED(rv)) return rv; \ + PR_END_MACRO + +#define XUL_LITERAL(ident, val) \ + PR_BEGIN_MACRO \ + rv = gRDF->GetLiteral(val, &(ident)); \ + if (NS_FAILED(rv)) return rv; \ + PR_END_MACRO + +#include "nsXULResourceList.h" +#undef XUL_RESOURCE +#undef XUL_LITERAL + + gFormat = nsIDateTimeFormat::Create().take(); + if (!gFormat) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + + +nsresult +nsXULContentUtils::Finish() +{ + NS_IF_RELEASE(gRDF); + +#define XUL_RESOURCE(ident, uri) NS_IF_RELEASE(ident) +#define XUL_LITERAL(ident, val) NS_IF_RELEASE(ident) +#include "nsXULResourceList.h" +#undef XUL_RESOURCE +#undef XUL_LITERAL + + NS_IF_RELEASE(gFormat); + NS_IF_RELEASE(gCollation); + + return NS_OK; +} + +nsICollation* +nsXULContentUtils::GetCollation() +{ + if (!gCollation) { + nsresult rv; + + // get a locale service + nsCOMPtr<nsILocaleService> localeService = + do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr<nsILocale> locale; + rv = localeService->GetApplicationLocale(getter_AddRefs(locale)); + if (NS_SUCCEEDED(rv) && locale) { + nsCOMPtr<nsICollationFactory> colFactory = + do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID); + if (colFactory) { + rv = colFactory->CreateCollation(locale, &gCollation); + NS_ASSERTION(NS_SUCCEEDED(rv), + "couldn't create collation instance"); + } else + NS_ERROR("couldn't create instance of collation factory"); + } else + NS_ERROR("unable to get application locale"); + } else + NS_ERROR("couldn't get locale factory"); + } + + return gCollation; +} + +//------------------------------------------------------------------------ + +nsresult +nsXULContentUtils::FindChildByTag(nsIContent* aElement, + int32_t aNameSpaceID, + nsIAtom* aTag, + nsIContent** aResult) +{ + for (nsIContent* child = aElement->GetFirstChild(); + child; + child = child->GetNextSibling()) { + + if (child->NodeInfo()->Equals(aTag, aNameSpaceID)) { + NS_ADDREF(*aResult = child); + + return NS_OK; + } + } + + *aResult = nullptr; + return NS_RDF_NO_VALUE; // not found +} + + +/* + Note: this routine is similar, yet distinctly different from, nsBookmarksService::GetTextForNode +*/ + +nsresult +nsXULContentUtils::GetTextForNode(nsIRDFNode* aNode, nsAString& aResult) +{ + if (! aNode) { + aResult.Truncate(); + return NS_OK; + } + + nsresult rv; + + // Literals are the most common, so try these first. + nsCOMPtr<nsIRDFLiteral> literal = do_QueryInterface(aNode); + if (literal) { + const char16_t* p; + rv = literal->GetValueConst(&p); + if (NS_FAILED(rv)) return rv; + + aResult = p; + return NS_OK; + } + + nsCOMPtr<nsIRDFDate> dateLiteral = do_QueryInterface(aNode); + if (dateLiteral) { + PRTime value; + rv = dateLiteral->GetValue(&value); + if (NS_FAILED(rv)) return rv; + + nsAutoString str; + rv = gFormat->FormatPRTime(nullptr /* nsILocale* locale */, + kDateFormatShort, + kTimeFormatSeconds, + value, + str); + aResult.Assign(str); + + if (NS_FAILED(rv)) return rv; + + return NS_OK; + } + + nsCOMPtr<nsIRDFInt> intLiteral = do_QueryInterface(aNode); + if (intLiteral) { + int32_t value; + rv = intLiteral->GetValue(&value); + if (NS_FAILED(rv)) return rv; + + aResult.Truncate(); + nsAutoString intStr; + intStr.AppendInt(value, 10); + aResult.Append(intStr); + return NS_OK; + } + + + nsCOMPtr<nsIRDFResource> resource = do_QueryInterface(aNode); + if (resource) { + const char* p; + rv = resource->GetValueConst(&p); + if (NS_FAILED(rv)) return rv; + CopyUTF8toUTF16(p, aResult); + return NS_OK; + } + + NS_ERROR("not a resource or a literal"); + return NS_ERROR_UNEXPECTED; +} + +nsresult +nsXULContentUtils::GetResource(int32_t aNameSpaceID, nsIAtom* aAttribute, nsIRDFResource** aResult) +{ + // construct a fully-qualified URI from the namespace/tag pair. + NS_PRECONDITION(aAttribute != nullptr, "null ptr"); + if (! aAttribute) + return NS_ERROR_NULL_POINTER; + + return GetResource(aNameSpaceID, nsDependentAtomString(aAttribute), + aResult); +} + + +nsresult +nsXULContentUtils::GetResource(int32_t aNameSpaceID, const nsAString& aAttribute, nsIRDFResource** aResult) +{ + // construct a fully-qualified URI from the namespace/tag pair. + + // XXX should we allow nodes with no namespace??? + //NS_PRECONDITION(aNameSpaceID != kNameSpaceID_Unknown, "no namespace"); + //if (aNameSpaceID == kNameSpaceID_Unknown) + // return NS_ERROR_UNEXPECTED; + + nsresult rv; + + char16_t buf[256]; + nsFixedString uri(buf, ArrayLength(buf), 0); + if (aNameSpaceID != kNameSpaceID_Unknown && aNameSpaceID != kNameSpaceID_None) { + rv = nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNameSpaceID, uri); + // XXX ignore failure; treat as "no namespace" + } + + // XXX check to see if we need to insert a '/' or a '#'. Oy. + if (!uri.IsEmpty() && uri.Last() != '#' && uri.Last() != '/' && aAttribute.First() != '#') + uri.Append(char16_t('#')); + + uri.Append(aAttribute); + + rv = gRDF->GetUnicodeResource(uri, aResult); + NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get resource"); + if (NS_FAILED(rv)) return rv; + + return NS_OK; +} + + +nsresult +nsXULContentUtils::SetCommandUpdater(nsIDocument* aDocument, nsIContent* aElement) +{ + // Deal with setting up a 'commandupdater'. Pulls the 'events' and + // 'targets' attributes off of aElement, and adds it to the + // document's command dispatcher. + NS_PRECONDITION(aDocument != nullptr, "null ptr"); + if (! aDocument) + return NS_ERROR_NULL_POINTER; + + NS_PRECONDITION(aElement != nullptr, "null ptr"); + if (! aElement) + return NS_ERROR_NULL_POINTER; + + nsresult rv; + + nsCOMPtr<nsIDOMXULDocument> xuldoc = do_QueryInterface(aDocument); + NS_ASSERTION(xuldoc != nullptr, "not a xul document"); + if (! xuldoc) + return NS_ERROR_UNEXPECTED; + + nsCOMPtr<nsIDOMXULCommandDispatcher> dispatcher; + rv = xuldoc->GetCommandDispatcher(getter_AddRefs(dispatcher)); + NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get dispatcher"); + if (NS_FAILED(rv)) return rv; + + NS_ASSERTION(dispatcher != nullptr, "no dispatcher"); + if (! dispatcher) + return NS_ERROR_UNEXPECTED; + + nsAutoString events; + aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::events, events); + if (events.IsEmpty()) + events.Assign('*'); + + nsAutoString targets; + aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::targets, targets); + + if (targets.IsEmpty()) + targets.Assign('*'); + + nsCOMPtr<nsIDOMElement> domelement = do_QueryInterface(aElement); + NS_ASSERTION(domelement != nullptr, "not a DOM element"); + if (! domelement) + return NS_ERROR_UNEXPECTED; + + rv = dispatcher->AddCommandUpdater(domelement, events, targets); + if (NS_FAILED(rv)) return rv; + + return NS_OK; +} + +void +nsXULContentUtils::LogTemplateError(const char* aStr) +{ + nsAutoString message; + message.AssignLiteral("Error parsing template: "); + message.Append(NS_ConvertUTF8toUTF16(aStr).get()); + + nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID); + if (cs) { + cs->LogStringMessage(message.get()); + MOZ_LOG(gXULTemplateLog, LogLevel::Info, ("Error parsing template: %s", aStr)); + } +} diff --git a/dom/xul/templates/nsXULContentUtils.h b/dom/xul/templates/nsXULContentUtils.h new file mode 100644 index 000000000..c106fd91e --- /dev/null +++ b/dom/xul/templates/nsXULContentUtils.h @@ -0,0 +1,149 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + + A package of routines shared by the XUL content code. + + */ + +#ifndef nsXULContentUtils_h__ +#define nsXULContentUtils_h__ + +#include "nsISupports.h" + +class nsIAtom; +class nsIContent; +class nsIDocument; +class nsIRDFNode; +class nsIRDFResource; +class nsIRDFLiteral; +class nsIRDFService; +class nsIDateTimeFormat; +class nsICollation; + +// errors to pass to LogTemplateError +#define ERROR_TEMPLATE_INVALID_QUERYPROCESSOR \ + "querytype attribute doesn't specify a valid query processor" +#define ERROR_TEMPLATE_INVALID_QUERYSET \ + "unexpected <queryset> element" +#define ERROR_TEMPLATE_NO_MEMBERVAR \ + "no member variable found. Action body should have an element with uri attribute" +#define ERROR_TEMPLATE_WHERE_NO_SUBJECT \ + "<where> element is missing a subject attribute" +#define ERROR_TEMPLATE_WHERE_NO_RELATION \ + "<where> element is missing a rel attribute" +#define ERROR_TEMPLATE_WHERE_NO_VALUE \ + "<where> element is missing a value attribute" +#define ERROR_TEMPLATE_WHERE_NO_VAR \ + "<where> element must have at least one variable as a subject or value" +#define ERROR_TEMPLATE_BINDING_BAD_SUBJECT \ + "<binding> requires a variable for its subject attribute" +#define ERROR_TEMPLATE_BINDING_BAD_PREDICATE \ + "<binding> element is missing a predicate attribute" +#define ERROR_TEMPLATE_BINDING_BAD_OBJECT \ + "<binding> requires a variable for its object attribute" +#define ERROR_TEMPLATE_CONTENT_NOT_FIRST \ + "expected <content> to be first" +#define ERROR_TEMPLATE_MEMBER_NOCONTAINERVAR \ + "<member> requires a variable for its container attribute" +#define ERROR_TEMPLATE_MEMBER_NOCHILDVAR \ + "<member> requires a variable for its child attribute" +#define ERROR_TEMPLATE_TRIPLE_NO_VAR \ + "<triple> should have at least one variable as a subject or object" +#define ERROR_TEMPLATE_TRIPLE_BAD_SUBJECT \ + "<triple> requires a variable for its subject attribute" +#define ERROR_TEMPLATE_TRIPLE_BAD_PREDICATE \ + "<triple> should have a non-variable value as a predicate" +#define ERROR_TEMPLATE_TRIPLE_BAD_OBJECT \ + "<triple> requires a variable for its object attribute" +#define ERROR_TEMPLATE_MEMBER_UNBOUND \ + "neither container or child variables of <member> has a value" +#define ERROR_TEMPLATE_TRIPLE_UNBOUND \ + "neither subject or object variables of <triple> has a value" +#define ERROR_TEMPLATE_BAD_XPATH \ + "XPath expression in query could not be parsed" +#define ERROR_TEMPLATE_BAD_ASSIGN_XPATH \ + "XPath expression in <assign> could not be parsed" +#define ERROR_TEMPLATE_BAD_BINDING_XPATH \ + "XPath expression in <binding> could not be parsed" +#define ERROR_TEMPLATE_STORAGE_BAD_URI \ + "only profile: or file URI are allowed" +#define ERROR_TEMPLATE_STORAGE_CANNOT_OPEN_DATABASE \ + "cannot open given database" +#define ERROR_TEMPLATE_STORAGE_BAD_QUERY \ + "syntax error in the SQL query" +#define ERROR_TEMPLATE_STORAGE_UNKNOWN_QUERY_PARAMETER \ + "the given named parameter is unknown in the SQL query" +#define ERROR_TEMPLATE_STORAGE_WRONG_TYPE_QUERY_PARAMETER \ + "the type of a query parameter is wrong" +#define ERROR_TEMPLATE_STORAGE_QUERY_PARAMETER_NOT_BOUND \ + "a query parameter cannot be bound to the SQL query" + +class nsXULContentUtils +{ +protected: + static nsIRDFService* gRDF; + static nsIDateTimeFormat* gFormat; + static nsICollation *gCollation; + + static bool gDisableXULCache; + + static int + DisableXULCacheChangedCallback(const char* aPrefName, void* aClosure); + +public: + static nsresult + Init(); + + static nsresult + Finish(); + + static nsresult + FindChildByTag(nsIContent *aElement, + int32_t aNameSpaceID, + nsIAtom* aTag, + nsIContent **aResult); + + static nsresult + FindChildByResource(nsIContent* aElement, + nsIRDFResource* aResource, + nsIContent** aResult); + + static nsresult + GetTextForNode(nsIRDFNode* aNode, nsAString& aResult); + + static nsresult + GetResource(int32_t aNameSpaceID, nsIAtom* aAttribute, nsIRDFResource** aResult); + + static nsresult + GetResource(int32_t aNameSpaceID, const nsAString& aAttribute, nsIRDFResource** aResult); + + static nsresult + SetCommandUpdater(nsIDocument* aDocument, nsIContent* aElement); + + /** + * Log a message to the error console + */ + static void + LogTemplateError(const char* aMsg); + + static nsIRDFService* + RDFService() + { + return gRDF; + } + + static nsICollation* + GetCollation(); + +#define XUL_RESOURCE(ident, uri) static nsIRDFResource* ident +#define XUL_LITERAL(ident, val) static nsIRDFLiteral* ident +#include "nsXULResourceList.h" +#undef XUL_RESOURCE +#undef XUL_LITERAL +}; + +#endif // nsXULContentUtils_h__ diff --git a/dom/xul/templates/nsXULResourceList.h b/dom/xul/templates/nsXULResourceList.h new file mode 100644 index 000000000..eaea7e996 --- /dev/null +++ b/dom/xul/templates/nsXULResourceList.h @@ -0,0 +1,13 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// N.B., no include guard! We'll include this multiple times in some +// files. + +XUL_RESOURCE(NC_child, NC_NAMESPACE_URI "child"); +XUL_RESOURCE(NC_Folder, NC_NAMESPACE_URI "Folder"); +XUL_RESOURCE(NC_open, NC_NAMESPACE_URI "open"); + +XUL_LITERAL(true_, u"true"); diff --git a/dom/xul/templates/nsXULSortService.cpp b/dom/xul/templates/nsXULSortService.cpp new file mode 100644 index 000000000..ab3e13461 --- /dev/null +++ b/dom/xul/templates/nsXULSortService.cpp @@ -0,0 +1,507 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This Original Code has been modified by IBM Corporation. + * Modifications made by IBM described herein are + * Copyright (c) International Business Machines + * Corporation, 2000 + * + * Modifications to Mozilla code or documentation + * identified per MPL Section 3.3 + * + * Date Modified by Description of modification + * 03/27/2000 IBM Corp. Added PR_CALLBACK for Optlink + * use in OS2 + */ + +/* + This file provides the implementation for the sort service manager. + */ + +#include "nsCOMPtr.h" +#include "nsIContent.h" +#include "nsIDOMElement.h" +#include "nsIDOMNode.h" +#include "nsIServiceManager.h" +#include "nsGkAtoms.h" +#include "nsNameSpaceManager.h" +#include "nsXULContentUtils.h" +#include "nsString.h" +#include "nsQuickSort.h" +#include "nsWhitespaceTokenizer.h" +#include "nsXULSortService.h" +#include "nsIDOMXULElement.h" +#include "nsIXULTemplateBuilder.h" +#include "nsTemplateMatch.h" +#include "nsICollation.h" +#include "nsUnicharUtils.h" + +NS_IMPL_ISUPPORTS(XULSortServiceImpl, nsIXULSortService) + +void +XULSortServiceImpl::SetSortHints(nsIContent *aNode, nsSortState* aSortState) +{ + // set sort and sortDirection attributes when is sort is done + aNode->SetAttr(kNameSpaceID_None, nsGkAtoms::sort, + aSortState->sort, true); + + nsAutoString direction; + if (aSortState->direction == nsSortState_descending) + direction.AssignLiteral("descending"); + else if (aSortState->direction == nsSortState_ascending) + direction.AssignLiteral("ascending"); + aNode->SetAttr(kNameSpaceID_None, nsGkAtoms::sortDirection, + direction, true); + + // for trees, also set the sort info on the currently sorted column + if (aNode->NodeInfo()->Equals(nsGkAtoms::tree, kNameSpaceID_XUL)) { + if (aSortState->sortKeys.Count() >= 1) { + nsAutoString sortkey; + aSortState->sortKeys[0]->ToString(sortkey); + SetSortColumnHints(aNode, sortkey, direction); + } + } +} + +void +XULSortServiceImpl::SetSortColumnHints(nsIContent *content, + const nsAString &sortResource, + const nsAString &sortDirection) +{ + // set sort info on current column. This ensures that the + // column header sort indicator is updated properly. + for (nsIContent* child = content->GetFirstChild(); + child; + child = child->GetNextSibling()) { + if (child->IsXULElement(nsGkAtoms::treecols)) { + SetSortColumnHints(child, sortResource, sortDirection); + } else if (child->IsXULElement(nsGkAtoms::treecol)) { + nsAutoString value; + child->GetAttr(kNameSpaceID_None, nsGkAtoms::sort, value); + // also check the resource attribute for older code + if (value.IsEmpty()) + child->GetAttr(kNameSpaceID_None, nsGkAtoms::resource, value); + if (value == sortResource) { + child->SetAttr(kNameSpaceID_None, nsGkAtoms::sortActive, + NS_LITERAL_STRING("true"), true); + child->SetAttr(kNameSpaceID_None, nsGkAtoms::sortDirection, + sortDirection, true); + // Note: don't break out of loop; want to set/unset + // attribs on ALL sort columns + } else if (!value.IsEmpty()) { + child->UnsetAttr(kNameSpaceID_None, nsGkAtoms::sortActive, + true); + child->UnsetAttr(kNameSpaceID_None, nsGkAtoms::sortDirection, + true); + } + } + } +} + +nsresult +XULSortServiceImpl::GetItemsToSort(nsIContent *aContainer, + nsSortState* aSortState, + nsTArray<contentSortInfo>& aSortItems) +{ + // if there is a template attached to the sort node, use the builder to get + // the items to be sorted + nsCOMPtr<nsIDOMXULElement> element = do_QueryInterface(aContainer); + if (element) { + nsCOMPtr<nsIXULTemplateBuilder> builder; + element->GetBuilder(getter_AddRefs(builder)); + + if (builder) { + nsresult rv = builder->GetQueryProcessor(getter_AddRefs(aSortState->processor)); + if (NS_FAILED(rv) || !aSortState->processor) + return rv; + + return GetTemplateItemsToSort(aContainer, builder, aSortState, aSortItems); + } + } + + // if there is no template builder, just get the children. For trees, + // get the treechildren element as use that as the parent + nsCOMPtr<nsIContent> treechildren; + if (aContainer->NodeInfo()->Equals(nsGkAtoms::tree, kNameSpaceID_XUL)) { + nsXULContentUtils::FindChildByTag(aContainer, + kNameSpaceID_XUL, + nsGkAtoms::treechildren, + getter_AddRefs(treechildren)); + if (!treechildren) + return NS_OK; + + aContainer = treechildren; + } + + for (nsIContent* child = aContainer->GetFirstChild(); + child; + child = child->GetNextSibling()) { + contentSortInfo* cinfo = aSortItems.AppendElement(); + if (!cinfo) + return NS_ERROR_OUT_OF_MEMORY; + + cinfo->content = child; + } + + return NS_OK; +} + + +nsresult +XULSortServiceImpl::GetTemplateItemsToSort(nsIContent* aContainer, + nsIXULTemplateBuilder* aBuilder, + nsSortState* aSortState, + nsTArray<contentSortInfo>& aSortItems) +{ + for (nsIContent* child = aContainer->GetFirstChild(); + child; + child = child->GetNextSibling()) { + + nsCOMPtr<nsIDOMElement> childnode = do_QueryInterface(child); + + nsCOMPtr<nsIXULTemplateResult> result; + nsresult rv = aBuilder->GetResultForContent(childnode, getter_AddRefs(result)); + NS_ENSURE_SUCCESS(rv, rv); + + if (result) { + contentSortInfo* cinfo = aSortItems.AppendElement(); + if (!cinfo) + return NS_ERROR_OUT_OF_MEMORY; + + cinfo->content = child; + cinfo->result = result; + } + else if (!aContainer->IsXULElement(nsGkAtoms::_template)) { + rv = GetTemplateItemsToSort(child, aBuilder, aSortState, aSortItems); + NS_ENSURE_SUCCESS(rv, rv); + } + } + + return NS_OK; +} + +int +testSortCallback(const void *data1, const void *data2, void *privateData) +{ + /// Note: testSortCallback is a small C callback stub for NS_QuickSort + contentSortInfo *left = (contentSortInfo *)data1; + contentSortInfo *right = (contentSortInfo *)data2; + nsSortState* sortState = (nsSortState *)privateData; + + int32_t sortOrder = 0; + + if (sortState->direction == nsSortState_natural && sortState->processor) { + // sort in natural order + sortState->processor->CompareResults(left->result, right->result, + nullptr, sortState->sortHints, &sortOrder); + } + else { + int32_t length = sortState->sortKeys.Count(); + for (int32_t t = 0; t < length; t++) { + // for templates, use the query processor to do sorting + if (sortState->processor) { + sortState->processor->CompareResults(left->result, right->result, + sortState->sortKeys[t], + sortState->sortHints, &sortOrder); + if (sortOrder) + break; + } + else { + // no template, so just compare attributes. Ignore namespaces for now. + nsAutoString leftstr, rightstr; + left->content->GetAttr(kNameSpaceID_None, sortState->sortKeys[t], leftstr); + right->content->GetAttr(kNameSpaceID_None, sortState->sortKeys[t], rightstr); + + sortOrder = XULSortServiceImpl::CompareValues(leftstr, rightstr, sortState->sortHints); + } + } + } + + if (sortState->direction == nsSortState_descending) + sortOrder = -sortOrder; + + return sortOrder; +} + +nsresult +XULSortServiceImpl::SortContainer(nsIContent *aContainer, nsSortState* aSortState) +{ + nsTArray<contentSortInfo> items; + nsresult rv = GetItemsToSort(aContainer, aSortState, items); + NS_ENSURE_SUCCESS(rv, rv); + + uint32_t numResults = items.Length(); + if (!numResults) + return NS_OK; + + uint32_t i; + + // inbetweenSeparatorSort sorts the items between separators independently + if (aSortState->inbetweenSeparatorSort) { + uint32_t startIndex = 0; + for (i = 0; i < numResults; i++) { + if (i > startIndex + 1) { + nsAutoString type; + items[i].result->GetType(type); + if (type.EqualsLiteral("separator")) { + if (aSortState->invertSort) + InvertSortInfo(items, startIndex, i - startIndex); + else + NS_QuickSort((void *)(items.Elements() + startIndex), i - startIndex, + sizeof(contentSortInfo), testSortCallback, (void*)aSortState); + + startIndex = i + 1; + } + } + } + + if (i > startIndex + 1) { + if (aSortState->invertSort) + InvertSortInfo(items, startIndex, i - startIndex); + else + NS_QuickSort((void *)(items.Elements() + startIndex), i - startIndex, + sizeof(contentSortInfo), testSortCallback, (void*)aSortState); + } + } else { + // if the items are just being inverted, that is, just switching between + // ascending and descending, just reverse the list. + if (aSortState->invertSort) + InvertSortInfo(items, 0, numResults); + else + NS_QuickSort((void *)items.Elements(), numResults, + sizeof(contentSortInfo), testSortCallback, (void*)aSortState); + } + + // first remove the items from the old positions + for (i = 0; i < numResults; i++) { + nsIContent* child = items[i].content; + nsIContent* parent = child->GetParent(); + + if (parent) { + // remember the parent so that it can be reinserted back + // into the same parent. This is necessary as multiple rules + // may generate results which get placed in different locations. + items[i].parent = parent; + int32_t index = parent->IndexOf(child); + parent->RemoveChildAt(index, true); + } + } + + // now add the items back in sorted order + for (i = 0; i < numResults; i++) + { + nsIContent* child = items[i].content; + nsIContent* parent = items[i].parent; + if (parent) { + parent->AppendChildTo(child, true); + + // if it's a container in a tree or menu, find its children, + // and sort those also + if (!child->AttrValueIs(kNameSpaceID_None, nsGkAtoms::container, + nsGkAtoms::_true, eCaseMatters)) + continue; + + for (nsIContent* grandchild = child->GetFirstChild(); + grandchild; + grandchild = grandchild->GetNextSibling()) { + mozilla::dom::NodeInfo *ni = grandchild->NodeInfo(); + nsIAtom *localName = ni->NameAtom(); + if (ni->NamespaceID() == kNameSpaceID_XUL && + (localName == nsGkAtoms::treechildren || + localName == nsGkAtoms::menupopup)) { + SortContainer(grandchild, aSortState); + } + } + } + } + + return NS_OK; +} + +nsresult +XULSortServiceImpl::InvertSortInfo(nsTArray<contentSortInfo>& aData, + int32_t aStart, int32_t aNumItems) +{ + if (aNumItems > 1) { + // reverse the items in the array starting from aStart + int32_t upPoint = (aNumItems + 1) / 2 + aStart; + int32_t downPoint = (aNumItems - 2) / 2 + aStart; + int32_t half = aNumItems / 2; + while (half-- > 0) { + aData[downPoint--].swap(aData[upPoint++]); + } + } + return NS_OK; +} + +nsresult +XULSortServiceImpl::InitializeSortState(nsIContent* aRootElement, + nsIContent* aContainer, + const nsAString& aSortKey, + const nsAString& aSortHints, + nsSortState* aSortState) +{ + // used as an optimization for the content builder + if (aContainer != aSortState->lastContainer.get()) { + aSortState->lastContainer = aContainer; + aSortState->lastWasFirst = false; + aSortState->lastWasLast = false; + } + + // The attributes allowed are either: + // sort="key1 key2 ..." + // or sortResource="key1" sortResource2="key2" + // The latter is for backwards compatibility, and is equivalent to concatenating + // both values in the sort attribute + nsAutoString sort(aSortKey); + aSortState->sortKeys.Clear(); + if (sort.IsEmpty()) { + nsAutoString sortResource, sortResource2; + aRootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::sortResource, sortResource); + if (!sortResource.IsEmpty()) { + nsCOMPtr<nsIAtom> sortkeyatom = NS_Atomize(sortResource); + aSortState->sortKeys.AppendObject(sortkeyatom); + sort.Append(sortResource); + + aRootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::sortResource2, sortResource2); + if (!sortResource2.IsEmpty()) { + nsCOMPtr<nsIAtom> sortkeyatom2 = NS_Atomize(sortResource2); + aSortState->sortKeys.AppendObject(sortkeyatom2); + sort.Append(' '); + sort.Append(sortResource2); + } + } + } + else { + nsWhitespaceTokenizer tokenizer(sort); + while (tokenizer.hasMoreTokens()) { + nsCOMPtr<nsIAtom> keyatom = NS_Atomize(tokenizer.nextToken()); + NS_ENSURE_TRUE(keyatom, NS_ERROR_OUT_OF_MEMORY); + aSortState->sortKeys.AppendObject(keyatom); + } + } + + aSortState->sort.Assign(sort); + aSortState->direction = nsSortState_natural; + + bool noNaturalState = false; + nsWhitespaceTokenizer tokenizer(aSortHints); + while (tokenizer.hasMoreTokens()) { + const nsDependentSubstring& token(tokenizer.nextToken()); + if (token.EqualsLiteral("comparecase")) + aSortState->sortHints |= nsIXULSortService::SORT_COMPARECASE; + else if (token.EqualsLiteral("integer")) + aSortState->sortHints |= nsIXULSortService::SORT_INTEGER; + else if (token.EqualsLiteral("descending")) + aSortState->direction = nsSortState_descending; + else if (token.EqualsLiteral("ascending")) + aSortState->direction = nsSortState_ascending; + else if (token.EqualsLiteral("twostate")) + noNaturalState = true; + } + + // if the twostate flag was set, the natural order is skipped and only + // ascending and descending are allowed + if (aSortState->direction == nsSortState_natural && noNaturalState) { + aSortState->direction = nsSortState_ascending; + } + + // set up sort order info + aSortState->invertSort = false; + + nsAutoString existingsort; + aRootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::sort, existingsort); + nsAutoString existingsortDirection; + aRootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::sortDirection, existingsortDirection); + + // if just switching direction, set the invertSort flag + if (sort.Equals(existingsort)) { + if (aSortState->direction == nsSortState_descending) { + if (existingsortDirection.EqualsLiteral("ascending")) + aSortState->invertSort = true; + } + else if (aSortState->direction == nsSortState_ascending && + existingsortDirection.EqualsLiteral("descending")) { + aSortState->invertSort = true; + } + } + + // sort items between separators independently + aSortState->inbetweenSeparatorSort = + aRootElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::sortSeparators, + nsGkAtoms::_true, eCaseMatters); + + // sort static content (non template generated nodes) after generated content + aSortState->sortStaticsLast = aRootElement->AttrValueIs(kNameSpaceID_None, + nsGkAtoms::sortStaticsLast, + nsGkAtoms::_true, eCaseMatters); + + aSortState->initialized = true; + + return NS_OK; +} + +int32_t +XULSortServiceImpl::CompareValues(const nsAString& aLeft, + const nsAString& aRight, + uint32_t aSortHints) +{ + if (aSortHints & SORT_INTEGER) { + nsresult err; + int32_t leftint = PromiseFlatString(aLeft).ToInteger(&err); + if (NS_SUCCEEDED(err)) { + int32_t rightint = PromiseFlatString(aRight).ToInteger(&err); + if (NS_SUCCEEDED(err)) { + return leftint - rightint; + } + } + // if they aren't integers, just fall through and compare strings + } + + if (aSortHints & SORT_COMPARECASE) { + return ::Compare(aLeft, aRight); + } + + nsICollation* collation = nsXULContentUtils::GetCollation(); + if (collation) { + int32_t result; + collation->CompareString(nsICollation::kCollationCaseInSensitive, + aLeft, aRight, &result); + return result; + } + + return ::Compare(aLeft, aRight, nsCaseInsensitiveStringComparator()); +} + +NS_IMETHODIMP +XULSortServiceImpl::Sort(nsIDOMNode* aNode, + const nsAString& aSortKey, + const nsAString& aSortHints) +{ + // get root content node + nsCOMPtr<nsIContent> sortNode = do_QueryInterface(aNode); + if (!sortNode) + return NS_ERROR_FAILURE; + + nsSortState sortState; + nsresult rv = InitializeSortState(sortNode, sortNode, + aSortKey, aSortHints, &sortState); + NS_ENSURE_SUCCESS(rv, rv); + + // store sort info in attributes on content + SetSortHints(sortNode, &sortState); + rv = SortContainer(sortNode, &sortState); + + sortState.processor = nullptr; // don't hang on to this reference + return rv; +} + +nsresult +NS_NewXULSortService(nsIXULSortService** sortService) +{ + *sortService = new XULSortServiceImpl(); + NS_ADDREF(*sortService); + return NS_OK; +} diff --git a/dom/xul/templates/nsXULSortService.h b/dom/xul/templates/nsXULSortService.h new file mode 100644 index 000000000..306481e0d --- /dev/null +++ b/dom/xul/templates/nsXULSortService.h @@ -0,0 +1,187 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This Original Code has been modified by IBM Corporation. + * Modifications made by IBM described herein are + * Copyright (c) International Business Machines + * Corporation, 2000 + * + * Modifications to Mozilla code or documentation + * identified per MPL Section 3.3 + * + * Date Modified by Description of modification + * 03/27/2000 IBM Corp. Added PR_CALLBACK for Optlink + * use in OS2 + */ + +/* + This sort service is used to sort template built content or content by attribute. + */ + +#ifndef nsXULTemplateResultSetRDF_h +#define nsXULTemplateResultSetRDF_h + +#include "nsCOMPtr.h" +#include "nsCOMArray.h" +#include "nsTArray.h" +#include "nsIContent.h" +#include "nsIXULTemplateResult.h" +#include "nsIXULTemplateQueryProcessor.h" +#include "nsIXULSortService.h" +#include "nsCycleCollectionParticipant.h" + +enum nsSortState_direction { + nsSortState_descending, + nsSortState_ascending, + nsSortState_natural +}; + +// the sort state holds info about the current sort +struct nsSortState +{ + bool initialized; + MOZ_INIT_OUTSIDE_CTOR bool invertSort; + MOZ_INIT_OUTSIDE_CTOR bool inbetweenSeparatorSort; + MOZ_INIT_OUTSIDE_CTOR bool sortStaticsLast; + MOZ_INIT_OUTSIDE_CTOR bool isContainerRDFSeq; + + uint32_t sortHints; + + MOZ_INIT_OUTSIDE_CTOR nsSortState_direction direction; + nsAutoString sort; + nsCOMArray<nsIAtom> sortKeys; + + nsCOMPtr<nsIXULTemplateQueryProcessor> processor; + nsCOMPtr<nsIContent> lastContainer; + MOZ_INIT_OUTSIDE_CTOR bool lastWasFirst, lastWasLast; + + nsSortState() + : initialized(false), + isContainerRDFSeq(false), + sortHints(0) + { + } + void Traverse(nsCycleCollectionTraversalCallback &cb) const + { + cb.NoteXPCOMChild(processor); + cb.NoteXPCOMChild(lastContainer); + } +}; + +// information about a particular item to be sorted +struct contentSortInfo { + nsCOMPtr<nsIContent> content; + nsCOMPtr<nsIContent> parent; + nsCOMPtr<nsIXULTemplateResult> result; + void swap(contentSortInfo& other) + { + content.swap(other.content); + parent.swap(other.parent); + result.swap(other.result); + } +}; + +//////////////////////////////////////////////////////////////////////// +// ServiceImpl +// +// This is the sort service. +// +class XULSortServiceImpl : public nsIXULSortService +{ +protected: + XULSortServiceImpl(void) {} + virtual ~XULSortServiceImpl(void) {} + + friend nsresult NS_NewXULSortService(nsIXULSortService** mgr); + +private: + +public: + // nsISupports + NS_DECL_ISUPPORTS + + // nsISortService + NS_DECL_NSIXULSORTSERVICE + + /** + * Set sort and sortDirection attributes when a sort is done. + */ + void + SetSortHints(nsIContent *aNode, nsSortState* aSortState); + + /** + * Set sortActive and sortDirection attributes on a tree column when a sort + * is done. The column to change is the one with a sort attribute that + * matches the sort key. The sort attributes are removed from the other + * columns. + */ + void + SetSortColumnHints(nsIContent *content, + const nsAString &sortResource, + const nsAString &sortDirection); + + /** + * Determine the list of items which need to be sorted. This is determined + * in the following way: + * - for elements that have a content builder, get its list of generated + * results + * - otherwise, for trees, get the child treeitems + * - otherwise, get the direct children + */ + nsresult + GetItemsToSort(nsIContent *aContainer, + nsSortState* aSortState, + nsTArray<contentSortInfo>& aSortItems); + + /** + * Get the list of items to sort for template built content + */ + nsresult + GetTemplateItemsToSort(nsIContent* aContainer, + nsIXULTemplateBuilder* aBuilder, + nsSortState* aSortState, + nsTArray<contentSortInfo>& aSortItems); + + /** + * Sort a container using the supplied sort state details. + */ + nsresult + SortContainer(nsIContent *aContainer, nsSortState* aSortState); + + /** + * Given a list of sortable items, reverse the list. This is done + * when simply changing the sort direction for the same key. + */ + nsresult + InvertSortInfo(nsTArray<contentSortInfo>& aData, + int32_t aStart, int32_t aNumItems); + + /** + * Initialize sort information from attributes specified on the container, + * the sort key and sort direction. + * + * @param aRootElement the element that contains sort attributes + * @param aContainer the container to sort, usually equal to aRootElement + * @param aSortKey space separated list of sort keys + * @param aSortDirection direction to sort in + * @param aSortState structure filled in with sort data + */ + static nsresult + InitializeSortState(nsIContent* aRootElement, + nsIContent* aContainer, + const nsAString& aSortKey, + const nsAString& aSortDirection, + nsSortState* aSortState); + + /** + * Compares aLeft and aRight and returns < 0, 0, or > 0. The sort + * hints are checked for case matching and integer sorting. + */ + static int32_t CompareValues(const nsAString& aLeft, + const nsAString& aRight, + uint32_t aSortHints); +}; + +#endif diff --git a/dom/xul/templates/nsXULTemplateBuilder.cpp b/dom/xul/templates/nsXULTemplateBuilder.cpp new file mode 100644 index 000000000..49fb3335d --- /dev/null +++ b/dom/xul/templates/nsXULTemplateBuilder.cpp @@ -0,0 +1,2573 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + + Builds content from a datasource using the XUL <template> tag. + + TO DO + + . Fix ContentTagTest's location in the network construction + + To turn on logging for this module, set: + + MOZ_LOG=nsXULTemplateBuilder:5 + + */ + +#include "nsAutoPtr.h" +#include "nsCOMPtr.h" +#include "nsCRT.h" +#include "nsIContent.h" +#include "nsIDOMElement.h" +#include "nsIDOMNode.h" +#include "nsIDOMDocument.h" +#include "nsIDOMXULElement.h" +#include "nsIDocument.h" +#include "nsBindingManager.h" +#include "nsIDOMNodeList.h" +#include "nsIObserverService.h" +#include "nsIRDFCompositeDataSource.h" +#include "nsIRDFInferDataSource.h" +#include "nsIRDFContainerUtils.h" +#include "nsIXULDocument.h" +#include "nsIXULTemplateBuilder.h" +#include "nsIXULBuilderListener.h" +#include "nsIRDFRemoteDataSource.h" +#include "nsIRDFService.h" +#include "nsIScriptContext.h" +#include "nsIScriptGlobalObject.h" +#include "nsIServiceManager.h" +#include "nsISimpleEnumerator.h" +#include "nsIMutableArray.h" +#include "nsIURL.h" +#include "nsIXPConnect.h" +#include "nsContentCID.h" +#include "nsNameSpaceManager.h" +#include "nsRDFCID.h" +#include "nsXULContentUtils.h" +#include "nsString.h" +#include "nsTArray.h" +#include "nsXPIDLString.h" +#include "nsWhitespaceTokenizer.h" +#include "nsGkAtoms.h" +#include "nsXULElement.h" +#include "jsapi.h" +#include "mozilla/Logging.h" +#include "rdf.h" +#include "PLDHashTable.h" +#include "plhash.h" +#include "nsDOMClassInfoID.h" +#include "nsPIDOMWindow.h" +#include "nsIConsoleService.h" +#include "nsNetUtil.h" +#include "nsXULTemplateBuilder.h" +#include "nsXULTemplateQueryProcessorRDF.h" +#include "nsXULTemplateQueryProcessorXML.h" +#include "nsXULTemplateQueryProcessorStorage.h" +#include "nsContentUtils.h" +#include "ChildIterator.h" +#include "mozilla/dom/ScriptSettings.h" +#include "nsGlobalWindow.h" + +using namespace mozilla::dom; +using namespace mozilla; + +//---------------------------------------------------------------------- +// +// nsXULTemplateBuilder +// + +nsrefcnt nsXULTemplateBuilder::gRefCnt = 0; +nsIRDFService* nsXULTemplateBuilder::gRDFService; +nsIRDFContainerUtils* nsXULTemplateBuilder::gRDFContainerUtils; +nsIScriptSecurityManager* nsXULTemplateBuilder::gScriptSecurityManager; +nsIPrincipal* nsXULTemplateBuilder::gSystemPrincipal; +nsIObserverService* nsXULTemplateBuilder::gObserverService; + +LazyLogModule gXULTemplateLog("nsXULTemplateBuilder"); + +#define NS_QUERY_PROCESSOR_CONTRACTID_PREFIX "@mozilla.org/xul/xul-query-processor;1?name=" + +//---------------------------------------------------------------------- +// +// nsXULTemplateBuilder methods +// + +nsXULTemplateBuilder::nsXULTemplateBuilder(void) + : mQueriesCompiled(false), + mFlags(0), + mTop(nullptr), + mObservedDocument(nullptr) +{ + MOZ_COUNT_CTOR(nsXULTemplateBuilder); +} + +void +nsXULTemplateBuilder::DestroyMatchMap() +{ + for (auto iter = mMatchMap.Iter(); !iter.Done(); iter.Next()) { + nsTemplateMatch*& match = iter.Data(); + // delete all the matches in the list + while (match) { + nsTemplateMatch* next = match->mNext; + nsTemplateMatch::Destroy(match, true); + match = next; + } + + iter.Remove(); + } +} + +nsXULTemplateBuilder::~nsXULTemplateBuilder(void) +{ + Uninit(true); + + if (--gRefCnt == 0) { + NS_IF_RELEASE(gRDFService); + NS_IF_RELEASE(gRDFContainerUtils); + NS_IF_RELEASE(gSystemPrincipal); + NS_IF_RELEASE(gScriptSecurityManager); + NS_IF_RELEASE(gObserverService); + } + + MOZ_COUNT_DTOR(nsXULTemplateBuilder); +} + + +nsresult +nsXULTemplateBuilder::InitGlobals() +{ + nsresult rv; + + if (gRefCnt++ == 0) { + // Initialize the global shared reference to the service + // manager and get some shared resource objects. + NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID); + rv = CallGetService(kRDFServiceCID, &gRDFService); + if (NS_FAILED(rv)) + return rv; + + NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID); + rv = CallGetService(kRDFContainerUtilsCID, &gRDFContainerUtils); + if (NS_FAILED(rv)) + return rv; + + rv = CallGetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, + &gScriptSecurityManager); + if (NS_FAILED(rv)) + return rv; + + rv = gScriptSecurityManager->GetSystemPrincipal(&gSystemPrincipal); + if (NS_FAILED(rv)) + return rv; + + rv = CallGetService(NS_OBSERVERSERVICE_CONTRACTID, &gObserverService); + if (NS_FAILED(rv)) + return rv; + } + + return NS_OK; +} + +void +nsXULTemplateBuilder::StartObserving(nsIDocument* aDocument) +{ + aDocument->AddObserver(this); + mObservedDocument = aDocument; + gObserverService->AddObserver(this, DOM_WINDOW_DESTROYED_TOPIC, false); +} + +void +nsXULTemplateBuilder::StopObserving() +{ + MOZ_ASSERT(mObservedDocument); + mObservedDocument->RemoveObserver(this); + mObservedDocument = nullptr; + gObserverService->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC); +} + +void +nsXULTemplateBuilder::CleanUp(bool aIsFinal) +{ + for (int32_t q = mQuerySets.Length() - 1; q >= 0; q--) { + nsTemplateQuerySet* qs = mQuerySets[q]; + delete qs; + } + + mQuerySets.Clear(); + + DestroyMatchMap(); + + // Setting mQueryProcessor to null will close connections. This would be + // handled by the cycle collector, but we want to close them earlier. + if (aIsFinal) + mQueryProcessor = nullptr; +} + +void +nsXULTemplateBuilder::Uninit(bool aIsFinal) +{ + if (mObservedDocument && aIsFinal) { + StopObserving(); + } + + if (mQueryProcessor) + mQueryProcessor->Done(); + + CleanUp(aIsFinal); + + mRootResult = nullptr; + mRefVariable = nullptr; + mMemberVariable = nullptr; + + mQueriesCompiled = false; +} + +NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULTemplateBuilder) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULTemplateBuilder) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mDataSource) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mDB) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mCompDB) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mRootResult) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mListeners) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mQueryProcessor) + tmp->DestroyMatchMap(); + for (uint32_t i = 0; i < tmp->mQuerySets.Length(); ++i) { + nsTemplateQuerySet* qs = tmp->mQuerySets[i]; + delete qs; + } + tmp->mQuerySets.Clear(); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULTemplateBuilder) + if (tmp->mObservedDocument && !cb.WantAllTraces()) { + // The global observer service holds us alive. + return NS_SUCCESS_INTERRUPTED_TRAVERSE; + } + + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDataSource) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDB) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCompDB) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRootResult) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListeners) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mQueryProcessor) + + for (auto iter = tmp->mMatchMap.Iter(); !iter.Done(); iter.Next()) { + cb.NoteXPCOMChild(iter.Key()); + nsTemplateMatch* match = iter.UserData(); + while (match) { + cb.NoteXPCOMChild(match->GetContainer()); + cb.NoteXPCOMChild(match->mResult); + match = match->mNext; + } + } + + { + uint32_t i, count = tmp->mQuerySets.Length(); + for (i = 0; i < count; ++i) { + nsTemplateQuerySet *set = tmp->mQuerySets[i]; + cb.NoteXPCOMChild(set->mQueryNode); + cb.NoteXPCOMChild(set->mCompiledQuery); + uint16_t j, rulesCount = set->RuleCount(); + for (j = 0; j < rulesCount; ++j) { + set->GetRuleAt(j)->Traverse(cb); + } + } + } + tmp->Traverse(cb); +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULTemplateBuilder) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULTemplateBuilder) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULTemplateBuilder) + NS_INTERFACE_MAP_ENTRY(nsIXULTemplateBuilder) + NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver) + NS_INTERFACE_MAP_ENTRY(nsIMutationObserver) + NS_INTERFACE_MAP_ENTRY(nsIObserver) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXULTemplateBuilder) + NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(XULTemplateBuilder) +NS_INTERFACE_MAP_END + +//---------------------------------------------------------------------- +// +// nsIXULTemplateBuilder methods +// + +NS_IMETHODIMP +nsXULTemplateBuilder::GetRoot(nsIDOMElement** aResult) +{ + if (mRoot) { + return CallQueryInterface(mRoot, aResult); + } + *aResult = nullptr; + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateBuilder::GetDatasource(nsISupports** aResult) +{ + if (mCompDB) + NS_ADDREF(*aResult = mCompDB); + else + NS_IF_ADDREF(*aResult = mDataSource); + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateBuilder::SetDatasource(nsISupports* aResult) +{ + mDataSource = aResult; + mCompDB = do_QueryInterface(mDataSource); + + return Rebuild(); +} + +NS_IMETHODIMP +nsXULTemplateBuilder::GetDatabase(nsIRDFCompositeDataSource** aResult) +{ + NS_IF_ADDREF(*aResult = mCompDB); + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateBuilder::GetQueryProcessor(nsIXULTemplateQueryProcessor** aResult) +{ + NS_IF_ADDREF(*aResult = mQueryProcessor.get()); + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateBuilder::AddRuleFilter(nsIDOMNode* aRule, nsIXULTemplateRuleFilter* aFilter) +{ + if (!aRule || !aFilter) + return NS_ERROR_NULL_POINTER; + + // a custom rule filter may be added, one for each rule. If a new one is + // added, it replaces the old one. Look for the right rule and set its + // filter + + int32_t count = mQuerySets.Length(); + for (int32_t q = 0; q < count; q++) { + nsTemplateQuerySet* queryset = mQuerySets[q]; + + int16_t rulecount = queryset->RuleCount(); + for (int16_t r = 0; r < rulecount; r++) { + nsTemplateRule* rule = queryset->GetRuleAt(r); + + nsCOMPtr<nsIDOMNode> rulenode; + rule->GetRuleNode(getter_AddRefs(rulenode)); + if (aRule == rulenode) { + rule->SetRuleFilter(aFilter); + return NS_OK; + } + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateBuilder::Rebuild() +{ + int32_t i; + + for (i = mListeners.Count() - 1; i >= 0; --i) { + mListeners[i]->WillRebuild(this); + } + + nsresult rv = RebuildAll(); + + for (i = mListeners.Count() - 1; i >= 0; --i) { + mListeners[i]->DidRebuild(this); + } + + return rv; +} + +NS_IMETHODIMP +nsXULTemplateBuilder::Refresh() +{ + nsresult rv; + + if (!mCompDB) + return NS_ERROR_FAILURE; + + nsCOMPtr<nsISimpleEnumerator> dslist; + rv = mCompDB->GetDataSources(getter_AddRefs(dslist)); + NS_ENSURE_SUCCESS(rv, rv); + + bool hasMore; + nsCOMPtr<nsISupports> next; + nsCOMPtr<nsIRDFRemoteDataSource> rds; + + while(NS_SUCCEEDED(dslist->HasMoreElements(&hasMore)) && hasMore) { + dslist->GetNext(getter_AddRefs(next)); + if (next && (rds = do_QueryInterface(next))) { + rds->Refresh(false); + } + } + + // XXXbsmedberg: it would be kinda nice to install an async nsIRDFXMLSink + // observer and call rebuild() once the load is complete. See bug 254600. + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateBuilder::Init(nsIContent* aElement) +{ + NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER); + mRoot = aElement; + + nsCOMPtr<nsIDocument> doc = mRoot->GetComposedDoc(); + NS_ASSERTION(doc, "element has no document"); + if (! doc) + return NS_ERROR_UNEXPECTED; + + bool shouldDelay; + nsresult rv = LoadDataSources(doc, &shouldDelay); + + if (NS_SUCCEEDED(rv)) { + StartObserving(doc); + } + + return rv; +} + +NS_IMETHODIMP +nsXULTemplateBuilder::CreateContents(nsIContent* aElement, bool aForceCreation) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateBuilder::HasGeneratedContent(nsIRDFResource* aResource, + nsIAtom* aTag, + bool* aGenerated) +{ + *aGenerated = false; + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateBuilder::AddResult(nsIXULTemplateResult* aResult, + nsIDOMNode* aQueryNode) +{ + NS_ENSURE_ARG_POINTER(aResult); + NS_ENSURE_ARG_POINTER(aQueryNode); + + return UpdateResult(nullptr, aResult, aQueryNode); +} + +NS_IMETHODIMP +nsXULTemplateBuilder::RemoveResult(nsIXULTemplateResult* aResult) +{ + NS_ENSURE_ARG_POINTER(aResult); + + return UpdateResult(aResult, nullptr, nullptr); +} + +NS_IMETHODIMP +nsXULTemplateBuilder::ReplaceResult(nsIXULTemplateResult* aOldResult, + nsIXULTemplateResult* aNewResult, + nsIDOMNode* aQueryNode) +{ + NS_ENSURE_ARG_POINTER(aOldResult); + NS_ENSURE_ARG_POINTER(aNewResult); + NS_ENSURE_ARG_POINTER(aQueryNode); + + // just remove the old result and then add a new result separately + + nsresult rv = UpdateResult(aOldResult, nullptr, nullptr); + if (NS_FAILED(rv)) + return rv; + + return UpdateResult(nullptr, aNewResult, aQueryNode); +} + +nsresult +nsXULTemplateBuilder::UpdateResult(nsIXULTemplateResult* aOldResult, + nsIXULTemplateResult* aNewResult, + nsIDOMNode* aQueryNode) +{ + MOZ_LOG(gXULTemplateLog, LogLevel::Info, + ("nsXULTemplateBuilder::UpdateResult %p %p %p", + aOldResult, aNewResult, aQueryNode)); + + if (!mRoot || !mQueriesCompiled) + return NS_OK; + + // get the containers where content may be inserted. If + // GetInsertionLocations returns false, no container has generated + // any content yet so new content should not be generated either. This + // will be false if the result applies to content that is in a closed menu + // or treeitem for example. + + nsAutoPtr<nsCOMArray<nsIContent> > insertionPoints; + bool mayReplace = GetInsertionLocations(aOldResult ? aOldResult : aNewResult, + getter_Transfers(insertionPoints)); + if (! mayReplace) + return NS_OK; + + nsresult rv = NS_OK; + + nsCOMPtr<nsIRDFResource> oldId, newId; + nsTemplateQuerySet* queryset = nullptr; + + if (aOldResult) { + rv = GetResultResource(aOldResult, getter_AddRefs(oldId)); + if (NS_FAILED(rv)) + return rv; + + // Ignore re-entrant builds for content that is currently in our + // activation stack. + if (IsActivated(oldId)) + return NS_OK; + } + + if (aNewResult) { + rv = GetResultResource(aNewResult, getter_AddRefs(newId)); + if (NS_FAILED(rv)) + return rv; + + // skip results that don't have ids + if (! newId) + return NS_OK; + + // Ignore re-entrant builds for content that is currently in our + // activation stack. + if (IsActivated(newId)) + return NS_OK; + + // look for the queryset associated with the supplied query node + nsCOMPtr<nsIContent> querycontent = do_QueryInterface(aQueryNode); + + int32_t count = mQuerySets.Length(); + for (int32_t q = 0; q < count; q++) { + nsTemplateQuerySet* qs = mQuerySets[q]; + if (qs->mQueryNode == querycontent) { + queryset = qs; + break; + } + } + + if (! queryset) + return NS_OK; + } + + if (insertionPoints) { + // iterate over each insertion point and add or remove the result from + // that container + uint32_t count = insertionPoints->Count(); + for (uint32_t t = 0; t < count; t++) { + nsCOMPtr<nsIContent> insertionPoint = insertionPoints->SafeObjectAt(t); + if (insertionPoint) { + rv = UpdateResultInContainer(aOldResult, aNewResult, queryset, + oldId, newId, insertionPoint); + if (NS_FAILED(rv)) + return rv; + } + } + } + else { + // The tree builder doesn't use insertion points, so no insertion + // points will be set. In this case, just update the one result. + rv = UpdateResultInContainer(aOldResult, aNewResult, queryset, + oldId, newId, nullptr); + } + + return NS_OK; +} + +nsresult +nsXULTemplateBuilder::UpdateResultInContainer(nsIXULTemplateResult* aOldResult, + nsIXULTemplateResult* aNewResult, + nsTemplateQuerySet* aQuerySet, + nsIRDFResource* aOldId, + nsIRDFResource* aNewId, + nsIContent* aInsertionPoint) +{ + // This method takes a result that no longer applies (aOldResult) and + // replaces it with a new result (aNewResult). Either may be null + // indicating to just remove a result or add a new one without replacing. + // + // Matches are stored in the hashtable mMatchMap, keyed by result id. If + // there is more than one query, or the same id is found in different + // containers, the values in the hashtable will be a linked list of all + // the matches for that id. The matches are sorted according to the + // queries they are associated with. Matches for earlier queries in the + // template take priority over matches from later queries. The priority + // for a match is determined from the match's QuerySetPriority method. + // The first query has a priority 0, and higher numbers are for later + // queries with successively higher priorities. Thus, a match takes + // precedence if it has a lower priority than another. If there is only + // one query or container, then the match doesn't have any linked items. + // + // Matches are nsTemplateMatch objects. They are wrappers around + // nsIXULTemplateResult result objects and are created with + // nsTemplateMatch::Create below. The aQuerySet argument specifies which + // query the match is associated with. + // + // When a result id exists in multiple containers, the match's mContainer + // field is set to the container it corresponds to. The aInsertionPoint + // argument specifies which container is being updated. Even though they + // are stored in the same linked list as other matches of the same id, the + // matches for different containers are treated separately. They are only + // stored in the same hashtable to avoid a more complex data structure, as + // the use of the same id in multiple containers isn't a common occurance. + // + // Only one match with a given id per container is active at a time. When + // a match is active, content is generated for it. When a match is + // inactive, content is not generated for it. A match becomes active if + // another match with the same id and container with a lower priority + // isn't already active, and the match has a rule or conditions clause + // which evaluates to true. The former is checked by comparing the value + // of the QuerySetPriority method of the match with earlier matches. The + // latter is checked with the DetermineMatchedRule method. + // + // Naturally, if a match with a lower priority is active, it overrides + // the new match, so the new match is hooked up into the match linked + // list as inactive, and no content is generated for it. If a match with a + // higher priority is active, and the new match's conditions evaluate + // to true, then this existing match with the higher priority needs to have + // its generated content removed and replaced with the new match's + // generated content. + // + // Similar situations apply when removing an existing match. If the match + // is active, the existing generated content will need to be removed, and + // a match of higher priority that is revealed may become active and need + // to have content generated. + // + // Content removal and generation is done by the ReplaceMatch method which + // is overridden for the content builder and tree builder to update the + // generated output for each type. + // + // The code below handles all of the various cases and ensures that the + // match lists are maintained properly. + + nsresult rv = NS_OK; + int16_t ruleindex; + nsTemplateRule* matchedrule = nullptr; + + // Indicates that the old match was active and must have its content + // removed + bool oldMatchWasActive = false; + + // acceptedmatch will be set to a new match that has to have new content + // generated for it. If a new match doesn't need to have content + // generated, (because for example, a match with a lower priority + // already applies), then acceptedmatch will be null, but the match will + // be still hooked up into the chain, since it may become active later + // as other results are updated. + nsTemplateMatch* acceptedmatch = nullptr; + + // When aOldResult is specified, removematch will be set to the + // corresponding match. This match needs to be deleted as it no longer + // applies. However, removedmatch will be null when aOldResult is null, or + // when no match was found corresponding to aOldResult. + nsTemplateMatch* removedmatch = nullptr; + + // These will be set when aNewResult is specified indicating to add a + // result, but will end up replacing an existing match. The former + // indicates a match being replaced that was active and had content + // generated for it, while the latter indicates a match that wasn't active + // and just needs to be deleted. Both may point to different matches. For + // example, if the new match becomes active, replacing an inactive match, + // the inactive match will need to be deleted. However, if another match + // with a higher priority is active, the new match will override it, so + // content will need to be generated for the new match and removed for + // this existing active match. + nsTemplateMatch* replacedmatch = nullptr; + nsTemplateMatch* replacedmatchtodelete = nullptr; + + if (aOldResult) { + nsTemplateMatch* firstmatch; + if (mMatchMap.Get(aOldId, &firstmatch)) { + nsTemplateMatch* oldmatch = firstmatch; + nsTemplateMatch* prevmatch = nullptr; + + // look for the right match if there was more than one + while (oldmatch && (oldmatch->mResult != aOldResult)) { + prevmatch = oldmatch; + oldmatch = oldmatch->mNext; + } + + if (oldmatch) { + nsTemplateMatch* findmatch = oldmatch->mNext; + + // Keep a reference so that linked list can be hooked up at + // the end in case an error occurs. + nsTemplateMatch* nextmatch = findmatch; + + if (oldmatch->IsActive()) { + // Indicate that the old match was active so its content + // will be removed later. + oldMatchWasActive = true; + + // The match being removed is the active match, so scan + // through the later matches to determine if one should + // now become the active match. + while (findmatch) { + // only other matches with the same container should + // now match, leave other containers alone + if (findmatch->GetContainer() == aInsertionPoint) { + nsTemplateQuerySet* qs = + mQuerySets[findmatch->QuerySetPriority()]; + + DetermineMatchedRule(aInsertionPoint, findmatch->mResult, + qs, &matchedrule, &ruleindex); + + if (matchedrule) { + rv = findmatch->RuleMatched(qs, + matchedrule, ruleindex, + findmatch->mResult); + if (NS_FAILED(rv)) + return rv; + + acceptedmatch = findmatch; + break; + } + } + + findmatch = findmatch->mNext; + } + } + + if (oldmatch == firstmatch) { + // the match to remove is at the beginning + if (oldmatch->mNext) { + mMatchMap.Put(aOldId, oldmatch->mNext); + } + else { + mMatchMap.Remove(aOldId); + } + } + + if (prevmatch) + prevmatch->mNext = nextmatch; + + removedmatch = oldmatch; + if (mFlags & eLoggingEnabled) + OutputMatchToLog(aOldId, removedmatch, false); + } + } + } + + nsTemplateMatch *newmatch = nullptr; + if (aNewResult) { + // only allow a result to be inserted into containers with a matching tag + nsIAtom* tag = aQuerySet->GetTag(); + if (aInsertionPoint && tag && + tag != aInsertionPoint->NodeInfo()->NameAtom()) + return NS_OK; + + int32_t findpriority = aQuerySet->Priority(); + + newmatch = nsTemplateMatch::Create(findpriority, + aNewResult, aInsertionPoint); + if (!newmatch) + return NS_ERROR_OUT_OF_MEMORY; + + nsTemplateMatch* firstmatch; + if (mMatchMap.Get(aNewId, &firstmatch)) { + bool hasEarlierActiveMatch = false; + + // Scan through the existing matches to find where the new one + // should be inserted. oldmatch will be set to the old match for + // the same query and prevmatch will be set to the match before it. + nsTemplateMatch* prevmatch = nullptr; + nsTemplateMatch* oldmatch = firstmatch; + while (oldmatch) { + // Break out once we've reached a query in the list with a + // lower priority. The new match will be inserted at this + // location so that the match list is sorted by priority. + int32_t priority = oldmatch->QuerySetPriority(); + if (priority > findpriority) { + oldmatch = nullptr; + break; + } + + // look for matches that belong in the same container + if (oldmatch->GetContainer() == aInsertionPoint) { + if (priority == findpriority) + break; + + // If a match with a lower priority is active, the new + // match can't replace it. + if (oldmatch->IsActive()) + hasEarlierActiveMatch = true; + } + + prevmatch = oldmatch; + oldmatch = oldmatch->mNext; + } + + // At this point, oldmatch will either be null, or set to a match + // with the same container and priority. If set, oldmatch will + // need to be replaced by newmatch. + + if (oldmatch) + newmatch->mNext = oldmatch->mNext; + else if (prevmatch) + newmatch->mNext = prevmatch->mNext; + else + newmatch->mNext = firstmatch; + + // hasEarlierActiveMatch will be set to true if a match with a + // lower priority was found. The new match won't replace it in + // this case. If hasEarlierActiveMatch is false, then the new match + // may be become active if it matches one of the rules, and will + // generate output. It's also possible however, that a match with + // the same priority already exists, which means that the new match + // will replace the old one. In this case, oldmatch will be set to + // the old match. The content for the old match must be removed and + // content for the new match generated in its place. + if (! hasEarlierActiveMatch) { + // If the old match was the active match, set replacedmatch to + // indicate that it needs its content removed. + if (oldmatch) { + if (oldmatch->IsActive()) + replacedmatch = oldmatch; + replacedmatchtodelete = oldmatch; + } + + // check if the new result matches the rules + rv = DetermineMatchedRule(aInsertionPoint, newmatch->mResult, + aQuerySet, &matchedrule, &ruleindex); + if (NS_FAILED(rv)) { + nsTemplateMatch::Destroy(newmatch, false); + return rv; + } + + if (matchedrule) { + rv = newmatch->RuleMatched(aQuerySet, + matchedrule, ruleindex, + newmatch->mResult); + if (NS_FAILED(rv)) { + nsTemplateMatch::Destroy(newmatch, false); + return rv; + } + + // acceptedmatch may have been set in the block handling + // aOldResult earlier. If so, we would only get here when + // that match has a higher priority than this new match. + // As only one match can have content generated for it, it + // is OK to set acceptedmatch here to the new match, + // ignoring the other one. + acceptedmatch = newmatch; + + // Clear the matched state of the later results for the + // same container. + nsTemplateMatch* clearmatch = newmatch->mNext; + while (clearmatch) { + if (clearmatch->GetContainer() == aInsertionPoint && + clearmatch->IsActive()) { + clearmatch->SetInactive(); + // Replacedmatch should be null here. If not, it + // means that two matches were active which isn't + // a valid state + NS_ASSERTION(!replacedmatch, + "replaced match already set"); + replacedmatch = clearmatch; + break; + } + clearmatch = clearmatch->mNext; + } + } + else if (oldmatch && oldmatch->IsActive()) { + // The result didn't match the rules, so look for a later + // one. However, only do this if the old match was the + // active match. + newmatch = newmatch->mNext; + while (newmatch) { + if (newmatch->GetContainer() == aInsertionPoint) { + rv = DetermineMatchedRule(aInsertionPoint, newmatch->mResult, + aQuerySet, &matchedrule, &ruleindex); + if (NS_FAILED(rv)) { + nsTemplateMatch::Destroy(newmatch, false); + return rv; + } + + if (matchedrule) { + rv = newmatch->RuleMatched(aQuerySet, + matchedrule, ruleindex, + newmatch->mResult); + if (NS_FAILED(rv)) { + nsTemplateMatch::Destroy(newmatch, false); + return rv; + } + + acceptedmatch = newmatch; + break; + } + } + + newmatch = newmatch->mNext; + } + } + + // put the match in the map if there isn't a previous match + if (! prevmatch) { + mMatchMap.Put(aNewId, newmatch); + } + } + + // hook up the match last in case an error occurs + if (prevmatch) + prevmatch->mNext = newmatch; + } + else { + // The id is not used in the hashtable yet so create a new match + // and add it to the hashtable. + rv = DetermineMatchedRule(aInsertionPoint, aNewResult, + aQuerySet, &matchedrule, &ruleindex); + if (NS_FAILED(rv)) { + nsTemplateMatch::Destroy(newmatch, false); + return rv; + } + + if (matchedrule) { + rv = newmatch->RuleMatched(aQuerySet, matchedrule, + ruleindex, aNewResult); + if (NS_FAILED(rv)) { + nsTemplateMatch::Destroy(newmatch, false); + return rv; + } + + acceptedmatch = newmatch; + } + + mMatchMap.Put(aNewId, newmatch); + } + } + + // The ReplaceMatch method is builder specific and removes the generated + // content for a match. + + // Remove the content for a match that was active and needs to be replaced. + if (replacedmatch) { + rv = ReplaceMatch(replacedmatch->mResult, nullptr, nullptr, + aInsertionPoint); + + if (mFlags & eLoggingEnabled) + OutputMatchToLog(aNewId, replacedmatch, false); + } + + // remove a match that needs to be deleted. + if (replacedmatchtodelete) + nsTemplateMatch::Destroy(replacedmatchtodelete, true); + + // If the old match was active, the content for it needs to be removed. + // If the old match was not active, it shouldn't have had any content, + // so just pass null to ReplaceMatch. If acceptedmatch was set, then + // content needs to be generated for a new match. + if (oldMatchWasActive || acceptedmatch) + rv = ReplaceMatch(oldMatchWasActive ? aOldResult : nullptr, + acceptedmatch, matchedrule, aInsertionPoint); + + // delete the old match that was replaced + if (removedmatch) + nsTemplateMatch::Destroy(removedmatch, true); + + if (mFlags & eLoggingEnabled && newmatch) + OutputMatchToLog(aNewId, newmatch, true); + + return rv; +} + +NS_IMETHODIMP +nsXULTemplateBuilder::ResultBindingChanged(nsIXULTemplateResult* aResult) +{ + // A binding update is used when only the values of the bindings have + // changed, so the same rule still applies. Just synchronize the content. + // The new result will have the new values. + NS_ENSURE_ARG_POINTER(aResult); + + if (!mRoot || !mQueriesCompiled) + return NS_OK; + + return SynchronizeResult(aResult); +} + +NS_IMETHODIMP +nsXULTemplateBuilder::GetRootResult(nsIXULTemplateResult** aResult) +{ + *aResult = mRootResult; + NS_IF_ADDREF(*aResult); + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateBuilder::GetResultForId(const nsAString& aId, + nsIXULTemplateResult** aResult) +{ + if (aId.IsEmpty()) + return NS_ERROR_INVALID_ARG; + + nsCOMPtr<nsIRDFResource> resource; + gRDFService->GetUnicodeResource(aId, getter_AddRefs(resource)); + + *aResult = nullptr; + + nsTemplateMatch* match; + if (mMatchMap.Get(resource, &match)) { + // find the active match + while (match) { + if (match->IsActive()) { + *aResult = match->mResult; + NS_IF_ADDREF(*aResult); + break; + } + match = match->mNext; + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateBuilder::GetResultForContent(nsIDOMElement* aContent, + nsIXULTemplateResult** aResult) +{ + *aResult = nullptr; + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateBuilder::AddListener(nsIXULBuilderListener* aListener) +{ + NS_ENSURE_ARG(aListener); + + if (!mListeners.AppendObject(aListener)) + return NS_ERROR_OUT_OF_MEMORY; + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateBuilder::RemoveListener(nsIXULBuilderListener* aListener) +{ + NS_ENSURE_ARG(aListener); + + mListeners.RemoveObject(aListener); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateBuilder::Observe(nsISupports* aSubject, + const char* aTopic, + const char16_t* aData) +{ + // Uuuuber hack to clean up circular references that the cycle collector + // doesn't know about. See bug 394514. + if (!strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC)) { + if (nsCOMPtr<mozIDOMWindow> window = do_QueryInterface(aSubject)) { + nsCOMPtr<nsIDocument> doc = + nsPIDOMWindowInner::From(window)->GetExtantDoc(); + if (doc && doc == mObservedDocument) + NodeWillBeDestroyed(doc); + } + } + return NS_OK; +} +//---------------------------------------------------------------------- +// +// nsIDocumentOberver interface +// + +void +nsXULTemplateBuilder::AttributeChanged(nsIDocument* aDocument, + Element* aElement, + int32_t aNameSpaceID, + nsIAtom* aAttribute, + int32_t aModType, + const nsAttrValue* aOldValue) +{ + if (aElement == mRoot && aNameSpaceID == kNameSpaceID_None) { + // Check for a change to the 'ref' attribute on an atom, in which + // case we may need to nuke and rebuild the entire content model + // beneath the element. + if (aAttribute == nsGkAtoms::ref) + nsContentUtils::AddScriptRunner( + NewRunnableMethod(this, &nsXULTemplateBuilder::RunnableRebuild)); + + // Check for a change to the 'datasources' attribute. If so, setup + // mDB by parsing the new value and rebuild. + else if (aAttribute == nsGkAtoms::datasources) { + nsContentUtils::AddScriptRunner( + NewRunnableMethod(this, &nsXULTemplateBuilder::RunnableLoadAndRebuild)); + } + } +} + +void +nsXULTemplateBuilder::ContentRemoved(nsIDocument* aDocument, + nsIContent* aContainer, + nsIContent* aChild, + int32_t aIndexInContainer, + nsIContent* aPreviousSibling) +{ + if (mRoot && nsContentUtils::ContentIsDescendantOf(mRoot, aChild)) { + RefPtr<nsXULTemplateBuilder> kungFuDeathGrip(this); + + if (mQueryProcessor) + mQueryProcessor->Done(); + + // Pass false to Uninit since content is going away anyway + nsContentUtils::AddScriptRunner( + NewRunnableMethod(this, &nsXULTemplateBuilder::UninitFalse)); + + MOZ_ASSERT(aDocument == mObservedDocument); + StopObserving(); + + nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(aDocument); + if (xuldoc) + xuldoc->SetTemplateBuilderFor(mRoot, nullptr); + + // clear the template state when removing content so that template + // content will be regenerated again if the content is reinserted + nsXULElement *xulcontent = nsXULElement::FromContent(mRoot); + if (xulcontent) + xulcontent->ClearTemplateGenerated(); + + CleanUp(true); + + mDB = nullptr; + mCompDB = nullptr; + mDataSource = nullptr; + } +} + +void +nsXULTemplateBuilder::NodeWillBeDestroyed(const nsINode* aNode) +{ + // The call to RemoveObserver could release the last reference to + // |this|, so hold another reference. + RefPtr<nsXULTemplateBuilder> kungFuDeathGrip(this); + + // Break circular references + if (mQueryProcessor) + mQueryProcessor->Done(); + + mDataSource = nullptr; + mDB = nullptr; + mCompDB = nullptr; + + nsContentUtils::AddScriptRunner( + NewRunnableMethod(this, &nsXULTemplateBuilder::UninitTrue)); +} + + + + +//---------------------------------------------------------------------- +// +// Implementation methods +// + +nsresult +nsXULTemplateBuilder::LoadDataSources(nsIDocument* aDocument, + bool* aShouldDelayBuilding) +{ + NS_PRECONDITION(mRoot != nullptr, "not initialized"); + + nsresult rv; + bool isRDFQuery = false; + + // we'll set these again later, after we create a new composite ds + mDB = nullptr; + mCompDB = nullptr; + mDataSource = nullptr; + + *aShouldDelayBuilding = false; + + nsAutoString datasources; + mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::datasources, datasources); + + nsAutoString querytype; + mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::querytype, querytype); + + // create the query processor. The querytype attribute on the root element + // may be used to create one of a specific type. + + // XXX should non-chrome be restricted to specific names? + if (querytype.IsEmpty()) + querytype.AssignLiteral("rdf"); + + if (querytype.EqualsLiteral("rdf")) { + isRDFQuery = true; + mQueryProcessor = new nsXULTemplateQueryProcessorRDF(); + } + else if (querytype.EqualsLiteral("xml")) { + mQueryProcessor = new nsXULTemplateQueryProcessorXML(); + } + else if (querytype.EqualsLiteral("storage")) { + mQueryProcessor = new nsXULTemplateQueryProcessorStorage(); + } + else { + nsAutoCString cid(NS_QUERY_PROCESSOR_CONTRACTID_PREFIX); + AppendUTF16toUTF8(querytype, cid); + mQueryProcessor = do_CreateInstance(cid.get(), &rv); + + if (!mQueryProcessor) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_INVALID_QUERYPROCESSOR); + return rv; + } + } + + rv = LoadDataSourceUrls(aDocument, datasources, + isRDFQuery, aShouldDelayBuilding); + NS_ENSURE_SUCCESS(rv, rv); + + // Now set the database on the element, so that script writers can + // access it. + nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(aDocument); + if (xuldoc) + xuldoc->SetTemplateBuilderFor(mRoot, this); + + if (!mRoot->IsXULElement()) { + // Hmm. This must be an HTML element. Try to set it as a + // JS property "by hand". + InitHTMLTemplateRoot(); + } + + return NS_OK; +} + +nsresult +nsXULTemplateBuilder::LoadDataSourceUrls(nsIDocument* aDocument, + const nsAString& aDataSources, + bool aIsRDFQuery, + bool* aShouldDelayBuilding) +{ + // Grab the doc's principal... + nsIPrincipal *docPrincipal = aDocument->NodePrincipal(); + + NS_ASSERTION(docPrincipal == mRoot->NodePrincipal(), + "Principal mismatch? Which one to use?"); + + bool isTrusted = false; + nsresult rv = IsSystemPrincipal(docPrincipal, &isTrusted); + NS_ENSURE_SUCCESS(rv, rv); + + // Parse datasources: they are assumed to be a whitespace + // separated list of URIs; e.g., + // + // rdf:bookmarks rdf:history http://foo.bar.com/blah.cgi?baz=9 + // + nsIURI *docurl = aDocument->GetDocumentURI(); + + nsCOMPtr<nsIMutableArray> uriList = do_CreateInstance(NS_ARRAY_CONTRACTID); + if (!uriList) + return NS_ERROR_FAILURE; + + nsAutoString datasources(aDataSources); + uint32_t first = 0; + while (1) { + while (first < datasources.Length() && nsCRT::IsAsciiSpace(datasources.CharAt(first))) + ++first; + + if (first >= datasources.Length()) + break; + + uint32_t last = first; + while (last < datasources.Length() && !nsCRT::IsAsciiSpace(datasources.CharAt(last))) + ++last; + + nsAutoString uriStr; + datasources.Mid(uriStr, first, last - first); + first = last + 1; + + // A special 'dummy' datasource + if (uriStr.EqualsLiteral("rdf:null")) + continue; + + if (uriStr.CharAt(0) == '#') { + // ok, the datasource is certainly a node of the current document + nsCOMPtr<nsIDOMDocument> domdoc = do_QueryInterface(aDocument); + nsCOMPtr<nsIDOMElement> dsnode; + + domdoc->GetElementById(Substring(uriStr, 1), + getter_AddRefs(dsnode)); + + if (dsnode) + uriList->AppendElement(dsnode, false); + continue; + } + + // N.B. that `failure' (e.g., because it's an unknown + // protocol) leaves uriStr unaltered. + NS_MakeAbsoluteURI(uriStr, uriStr, docurl); + + nsCOMPtr<nsIURI> uri; + rv = NS_NewURI(getter_AddRefs(uri), uriStr); + if (NS_FAILED(rv) || !uri) + continue; // Necko will barf if our URI is weird + + // don't add the uri to the list if the document is not allowed to + // load it + if (!isTrusted && NS_FAILED(docPrincipal->CheckMayLoad(uri, true, false))) + continue; + + uriList->AppendElement(uri, false); + } + + nsCOMPtr<nsIDOMNode> rootNode = do_QueryInterface(mRoot); + rv = mQueryProcessor->GetDatasource(uriList, + rootNode, + isTrusted, + this, + aShouldDelayBuilding, + getter_AddRefs(mDataSource)); + NS_ENSURE_SUCCESS(rv, rv); + + if (aIsRDFQuery && mDataSource) { + // check if we were given an inference engine type + nsCOMPtr<nsIRDFInferDataSource> inferDB = do_QueryInterface(mDataSource); + if (inferDB) { + nsCOMPtr<nsIRDFDataSource> ds; + inferDB->GetBaseDataSource(getter_AddRefs(ds)); + if (ds) + mCompDB = do_QueryInterface(ds); + } + + if (!mCompDB) + mCompDB = do_QueryInterface(mDataSource); + + mDB = do_QueryInterface(mDataSource); + } + + if (!mDB && isTrusted) { + gRDFService->GetDataSource("rdf:local-store", getter_AddRefs(mDB)); + } + + return NS_OK; +} + +nsresult +nsXULTemplateBuilder::InitHTMLTemplateRoot() +{ + // Use XPConnect and the JS APIs to whack mDB and this as the + // 'database' and 'builder' properties onto aElement. + nsresult rv; + + nsCOMPtr<nsIDocument> doc = mRoot->GetComposedDoc(); + NS_ASSERTION(doc, "no document"); + if (! doc) + return NS_ERROR_UNEXPECTED; + + nsCOMPtr<nsIScriptGlobalObject> global = + do_QueryInterface(doc->GetWindow()); + if (! global) + return NS_ERROR_UNEXPECTED; + + nsCOMPtr<nsIGlobalObject> innerWin = + do_QueryInterface(doc->GetInnerWindow()); + + // We are going to run script via JS_SetProperty, so we need a script entry + // point, but as this is XUL related it does not appear in the HTML spec. + AutoEntryScript aes(innerWin, "nsXULTemplateBuilder creation", true); + JSContext* jscontext = aes.cx(); + + JS::Rooted<JS::Value> v(jscontext); + rv = nsContentUtils::WrapNative(jscontext, mRoot, mRoot, &v); + NS_ENSURE_SUCCESS(rv, rv); + + JS::Rooted<JSObject*> jselement(jscontext, v.toObjectOrNull()); + + if (mDB) { + // database + JS::Rooted<JS::Value> jsdatabase(jscontext); + rv = nsContentUtils::WrapNative(jscontext, mDB, + &NS_GET_IID(nsIRDFCompositeDataSource), + &jsdatabase); + NS_ENSURE_SUCCESS(rv, rv); + + bool ok = JS_SetProperty(jscontext, jselement, "database", jsdatabase); + NS_ASSERTION(ok, "unable to set database property"); + if (! ok) + return NS_ERROR_FAILURE; + } + + { + // builder + JS::Rooted<JS::Value> jsbuilder(jscontext); + rv = nsContentUtils::WrapNative(jscontext, + static_cast<nsIXULTemplateBuilder*>(this), + &NS_GET_IID(nsIXULTemplateBuilder), + &jsbuilder); + NS_ENSURE_SUCCESS(rv, rv); + + bool ok = JS_SetProperty(jscontext, jselement, "builder", jsbuilder); + if (! ok) + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +nsresult +nsXULTemplateBuilder::DetermineMatchedRule(nsIContent *aContainer, + nsIXULTemplateResult* aResult, + nsTemplateQuerySet* aQuerySet, + nsTemplateRule** aMatchedRule, + int16_t *aRuleIndex) +{ + // iterate through the rules and look for one that the result matches + int16_t count = aQuerySet->RuleCount(); + for (int16_t r = 0; r < count; r++) { + nsTemplateRule* rule = aQuerySet->GetRuleAt(r); + // If a tag was specified, it must match the tag of the container + // where content is being inserted. + nsIAtom* tag = rule->GetTag(); + if ((!aContainer || !tag || + tag == aContainer->NodeInfo()->NameAtom()) && + rule->CheckMatch(aResult)) { + *aMatchedRule = rule; + *aRuleIndex = r; + return NS_OK; + } + } + + *aRuleIndex = -1; + *aMatchedRule = nullptr; + return NS_OK; +} + +void +nsXULTemplateBuilder::ParseAttribute(const nsAString& aAttributeValue, + void (*aVariableCallback)(nsXULTemplateBuilder*, const nsAString&, void*), + void (*aTextCallback)(nsXULTemplateBuilder*, const nsAString&, void*), + void* aClosure) +{ + nsAString::const_iterator done_parsing; + aAttributeValue.EndReading(done_parsing); + + nsAString::const_iterator iter; + aAttributeValue.BeginReading(iter); + + nsAString::const_iterator mark(iter), backup(iter); + + for (; iter != done_parsing; backup = ++iter) { + // A variable is either prefixed with '?' (in the extended + // syntax) or "rdf:" (in the simple syntax). + bool isvar; + if (*iter == char16_t('?') && (++iter != done_parsing)) { + isvar = true; + } + else if ((*iter == char16_t('r') && (++iter != done_parsing)) && + (*iter == char16_t('d') && (++iter != done_parsing)) && + (*iter == char16_t('f') && (++iter != done_parsing)) && + (*iter == char16_t(':') && (++iter != done_parsing))) { + isvar = true; + } + else { + isvar = false; + } + + if (! isvar) { + // It's not a variable, or we ran off the end of the + // string after the initial variable prefix. Since we may + // have slurped down some characters before realizing that + // fact, back up to the point where we started. + iter = backup; + continue; + } + else if (backup != mark && aTextCallback) { + // Okay, we've found a variable, and there's some vanilla + // text that's been buffered up. Flush it. + (*aTextCallback)(this, Substring(mark, backup), aClosure); + } + + if (*iter == char16_t('?')) { + // Well, it was not really a variable, but "??". We use one + // question mark (the second one, actually) literally. + mark = iter; + continue; + } + + // Construct a substring that is the symbol we need to look up + // in the rule's symbol table. The symbol is terminated by a + // space character, a caret, or the end of the string, + // whichever comes first. + nsAString::const_iterator first(backup); + + char16_t c = 0; + while (iter != done_parsing) { + c = *iter; + if ((c == char16_t(' ')) || (c == char16_t('^'))) + break; + + ++iter; + } + + nsAString::const_iterator last(iter); + + // Back up so we don't consume the terminating character + // *unless* the terminating character was a caret: the caret + // means "concatenate with no space in between". + if (c != char16_t('^')) + --iter; + + (*aVariableCallback)(this, Substring(first, last), aClosure); + mark = iter; + ++mark; + } + + if (backup != mark && aTextCallback) { + // If there's any text left over, then fire the text callback + (*aTextCallback)(this, Substring(mark, backup), aClosure); + } +} + + +struct MOZ_STACK_CLASS SubstituteTextClosure { + SubstituteTextClosure(nsIXULTemplateResult* aResult, nsAString& aString) + : result(aResult), str(aString) {} + + // some datasources are lazily initialized or modified while values are + // being retrieved, causing results to be removed. Due to this, hold a + // strong reference to the result. + nsCOMPtr<nsIXULTemplateResult> result; + nsAString& str; +}; + +nsresult +nsXULTemplateBuilder::SubstituteText(nsIXULTemplateResult* aResult, + const nsAString& aAttributeValue, + nsAString& aString) +{ + // See if it's the special value "..." + if (aAttributeValue.EqualsLiteral("...")) { + aResult->GetId(aString); + return NS_OK; + } + + // Reasonable guess at how big it should be + aString.SetCapacity(aAttributeValue.Length()); + + SubstituteTextClosure closure(aResult, aString); + ParseAttribute(aAttributeValue, + SubstituteTextReplaceVariable, + SubstituteTextAppendText, + &closure); + + return NS_OK; +} + + +void +nsXULTemplateBuilder::SubstituteTextAppendText(nsXULTemplateBuilder* aThis, + const nsAString& aText, + void* aClosure) +{ + // Append aString to the closure's result + SubstituteTextClosure* c = static_cast<SubstituteTextClosure*>(aClosure); + c->str.Append(aText); +} + +void +nsXULTemplateBuilder::SubstituteTextReplaceVariable(nsXULTemplateBuilder* aThis, + const nsAString& aVariable, + void* aClosure) +{ + // Substitute the value for the variable and append to the + // closure's result. + SubstituteTextClosure* c = static_cast<SubstituteTextClosure*>(aClosure); + + nsAutoString replacementText; + + // The symbol "rdf:*" is special, and means "this guy's URI" + if (aVariable.EqualsLiteral("rdf:*")){ + c->result->GetId(replacementText); + } + else { + // Got a variable; get the value it's assigned to + nsCOMPtr<nsIAtom> var = NS_Atomize(aVariable); + c->result->GetBindingFor(var, replacementText); + } + + c->str += replacementText; +} + +bool +nsXULTemplateBuilder::IsTemplateElement(nsIContent* aContent) +{ + return aContent->NodeInfo()->Equals(nsGkAtoms::_template, + kNameSpaceID_XUL); +} + +nsresult +nsXULTemplateBuilder::GetTemplateRoot(nsIContent** aResult) +{ + NS_PRECONDITION(mRoot != nullptr, "not initialized"); + if (! mRoot) + return NS_ERROR_NOT_INITIALIZED; + + // First, check and see if the root has a template attribute. This + // allows a template to be specified "out of line"; e.g., + // + // <window> + // <foo template="MyTemplate">...</foo> + // <template id="MyTemplate">...</template> + // </window> + // + nsAutoString templateID; + mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::_template, templateID); + + if (! templateID.IsEmpty()) { + nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(mRoot->GetComposedDoc()); + if (! domDoc) + return NS_OK; + + nsCOMPtr<nsIDOMElement> domElement; + domDoc->GetElementById(templateID, getter_AddRefs(domElement)); + + if (domElement) { + nsCOMPtr<nsIContent> content = do_QueryInterface(domElement); + NS_ENSURE_STATE(content && + !nsContentUtils::ContentIsDescendantOf(mRoot, + content)); + content.forget(aResult); + return NS_OK; + } + } + + // If root node has no template attribute, then look for a child + // node which is a template tag. + for (nsIContent* child = mRoot->GetFirstChild(); + child; + child = child->GetNextSibling()) { + + if (IsTemplateElement(child)) { + NS_ADDREF(*aResult = child); + return NS_OK; + } + } + + // Look through the anonymous children as well. Although FlattenedChildIterator + // will find a template element that has been placed in an insertion point, many + // bindings do not have a specific insertion point for the template element, which + // would cause it to not be part of the flattened content tree. The check above to + // check the explicit children as well handles this case. + FlattenedChildIterator iter(mRoot); + for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) { + if (IsTemplateElement(child)) { + NS_ADDREF(*aResult = child); + return NS_OK; + } + } + + *aResult = nullptr; + return NS_OK; +} + +nsresult +nsXULTemplateBuilder::CompileQueries() +{ + nsCOMPtr<nsIContent> tmpl; + GetTemplateRoot(getter_AddRefs(tmpl)); + if (! tmpl) + return NS_OK; + + if (! mRoot) + return NS_ERROR_NOT_INITIALIZED; + + // Determine if there are any special settings we need to observe + mFlags = 0; + + nsAutoString flags; + mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::flags, flags); + + // if the dont-test-empty flag is set, containers should not be checked to + // see if they are empty. If dont-recurse is set, then don't process the + // template recursively and only show one level of results. The logging + // flag logs errors and results to the console, which is useful when + // debugging templates. + nsWhitespaceTokenizer tokenizer(flags); + while (tokenizer.hasMoreTokens()) { + const nsDependentSubstring& token(tokenizer.nextToken()); + if (token.EqualsLiteral("dont-test-empty")) + mFlags |= eDontTestEmpty; + else if (token.EqualsLiteral("dont-recurse")) + mFlags |= eDontRecurse; + else if (token.EqualsLiteral("logging")) + mFlags |= eLoggingEnabled; + } + + // always enable logging if the debug setting is used + if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) + mFlags |= eLoggingEnabled; + + nsCOMPtr<nsIDOMNode> rootnode = do_QueryInterface(mRoot); + nsresult rv = + mQueryProcessor->InitializeForBuilding(mDataSource, this, rootnode); + if (NS_FAILED(rv)) + return rv; + + // Set the "container" and "member" variables, if the user has specified + // them. The container variable may be specified with the container + // attribute on the <template> and the member variable may be specified + // using the member attribute or the value of the uri attribute inside the + // first action body in the template. If not specified, the container + // variable defaults to '?uri' and the member variable defaults to '?' or + // 'rdf:*' for simple queries. + + // For RDF queries, the container variable may also be set via the + // <content> tag. + + nsAutoString containervar; + tmpl->GetAttr(kNameSpaceID_None, nsGkAtoms::container, containervar); + + if (containervar.IsEmpty()) + mRefVariable = NS_Atomize("?uri"); + else + mRefVariable = NS_Atomize(containervar); + + nsAutoString membervar; + tmpl->GetAttr(kNameSpaceID_None, nsGkAtoms::member, membervar); + + if (membervar.IsEmpty()) + mMemberVariable = nullptr; + else + mMemberVariable = NS_Atomize(membervar); + + nsTemplateQuerySet* queryset = new nsTemplateQuerySet(0); + if (!mQuerySets.AppendElement(queryset)) { + delete queryset; + return NS_ERROR_OUT_OF_MEMORY; + } + + bool canUseTemplate = false; + int32_t priority = 0; + rv = CompileTemplate(tmpl, queryset, false, &priority, &canUseTemplate); + + if (NS_FAILED(rv) || !canUseTemplate) { + for (int32_t q = mQuerySets.Length() - 1; q >= 0; q--) { + nsTemplateQuerySet* qs = mQuerySets[q]; + delete qs; + } + mQuerySets.Clear(); + } + + mQueriesCompiled = true; + + return NS_OK; +} + +nsresult +nsXULTemplateBuilder::CompileTemplate(nsIContent* aTemplate, + nsTemplateQuerySet* aQuerySet, + bool aIsQuerySet, + int32_t* aPriority, + bool* aCanUseTemplate) +{ + NS_ASSERTION(aQuerySet, "No queryset supplied"); + + nsresult rv = NS_OK; + + bool isQuerySetMode = false; + bool hasQuerySet = false, hasRule = false, hasQuery = false; + + for (nsIContent* rulenode = aTemplate->GetFirstChild(); + rulenode; + rulenode = rulenode->GetNextSibling()) { + + mozilla::dom::NodeInfo *ni = rulenode->NodeInfo(); + + // don't allow more queries than can be supported + if (*aPriority == INT16_MAX) + return NS_ERROR_FAILURE; + + // XXXndeakin queryset isn't a good name for this tag since it only + // ever contains one query + if (!aIsQuerySet && ni->Equals(nsGkAtoms::queryset, kNameSpaceID_XUL)) { + if (hasRule || hasQuery) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_INVALID_QUERYSET); + continue; + } + + isQuerySetMode = true; + + // only create a queryset for those after the first since the + // first one is always created by CompileQueries + if (hasQuerySet) { + aQuerySet = new nsTemplateQuerySet(++*aPriority); + + // once the queryset is appended to the mQuerySets list, it + // will be removed by CompileQueries if an error occurs + if (!mQuerySets.AppendElement(aQuerySet)) { + delete aQuerySet; + return NS_ERROR_OUT_OF_MEMORY; + } + } + + hasQuerySet = true; + + rv = CompileTemplate(rulenode, aQuerySet, true, aPriority, aCanUseTemplate); + if (NS_FAILED(rv)) + return rv; + } + + // once a queryset is used, everything must be a queryset + if (isQuerySetMode) + continue; + + if (ni->Equals(nsGkAtoms::rule, kNameSpaceID_XUL)) { + nsCOMPtr<nsIContent> action; + nsXULContentUtils::FindChildByTag(rulenode, + kNameSpaceID_XUL, + nsGkAtoms::action, + getter_AddRefs(action)); + + if (action){ + nsCOMPtr<nsIAtom> memberVariable = mMemberVariable; + if (!memberVariable) { + memberVariable = DetermineMemberVariable(action); + if (!memberVariable) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_NO_MEMBERVAR); + continue; + } + } + + if (hasQuery) { + nsCOMPtr<nsIAtom> tag; + DetermineRDFQueryRef(aQuerySet->mQueryNode, + getter_AddRefs(tag)); + if (tag) + aQuerySet->SetTag(tag); + + if (! aQuerySet->mCompiledQuery) { + nsCOMPtr<nsIDOMNode> query(do_QueryInterface(aQuerySet->mQueryNode)); + + rv = mQueryProcessor->CompileQuery(this, query, + mRefVariable, memberVariable, + getter_AddRefs(aQuerySet->mCompiledQuery)); + if (NS_FAILED(rv)) + return rv; + } + + if (aQuerySet->mCompiledQuery) { + rv = CompileExtendedQuery(rulenode, action, memberVariable, + aQuerySet); + if (NS_FAILED(rv)) + return rv; + + *aCanUseTemplate = true; + } + } + else { + // backwards-compatible RDF template syntax where there is + // an <action> node but no <query> node. In this case, + // use the conditions as if it was the query. + + nsCOMPtr<nsIContent> conditions; + nsXULContentUtils::FindChildByTag(rulenode, + kNameSpaceID_XUL, + nsGkAtoms::conditions, + getter_AddRefs(conditions)); + + if (conditions) { + // create a new queryset if one hasn't been created already + if (hasQuerySet) { + aQuerySet = new nsTemplateQuerySet(++*aPriority); + if (!mQuerySets.AppendElement(aQuerySet)) { + delete aQuerySet; + return NS_ERROR_OUT_OF_MEMORY; + } + } + + nsCOMPtr<nsIAtom> tag; + DetermineRDFQueryRef(conditions, getter_AddRefs(tag)); + if (tag) + aQuerySet->SetTag(tag); + + hasQuerySet = true; + + nsCOMPtr<nsIDOMNode> conditionsnode(do_QueryInterface(conditions)); + + aQuerySet->mQueryNode = conditions; + rv = mQueryProcessor->CompileQuery(this, conditionsnode, + mRefVariable, + memberVariable, + getter_AddRefs(aQuerySet->mCompiledQuery)); + if (NS_FAILED(rv)) + return rv; + + if (aQuerySet->mCompiledQuery) { + rv = CompileExtendedQuery(rulenode, action, memberVariable, + aQuerySet); + if (NS_FAILED(rv)) + return rv; + + *aCanUseTemplate = true; + } + } + } + } + else { + if (hasQuery) + continue; + + // a new queryset must always be created in this case + if (hasQuerySet) { + aQuerySet = new nsTemplateQuerySet(++*aPriority); + if (!mQuerySets.AppendElement(aQuerySet)) { + delete aQuerySet; + return NS_ERROR_OUT_OF_MEMORY; + } + } + + hasQuerySet = true; + + rv = CompileSimpleQuery(rulenode, aQuerySet, aCanUseTemplate); + if (NS_FAILED(rv)) + return rv; + } + + hasRule = true; + } + else if (ni->Equals(nsGkAtoms::query, kNameSpaceID_XUL)) { + if (hasQuery) + continue; + + aQuerySet->mQueryNode = rulenode; + hasQuery = true; + } + else if (ni->Equals(nsGkAtoms::action, kNameSpaceID_XUL)) { + // the query must appear before the action + if (! hasQuery) + continue; + + nsCOMPtr<nsIAtom> tag; + DetermineRDFQueryRef(aQuerySet->mQueryNode, getter_AddRefs(tag)); + if (tag) + aQuerySet->SetTag(tag); + + nsCOMPtr<nsIAtom> memberVariable = mMemberVariable; + if (!memberVariable) { + memberVariable = DetermineMemberVariable(rulenode); + if (!memberVariable) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_NO_MEMBERVAR); + continue; + } + } + + nsCOMPtr<nsIDOMNode> query(do_QueryInterface(aQuerySet->mQueryNode)); + + rv = mQueryProcessor->CompileQuery(this, query, + mRefVariable, memberVariable, + getter_AddRefs(aQuerySet->mCompiledQuery)); + + if (aQuerySet->mCompiledQuery) { + nsTemplateRule* rule = aQuerySet->NewRule(aTemplate, rulenode, aQuerySet); + if (! rule) + return NS_ERROR_OUT_OF_MEMORY; + + rule->SetVars(mRefVariable, memberVariable); + + *aCanUseTemplate = true; + + return NS_OK; + } + } + } + + if (! hasRule && ! hasQuery && ! hasQuerySet) { + // if no rules are specified in the template, then the contents of the + // <template> tag are the one-and-only template. + rv = CompileSimpleQuery(aTemplate, aQuerySet, aCanUseTemplate); + } + + return rv; +} + +nsresult +nsXULTemplateBuilder::CompileExtendedQuery(nsIContent* aRuleElement, + nsIContent* aActionElement, + nsIAtom* aMemberVariable, + nsTemplateQuerySet* aQuerySet) +{ + // Compile an "extended" <template> rule. An extended rule may have + // a <conditions> child, an <action> child, and a <bindings> child. + nsresult rv; + + nsTemplateRule* rule = aQuerySet->NewRule(aRuleElement, aActionElement, aQuerySet); + if (! rule) + return NS_ERROR_OUT_OF_MEMORY; + + nsCOMPtr<nsIContent> conditions; + nsXULContentUtils::FindChildByTag(aRuleElement, + kNameSpaceID_XUL, + nsGkAtoms::conditions, + getter_AddRefs(conditions)); + + // allow the conditions to be placed directly inside the rule + if (!conditions) + conditions = aRuleElement; + + rv = CompileConditions(rule, conditions); + // If the rule compilation failed, then we have to bail. + if (NS_FAILED(rv)) { + aQuerySet->RemoveRule(rule); + return rv; + } + + rule->SetVars(mRefVariable, aMemberVariable); + + // If we've got bindings, add 'em. + nsCOMPtr<nsIContent> bindings; + nsXULContentUtils::FindChildByTag(aRuleElement, + kNameSpaceID_XUL, + nsGkAtoms::bindings, + getter_AddRefs(bindings)); + + // allow bindings to be placed directly inside rule + if (!bindings) + bindings = aRuleElement; + + rv = CompileBindings(rule, bindings); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +already_AddRefed<nsIAtom> +nsXULTemplateBuilder::DetermineMemberVariable(nsIContent* aElement) +{ + // recursively iterate over the children looking for an element + // with uri="?..." + for (nsIContent* child = aElement->GetFirstChild(); + child; + child = child->GetNextSibling()) { + nsAutoString uri; + child->GetAttr(kNameSpaceID_None, nsGkAtoms::uri, uri); + if (!uri.IsEmpty() && uri[0] == char16_t('?')) { + return NS_Atomize(uri); + } + + nsCOMPtr<nsIAtom> result = DetermineMemberVariable(child); + if (result) { + return result.forget(); + } + } + + return nullptr; +} + +void +nsXULTemplateBuilder::DetermineRDFQueryRef(nsIContent* aQueryElement, nsIAtom** aTag) +{ + // check for a tag + nsCOMPtr<nsIContent> content; + nsXULContentUtils::FindChildByTag(aQueryElement, + kNameSpaceID_XUL, + nsGkAtoms::content, + getter_AddRefs(content)); + + if (! content) { + // look for older treeitem syntax as well + nsXULContentUtils::FindChildByTag(aQueryElement, + kNameSpaceID_XUL, + nsGkAtoms::treeitem, + getter_AddRefs(content)); + } + + if (content) { + nsAutoString uri; + content->GetAttr(kNameSpaceID_None, nsGkAtoms::uri, uri); + + if (!uri.IsEmpty()) + mRefVariable = NS_Atomize(uri); + + nsAutoString tag; + content->GetAttr(kNameSpaceID_None, nsGkAtoms::tag, tag); + + if (!tag.IsEmpty()) + *aTag = NS_Atomize(tag).take(); + } +} + +nsresult +nsXULTemplateBuilder::CompileSimpleQuery(nsIContent* aRuleElement, + nsTemplateQuerySet* aQuerySet, + bool* aCanUseTemplate) +{ + // compile a simple query, which is a query with no <query> or + // <conditions>. This means that a default query is used. + nsCOMPtr<nsIDOMNode> query(do_QueryInterface(aRuleElement)); + + nsCOMPtr<nsIAtom> memberVariable; + if (mMemberVariable) + memberVariable = mMemberVariable; + else + memberVariable = NS_Atomize("rdf:*"); + + // since there is no <query> node for a simple query, the query node will + // be either the <rule> node if multiple rules are used, or the <template> node. + aQuerySet->mQueryNode = aRuleElement; + nsresult rv = mQueryProcessor->CompileQuery(this, query, + mRefVariable, memberVariable, + getter_AddRefs(aQuerySet->mCompiledQuery)); + if (NS_FAILED(rv)) + return rv; + + if (! aQuerySet->mCompiledQuery) { + *aCanUseTemplate = false; + return NS_OK; + } + + nsTemplateRule* rule = aQuerySet->NewRule(aRuleElement, aRuleElement, aQuerySet); + if (! rule) + return NS_ERROR_OUT_OF_MEMORY; + + rule->SetVars(mRefVariable, memberVariable); + + nsAutoString tag; + aRuleElement->GetAttr(kNameSpaceID_None, nsGkAtoms::parent, tag); + + if (!tag.IsEmpty()) { + nsCOMPtr<nsIAtom> tagatom = NS_Atomize(tag); + aQuerySet->SetTag(tagatom); + } + + *aCanUseTemplate = true; + + return AddSimpleRuleBindings(rule, aRuleElement); +} + +nsresult +nsXULTemplateBuilder::CompileConditions(nsTemplateRule* aRule, + nsIContent* aCondition) +{ + nsAutoString tag; + aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::parent, tag); + + if (!tag.IsEmpty()) { + nsCOMPtr<nsIAtom> tagatom = NS_Atomize(tag); + aRule->SetTag(tagatom); + } + + nsTemplateCondition* currentCondition = nullptr; + + for (nsIContent* node = aCondition->GetFirstChild(); + node; + node = node->GetNextSibling()) { + + if (node->NodeInfo()->Equals(nsGkAtoms::where, kNameSpaceID_XUL)) { + nsresult rv = CompileWhereCondition(aRule, node, ¤tCondition); + if (NS_FAILED(rv)) + return rv; + } + } + + return NS_OK; +} + +nsresult +nsXULTemplateBuilder::CompileWhereCondition(nsTemplateRule* aRule, + nsIContent* aCondition, + nsTemplateCondition** aCurrentCondition) +{ + // Compile a <where> condition, which must be of the form: + // + // <where subject="?var1|string" rel="relation" value="?var2|string" /> + // + // The value of rel may be: + // equal - subject must be equal to object + // notequal - subject must not be equal to object + // less - subject must be less than object + // greater - subject must be greater than object + // startswith - subject must start with object + // endswith - subject must end with object + // contains - subject must contain object + // Comparisons are done as strings unless the subject is an integer. + + // subject + nsAutoString subject; + aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::subject, subject); + if (subject.IsEmpty()) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_WHERE_NO_SUBJECT); + return NS_OK; + } + + nsCOMPtr<nsIAtom> svar; + if (subject[0] == char16_t('?')) + svar = NS_Atomize(subject); + + nsAutoString relstring; + aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, relstring); + if (relstring.IsEmpty()) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_WHERE_NO_RELATION); + return NS_OK; + } + + // object + nsAutoString value; + aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::value, value); + if (value.IsEmpty()) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_WHERE_NO_VALUE); + return NS_OK; + } + + // multiple + bool shouldMultiple = + aCondition->AttrValueIs(kNameSpaceID_None, nsGkAtoms::multiple, + nsGkAtoms::_true, eCaseMatters); + + nsCOMPtr<nsIAtom> vvar; + if (!shouldMultiple && (value[0] == char16_t('?'))) { + vvar = NS_Atomize(value); + } + + // ignorecase + bool shouldIgnoreCase = + aCondition->AttrValueIs(kNameSpaceID_None, nsGkAtoms::ignorecase, + nsGkAtoms::_true, eCaseMatters); + + // negate + bool shouldNegate = + aCondition->AttrValueIs(kNameSpaceID_None, nsGkAtoms::negate, + nsGkAtoms::_true, eCaseMatters); + + nsTemplateCondition* condition; + + if (svar && vvar) { + condition = new nsTemplateCondition(svar, relstring, vvar, + shouldIgnoreCase, shouldNegate); + } + else if (svar && !value.IsEmpty()) { + condition = new nsTemplateCondition(svar, relstring, value, + shouldIgnoreCase, shouldNegate, shouldMultiple); + } + else if (vvar) { + condition = new nsTemplateCondition(subject, relstring, vvar, + shouldIgnoreCase, shouldNegate); + } + else { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_WHERE_NO_VAR); + return NS_OK; + } + + if (*aCurrentCondition) { + (*aCurrentCondition)->SetNext(condition); + } + else { + aRule->SetCondition(condition); + } + + *aCurrentCondition = condition; + + return NS_OK; +} + +nsresult +nsXULTemplateBuilder::CompileBindings(nsTemplateRule* aRule, nsIContent* aBindings) +{ + // Add an extended rule's bindings. + nsresult rv; + + for (nsIContent* binding = aBindings->GetFirstChild(); + binding; + binding = binding->GetNextSibling()) { + + if (binding->NodeInfo()->Equals(nsGkAtoms::binding, + kNameSpaceID_XUL)) { + rv = CompileBinding(aRule, binding); + if (NS_FAILED(rv)) + return rv; + } + } + + aRule->AddBindingsToQueryProcessor(mQueryProcessor); + + return NS_OK; +} + + +nsresult +nsXULTemplateBuilder::CompileBinding(nsTemplateRule* aRule, + nsIContent* aBinding) +{ + // Compile a <binding> "condition", which must be of the form: + // + // <binding subject="?var1" + // predicate="resource" + // object="?var2" /> + // + // XXXwaterson Some day it would be cool to allow the 'predicate' + // to be bound to a variable. + + // subject + nsAutoString subject; + aBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::subject, subject); + if (subject.IsEmpty()) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BINDING_BAD_SUBJECT); + return NS_OK; + } + + nsCOMPtr<nsIAtom> svar; + if (subject[0] == char16_t('?')) { + svar = NS_Atomize(subject); + } + else { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BINDING_BAD_SUBJECT); + return NS_OK; + } + + // predicate + nsAutoString predicate; + aBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::predicate, predicate); + if (predicate.IsEmpty()) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BINDING_BAD_PREDICATE); + return NS_OK; + } + + // object + nsAutoString object; + aBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::object, object); + + if (object.IsEmpty()) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BINDING_BAD_OBJECT); + return NS_OK; + } + + nsCOMPtr<nsIAtom> ovar; + if (object[0] == char16_t('?')) { + ovar = NS_Atomize(object); + } + else { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BINDING_BAD_OBJECT); + return NS_OK; + } + + return aRule->AddBinding(svar, predicate, ovar); +} + +nsresult +nsXULTemplateBuilder::AddSimpleRuleBindings(nsTemplateRule* aRule, + nsIContent* aElement) +{ + // Crawl the content tree of a "simple" rule, adding a variable + // assignment for any attribute whose value is "rdf:". + + AutoTArray<nsIContent*, 8> elements; + + if (elements.AppendElement(aElement) == nullptr) + return NS_ERROR_OUT_OF_MEMORY; + + while (elements.Length()) { + // Pop the next element off the stack + uint32_t i = elements.Length() - 1; + nsIContent* element = elements[i]; + elements.RemoveElementAt(i); + + // Iterate through its attributes, looking for substitutions + // that we need to add as bindings. + uint32_t count = element->GetAttrCount(); + + for (i = 0; i < count; ++i) { + const nsAttrName* name = element->GetAttrNameAt(i); + + if (!name->Equals(nsGkAtoms::id, kNameSpaceID_None) && + !name->Equals(nsGkAtoms::uri, kNameSpaceID_None)) { + nsAutoString value; + element->GetAttr(name->NamespaceID(), name->LocalName(), value); + + // Scan the attribute for variables, adding a binding for + // each one. + ParseAttribute(value, AddBindingsFor, nullptr, aRule); + } + } + + // Push kids onto the stack, and search them next. + for (nsIContent* child = element->GetLastChild(); + child; + child = child->GetPreviousSibling()) { + + if (!elements.AppendElement(child)) + return NS_ERROR_OUT_OF_MEMORY; + } + } + + aRule->AddBindingsToQueryProcessor(mQueryProcessor); + + return NS_OK; +} + +void +nsXULTemplateBuilder::AddBindingsFor(nsXULTemplateBuilder* aThis, + const nsAString& aVariable, + void* aClosure) +{ + // We should *only* be recieving "rdf:"-style variables. Make + // sure... + if (!StringBeginsWith(aVariable, NS_LITERAL_STRING("rdf:"))) + return; + + nsTemplateRule* rule = static_cast<nsTemplateRule*>(aClosure); + + nsCOMPtr<nsIAtom> var = NS_Atomize(aVariable); + + // Strip it down to the raw RDF property by clobbering the "rdf:" + // prefix + nsAutoString property; + property.Assign(Substring(aVariable, uint32_t(4), aVariable.Length() - 4)); + + if (! rule->HasBinding(rule->GetMemberVariable(), property, var)) + // In the simple syntax, the binding is always from the + // member variable, through the property, to the target. + rule->AddBinding(rule->GetMemberVariable(), property, var); +} + + +nsresult +nsXULTemplateBuilder::IsSystemPrincipal(nsIPrincipal *principal, bool *result) +{ + if (!gSystemPrincipal) + return NS_ERROR_UNEXPECTED; + + *result = (principal == gSystemPrincipal); + return NS_OK; +} + +bool +nsXULTemplateBuilder::IsActivated(nsIRDFResource *aResource) +{ + for (ActivationEntry *entry = mTop; + entry != nullptr; + entry = entry->mPrevious) { + if (entry->mResource == aResource) + return true; + } + return false; +} + +nsresult +nsXULTemplateBuilder::GetResultResource(nsIXULTemplateResult* aResult, + nsIRDFResource** aResource) +{ + // get the resource for a result by checking its resource property. If it + // is not set, check the id. This allows non-chrome implementations to + // avoid having to use RDF. + nsresult rv = aResult->GetResource(aResource); + if (NS_FAILED(rv)) + return rv; + + if (! *aResource) { + nsAutoString id; + rv = aResult->GetId(id); + if (NS_FAILED(rv)) + return rv; + + return gRDFService->GetUnicodeResource(id, aResource); + } + + return rv; +} + + +void +nsXULTemplateBuilder::OutputMatchToLog(nsIRDFResource* aId, + nsTemplateMatch* aMatch, + bool aIsNew) +{ + int32_t priority = aMatch->QuerySetPriority() + 1; + int32_t activePriority = -1; + + nsAutoString msg; + + nsAutoString templateid; + mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::id, templateid); + msg.AppendLiteral("In template"); + if (!templateid.IsEmpty()) { + msg.AppendLiteral(" with id "); + msg.Append(templateid); + } + + nsAutoString refstring; + aMatch->mResult->GetBindingFor(mRefVariable, refstring); + if (!refstring.IsEmpty()) { + msg.AppendLiteral(" using ref "); + msg.Append(refstring); + } + + msg.AppendLiteral("\n "); + + nsTemplateMatch* match = nullptr; + if (mMatchMap.Get(aId, &match)){ + while (match) { + if (match == aMatch) + break; + if (match->IsActive() && + match->GetContainer() == aMatch->GetContainer()) { + activePriority = match->QuerySetPriority() + 1; + break; + } + match = match->mNext; + } + } + + if (aMatch->IsActive()) { + if (aIsNew) { + msg.AppendLiteral("New active result for query "); + msg.AppendInt(priority); + msg.AppendLiteral(" matching rule "); + msg.AppendInt(aMatch->RuleIndex() + 1); + } + else { + msg.AppendLiteral("Removed active result for query "); + msg.AppendInt(priority); + if (activePriority > 0) { + msg.AppendLiteral(" (new active query is "); + msg.AppendInt(activePriority); + msg.Append(')'); + } + else { + msg.AppendLiteral(" (no new active query)"); + } + } + } + else { + if (aIsNew) { + msg.AppendLiteral("New inactive result for query "); + msg.AppendInt(priority); + if (activePriority > 0) { + msg.AppendLiteral(" (overridden by query "); + msg.AppendInt(activePriority); + msg.Append(')'); + } + else { + msg.AppendLiteral(" (didn't match a rule)"); + } + } + else { + msg.AppendLiteral("Removed inactive result for query "); + msg.AppendInt(priority); + if (activePriority > 0) { + msg.AppendLiteral(" (active query is "); + msg.AppendInt(activePriority); + msg.Append(')'); + } + else { + msg.AppendLiteral(" (no active query)"); + } + } + } + + nsAutoString idstring; + nsXULContentUtils::GetTextForNode(aId, idstring); + msg.AppendLiteral(": "); + msg.Append(idstring); + + nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID); + if (cs) + cs->LogStringMessage(msg.get()); +} diff --git a/dom/xul/templates/nsXULTemplateBuilder.h b/dom/xul/templates/nsXULTemplateBuilder.h new file mode 100644 index 000000000..7da8ffc98 --- /dev/null +++ b/dom/xul/templates/nsXULTemplateBuilder.h @@ -0,0 +1,502 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsXULTemplateBuilder_h__ +#define nsXULTemplateBuilder_h__ + +#include "nsStubDocumentObserver.h" +#include "nsIScriptSecurityManager.h" +#include "nsIObserver.h" +#include "nsIRDFCompositeDataSource.h" +#include "nsIRDFContainer.h" +#include "nsIRDFContainerUtils.h" +#include "nsIRDFDataSource.h" +#include "nsIRDFObserver.h" +#include "nsIRDFService.h" +#include "nsIXULTemplateBuilder.h" + +#include "nsCOMArray.h" +#include "nsTArray.h" +#include "nsDataHashtable.h" +#include "nsTemplateRule.h" +#include "nsTemplateMatch.h" +#include "nsIXULTemplateQueryProcessor.h" +#include "nsCycleCollectionParticipant.h" + +#include "mozilla/Logging.h" +extern mozilla::LazyLogModule gXULTemplateLog; + +class nsIContent; +class nsIObserverService; +class nsIRDFCompositeDataSource; + +/** + * An object that translates an RDF graph into a presentation using a + * set of rules. + */ +class nsXULTemplateBuilder : public nsIXULTemplateBuilder, + public nsIObserver, + public nsStubDocumentObserver +{ + void CleanUp(bool aIsFinal); + void DestroyMatchMap(); + +public: + nsXULTemplateBuilder(); + + nsresult InitGlobals(); + + /** + * Clear the template builder structures. The aIsFinal flag is set to true + * when the template is going away. + */ + virtual void Uninit(bool aIsFinal); + + // nsISupports interface + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXULTemplateBuilder, + nsIXULTemplateBuilder) + + // nsIXULTemplateBuilder interface + NS_DECL_NSIXULTEMPLATEBUILDER + + // nsIObserver Interface + NS_DECL_NSIOBSERVER + + // nsIMutationObserver + NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED + NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED + NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED + + /** + * Remove an old result and/or add a new result. This method will retrieve + * the set of containers where the result could be inserted and either add + * the new result to those containers, or remove the result from those + * containers. UpdateResultInContainer is called for each container. + * + * @param aOldResult result to remove + * @param aNewResult result to add + * @param aQueryNode query node for new result + */ + nsresult + UpdateResult(nsIXULTemplateResult* aOldResult, + nsIXULTemplateResult* aNewResult, + nsIDOMNode* aQueryNode); + + /** + * Remove an old result and/or add a new result from a specific container. + * + * @param aOldResult result to remove + * @param aNewResult result to add + * @param aQueryNode queryset for the new result + * @param aOldId id of old result + * @param aNewId id of new result + * @param aInsertionPoint container to remove or add result inside + */ + nsresult + UpdateResultInContainer(nsIXULTemplateResult* aOldResult, + nsIXULTemplateResult* aNewResult, + nsTemplateQuerySet* aQuerySet, + nsIRDFResource* aOldId, + nsIRDFResource* aNewId, + nsIContent* aInsertionPoint); + + nsresult + ComputeContainmentProperties(); + + static bool + IsTemplateElement(nsIContent* aContent); + + virtual nsresult + RebuildAll() = 0; // must be implemented by subclasses + + void RunnableRebuild() { Rebuild(); } + void RunnableLoadAndRebuild() { + Uninit(false); // Reset results + + nsCOMPtr<nsIDocument> doc = mRoot ? mRoot->GetComposedDoc() : nullptr; + if (doc) { + bool shouldDelay; + LoadDataSources(doc, &shouldDelay); + if (!shouldDelay) { + Rebuild(); + } + } + } + + // mRoot should not be cleared until after Uninit is finished so that + // generated content can be removed during uninitialization. + void UninitFalse() { Uninit(false); mRoot = nullptr; } + void UninitTrue() { Uninit(true); mRoot = nullptr; } + + /** + * Find the <template> tag that applies for this builder + */ + nsresult + GetTemplateRoot(nsIContent** aResult); + + /** + * Compile the template's queries + */ + nsresult + CompileQueries(); + + /** + * Compile the template given a <template> in aTemplate. This function + * is called recursively to handle queries inside a queryset. For the + * outer pass, aIsQuerySet will be false, while the inner pass this will + * be true. + * + * aCanUseTemplate will be set to true if the template's queries could be + * compiled, and false otherwise. If false, the entire template is + * invalid. + * + * @param aTemplate <template> to compile + * @param aQuerySet first queryset + * @param aIsQuerySet true if + * @param aPriority the queryset index, incremented when a new one is added + * @param aCanUseTemplate true if template is valid + */ + nsresult + CompileTemplate(nsIContent* aTemplate, + nsTemplateQuerySet* aQuerySet, + bool aIsQuerySet, + int32_t* aPriority, + bool* aCanUseTemplate); + + /** + * Compile a query using the extended syntax. For backwards compatible RDF + * syntax where there is no <query>, the <conditions> becomes the query. + * + * @param aRuleElement <rule> element + * @param aActionElement <action> element + * @param aMemberVariable member variable for the query + * @param aQuerySet the queryset + */ + nsresult + CompileExtendedQuery(nsIContent* aRuleElement, + nsIContent* aActionElement, + nsIAtom* aMemberVariable, + nsTemplateQuerySet* aQuerySet); + + /** + * Determine the ref variable and tag from inside a RDF query. + */ + void DetermineRDFQueryRef(nsIContent* aQueryElement, nsIAtom** tag); + + /** + * Determine the member variable from inside an action body. It will be + * the value of the uri attribute on a node. + */ + already_AddRefed<nsIAtom> DetermineMemberVariable(nsIContent* aElement); + + /** + * Compile a simple query. A simple query is one that doesn't have a + * <query> and should use a default query which would normally just return + * a list of children of the reference point. + * + * @param aRuleElement the <rule> + * @param aQuerySet the query set + * @param aCanUseTemplate true if the query is valid + */ + nsresult + CompileSimpleQuery(nsIContent* aRuleElement, + nsTemplateQuerySet* aQuerySet, + bool* aCanUseTemplate); + + /** + * Compile the <conditions> tag in a rule + * + * @param aRule template rule + * @param aConditions <conditions> element + */ + nsresult + CompileConditions(nsTemplateRule* aRule, nsIContent* aConditions); + + /** + * Compile a <where> tag in a condition. The caller should set + * *aCurrentCondition to null for the first condition. This value will be + * updated to point to the new condition before returning. The conditions + * will be added to the rule aRule by this method. + * + * @param aRule template rule + * @param aCondition <where> element + * @param aCurrentCondition compiled condition + */ + nsresult + CompileWhereCondition(nsTemplateRule* aRule, + nsIContent* aCondition, + nsTemplateCondition** aCurrentCondition); + + /** + * Compile the <bindings> for an extended template syntax rule. + */ + nsresult + CompileBindings(nsTemplateRule* aRule, nsIContent* aBindings); + + /** + * Compile a single binding for an extended template syntax rule. + */ + nsresult + CompileBinding(nsTemplateRule* aRule, nsIContent* aBinding); + + /** + * Add automatic bindings for simple rules + */ + nsresult + AddSimpleRuleBindings(nsTemplateRule* aRule, nsIContent* aElement); + + static void + AddBindingsFor(nsXULTemplateBuilder* aSelf, + const nsAString& aVariable, + void* aClosure); + + /** + * Load the datasources for the template. shouldDelayBuilding is an out + * parameter which will be set to true to indicate that content building + * should not be performed yet as the datasource has not yet loaded. If + * false, the datasource has already loaded so building can proceed + * immediately. In the former case, the datasource or query processor + * should either rebuild the template or update results when the + * datasource is loaded as needed. + */ + nsresult + LoadDataSources(nsIDocument* aDoc, bool* shouldDelayBuilding); + + /** + * Called by LoadDataSources to load a datasource given a uri list + * in aDataSource. The list is a set of uris separated by spaces. + * If aIsRDFQuery is true, then this is for an RDF datasource which + * causes the method to check for additional flags specific to the + * RDF processor. + */ + nsresult + LoadDataSourceUrls(nsIDocument* aDocument, + const nsAString& aDataSources, + bool aIsRDFQuery, + bool* aShouldDelayBuilding); + + nsresult + InitHTMLTemplateRoot(); + + /** + * Determine which rule matches a given result. aContainer is used for + * tag matching and is optional for non-content generating builders. + * The returned matched rule is always one of the rules owned by the + * query set aQuerySet. + * + * @param aContainer parent where generated content will be inserted + * @param aResult result to match + * @param aQuerySet query set to examine the rules of + * @param aMatchedRule [out] rule that has matched, or null if any. + * @param aRuleIndex [out] index of the rule + */ + nsresult + DetermineMatchedRule(nsIContent* aContainer, + nsIXULTemplateResult* aResult, + nsTemplateQuerySet* aQuerySet, + nsTemplateRule** aMatchedRule, + int16_t *aRuleIndex); + + // XXX sigh, the string template foo doesn't mix with + // operator->*() on egcs-1.1.2, so we'll need to explicitly pass + // "this" and use good ol' fashioned static callbacks. + void + ParseAttribute(const nsAString& aAttributeValue, + void (*aVariableCallback)(nsXULTemplateBuilder* aThis, const nsAString&, void*), + void (*aTextCallback)(nsXULTemplateBuilder* aThis, const nsAString&, void*), + void* aClosure); + + nsresult + SubstituteText(nsIXULTemplateResult* aMatch, + const nsAString& aAttributeValue, + nsAString& aResult); + + static void + SubstituteTextAppendText(nsXULTemplateBuilder* aThis, const nsAString& aText, void* aClosure); + + static void + SubstituteTextReplaceVariable(nsXULTemplateBuilder* aThis, const nsAString& aVariable, void* aClosure); + + nsresult + IsSystemPrincipal(nsIPrincipal *principal, bool *result); + + /** + * Convenience method which gets a resource for a result. If a result + * doesn't have a resource set, it will create one from the result's id. + */ + nsresult GetResultResource(nsIXULTemplateResult* aResult, + nsIRDFResource** aResource); + +protected: + virtual ~nsXULTemplateBuilder(); + + nsCOMPtr<nsISupports> mDataSource; + nsCOMPtr<nsIRDFDataSource> mDB; + nsCOMPtr<nsIRDFCompositeDataSource> mCompDB; + + /** + * Circular reference, broken when the document is destroyed. + */ + nsCOMPtr<nsIContent> mRoot; + + /** + * The root result, translated from the root element's ref + */ + nsCOMPtr<nsIXULTemplateResult> mRootResult; + + nsCOMArray<nsIXULBuilderListener> mListeners; + + /** + * The query processor which generates results + */ + nsCOMPtr<nsIXULTemplateQueryProcessor> mQueryProcessor; + + /** + * The list of querysets + */ + nsTArray<nsTemplateQuerySet *> mQuerySets; + + /** + * Set to true if the rules have already been compiled + */ + bool mQueriesCompiled; + + /** + * The default reference and member variables. + */ + nsCOMPtr<nsIAtom> mRefVariable; + nsCOMPtr<nsIAtom> mMemberVariable; + + /** + * The match map contains nsTemplateMatch objects, one for each unique + * match found, keyed by the resource for that match. A particular match + * will contain a linked list of all of the matches for that unique result + * id. Only one is active at a time. When a match is retracted, look in + * the match map, remove it, and apply the next valid match in sequence to + * make active. + */ + nsDataHashtable<nsISupportsHashKey, nsTemplateMatch*> mMatchMap; + + // pseudo-constants + static nsrefcnt gRefCnt; + static nsIRDFService* gRDFService; + static nsIRDFContainerUtils* gRDFContainerUtils; + static nsIScriptSecurityManager* gScriptSecurityManager; + static nsIPrincipal* gSystemPrincipal; + static nsIObserverService* gObserverService; + + enum { + eDontTestEmpty = (1 << 0), + eDontRecurse = (1 << 1), + eLoggingEnabled = (1 << 2) + }; + + int32_t mFlags; + + /** + * Stack-based helper class to maintain a list of ``activated'' + * resources; i.e., resources for which we are currently building + * content. + */ + class ActivationEntry { + public: + nsIRDFResource *mResource; + ActivationEntry *mPrevious; + ActivationEntry **mLink; + + ActivationEntry(nsIRDFResource *aResource, ActivationEntry **aLink) + : mResource(aResource), + mPrevious(*aLink), + mLink(aLink) { *mLink = this; } + + ~ActivationEntry() { *mLink = mPrevious; } + }; + + /** + * The top of the stack of resources that we're currently building + * content for. + */ + ActivationEntry *mTop; + + /** + * Determine if a resource is currently on the activation stack. + */ + bool + IsActivated(nsIRDFResource *aResource); + + /** + * Returns true if content may be generated for a result, or false if it + * cannot, for example, if it would be created inside a closed container. + * Those results will be generated when the container is opened. + * If false is returned, no content should be generated. Possible + * insertion locations may optionally be set for new content, depending on + * the builder being used. Note that *aLocations or some items within + * aLocations may be null. + */ + virtual bool + GetInsertionLocations(nsIXULTemplateResult* aResult, + nsCOMArray<nsIContent>** aLocations) = 0; + + /** + * Must be implemented by subclasses. Handle removing the generated + * output for aOldMatch and adding new output for aNewMatch. Either + * aOldMatch or aNewMatch may be null. aContext is the location returned + * from the call to MayGenerateResult. + */ + virtual nsresult + ReplaceMatch(nsIXULTemplateResult* aOldResult, + nsTemplateMatch* aNewMatch, + nsTemplateRule* aNewMatchRule, + void *aContext) = 0; + + /** + * Must be implemented by subclasses. Handle change in bound + * variable values for aResult. aModifiedVars contains the set + * of variables that have changed. + * @param aResult the ersult for which variable bindings has changed. + * @param aModifiedVars the set of variables for which the bindings + * have changed. + */ + virtual nsresult + SynchronizeResult(nsIXULTemplateResult* aResult) = 0; + + /** + * Output a new match or removed match to the console. + * + * @param aId id of the result + * @param aMatch new or removed match + * @param aIsNew true for new matched, false for removed matches + */ + void + OutputMatchToLog(nsIRDFResource* aId, + nsTemplateMatch* aMatch, + bool aIsNew); + + virtual void Traverse(nsCycleCollectionTraversalCallback &cb) const + { + } + + /** + * Start observing events from the observer service and the given + * document. + * + * @param aDocument the document to observe + */ + void StartObserving(nsIDocument* aDocument); + + /** + * Stop observing events from the observer service and any associated + * document. + */ + void StopObserving(); + + /** + * Document that we're observing. Weak ref! + */ + nsIDocument* mObservedDocument; +}; + +#endif // nsXULTemplateBuilder_h__ diff --git a/dom/xul/templates/nsXULTemplateQueryProcessorRDF.cpp b/dom/xul/templates/nsXULTemplateQueryProcessorRDF.cpp new file mode 100644 index 000000000..732e545d0 --- /dev/null +++ b/dom/xul/templates/nsXULTemplateQueryProcessorRDF.cpp @@ -0,0 +1,1825 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsCOMPtr.h" +#include "nsICollation.h" +#include "nsIDOMNode.h" +#include "nsIRDFNode.h" +#include "nsIRDFObserver.h" +#include "nsIRDFRemoteDataSource.h" +#include "nsIRDFInferDataSource.h" +#include "nsIRDFService.h" +#include "nsRDFCID.h" +#include "nsIServiceManager.h" +#include "nsNameSpaceManager.h" +#include "nsGkAtoms.h" +#include "nsIDOMDocument.h" +#include "nsAttrName.h" +#include "rdf.h" +#include "nsArrayUtils.h" +#include "nsIURI.h" + +#include "nsContentTestNode.h" +#include "nsRDFConInstanceTestNode.h" +#include "nsRDFConMemberTestNode.h" +#include "nsRDFPropertyTestNode.h" +#include "nsInstantiationNode.h" +#include "nsRDFTestNode.h" +#include "nsXULContentUtils.h" +#include "nsXULTemplateBuilder.h" +#include "nsXULTemplateResultRDF.h" +#include "nsXULTemplateResultSetRDF.h" +#include "nsXULTemplateQueryProcessorRDF.h" +#include "nsXULSortService.h" +#include "nsIDocument.h" + +//---------------------------------------------------------------------- + +#define PARSE_TYPE_INTEGER "Integer" + +nsrefcnt nsXULTemplateQueryProcessorRDF::gRefCnt = 0; +nsIRDFService* nsXULTemplateQueryProcessorRDF::gRDFService; +nsIRDFContainerUtils* nsXULTemplateQueryProcessorRDF::gRDFContainerUtils; +nsIRDFResource* nsXULTemplateQueryProcessorRDF::kNC_BookmarkSeparator; +nsIRDFResource* nsXULTemplateQueryProcessorRDF::kRDF_type; + +NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULTemplateQueryProcessorRDF) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULTemplateQueryProcessorRDF) + tmp->Done(); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULTemplateQueryProcessorRDF) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDB) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLastRef) + + for (auto it = tmp->mBindingDependencies.Iter(); !it.Done(); it.Next()) { + nsXULTemplateQueryProcessorRDF::ResultArray* array = it.UserData(); + int32_t count = array->Length(); + for (int32_t i = 0; i < count; ++i) { + cb.NoteXPCOMChild(array->ElementAt(i)); + } + } + + for (auto it = tmp->mMemoryElementToResultMap.Iter(); + !it.Done(); + it.Next()) { + nsCOMArray<nsXULTemplateResultRDF>* array = it.UserData(); + int32_t count = array->Count(); + for (int32_t i = 0; i < count; ++i) { + cb.NoteXPCOMChild(array->ObjectAt(i)); + } + } + + for (auto it = tmp->mRuleToBindingsMap.Iter(); !it.Done(); it.Next()) { + cb.NoteXPCOMChild(it.Key()); + } + + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mQueries) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULTemplateQueryProcessorRDF) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULTemplateQueryProcessorRDF) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULTemplateQueryProcessorRDF) + NS_INTERFACE_MAP_ENTRY(nsIXULTemplateQueryProcessor) + NS_INTERFACE_MAP_ENTRY(nsIRDFObserver) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXULTemplateQueryProcessor) +NS_INTERFACE_MAP_END + +nsXULTemplateQueryProcessorRDF::nsXULTemplateQueryProcessorRDF(void) + : mDB(nullptr), + mBuilder(nullptr), + mQueryProcessorRDFInited(false), + mGenerationStarted(false), + mUpdateBatchNest(0), + mSimpleRuleMemberTest(nullptr) +{ + gRefCnt++; +} + +nsXULTemplateQueryProcessorRDF::~nsXULTemplateQueryProcessorRDF(void) +{ + if (--gRefCnt == 0) { + NS_IF_RELEASE(gRDFService); + NS_IF_RELEASE(gRDFContainerUtils); + NS_IF_RELEASE(kNC_BookmarkSeparator); + NS_IF_RELEASE(kRDF_type); + } +} + +nsresult +nsXULTemplateQueryProcessorRDF::InitGlobals() +{ + nsresult rv; + + // Initialize the global shared reference to the service + // manager and get some shared resource objects. + if (!gRDFService) { + NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID); + rv = CallGetService(kRDFServiceCID, &gRDFService); + if (NS_FAILED(rv)) + return rv; + } + + if (!gRDFContainerUtils) { + NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID); + rv = CallGetService(kRDFContainerUtilsCID, &gRDFContainerUtils); + if (NS_FAILED(rv)) + return rv; + } + + if (!kNC_BookmarkSeparator) { + gRDFService->GetResource( + NS_LITERAL_CSTRING(NC_NAMESPACE_URI "BookmarkSeparator"), + &kNC_BookmarkSeparator); + } + + if (!kRDF_type) { + gRDFService->GetResource( + NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "type"), + &kRDF_type); + } + + return NS_OK; +} + +//---------------------------------------------------------------------- +// +// nsIXULTemplateQueryProcessor interface +// + +NS_IMETHODIMP +nsXULTemplateQueryProcessorRDF::GetDatasource(nsIArray* aDataSources, + nsIDOMNode* aRootNode, + bool aIsTrusted, + nsIXULTemplateBuilder* aBuilder, + bool* aShouldDelayBuilding, + nsISupports** aResult) +{ + nsCOMPtr<nsIRDFCompositeDataSource> compDB; + nsCOMPtr<nsIContent> root = do_QueryInterface(aRootNode); + nsresult rv; + + *aResult = nullptr; + *aShouldDelayBuilding = false; + + NS_ENSURE_TRUE(root, NS_ERROR_UNEXPECTED); + + // make sure the RDF service is set up + rv = InitGlobals(); + NS_ENSURE_SUCCESS(rv, rv); + + // create a database for the builder + compDB = do_CreateInstance(NS_RDF_DATASOURCE_CONTRACTID_PREFIX + "composite-datasource"); + if (!compDB) { + NS_ERROR("unable to construct new composite data source"); + return NS_ERROR_UNEXPECTED; + } + + // check for magical attributes. XXX move to ``flags''? + if (root->AttrValueIs(kNameSpaceID_None, + nsGkAtoms::coalesceduplicatearcs, + nsGkAtoms::_false, eCaseMatters)) + compDB->SetCoalesceDuplicateArcs(false); + + if (root->AttrValueIs(kNameSpaceID_None, + nsGkAtoms::allownegativeassertions, + nsGkAtoms::_false, eCaseMatters)) + compDB->SetAllowNegativeAssertions(false); + + if (aIsTrusted) { + // If we're a privileged (e.g., chrome) document, then add the + // local store as the first data source in the db. Note that + // we _might_ not be able to get a local store if we haven't + // got a profile to read from yet. + nsCOMPtr<nsIRDFDataSource> localstore; + rv = gRDFService->GetDataSource("rdf:local-store", + getter_AddRefs(localstore)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = compDB->AddDataSource(localstore); + NS_ASSERTION(NS_SUCCEEDED(rv), "unable to add local store to db"); + NS_ENSURE_SUCCESS(rv, rv); + } + + uint32_t length, index; + rv = aDataSources->GetLength(&length); + NS_ENSURE_SUCCESS(rv,rv); + + for (index = 0; index < length; index++) { + + nsCOMPtr<nsIURI> uri = do_QueryElementAt(aDataSources, index); + if (!uri) // we ignore other datasources than uri + continue; + + nsCOMPtr<nsIRDFDataSource> ds; + nsAutoCString uristrC; + uri->GetSpec(uristrC); + + rv = gRDFService->GetDataSource(uristrC.get(), getter_AddRefs(ds)); + + if (NS_FAILED(rv)) { + // This is only a warning because the data source may not + // be accessible for any number of reasons, including + // security, a bad URL, etc. + #ifdef DEBUG + nsAutoCString msg; + msg.AppendLiteral("unable to load datasource '"); + msg.Append(uristrC); + msg.Append('\''); + NS_WARNING(msg.get()); + #endif + continue; + } + + compDB->AddDataSource(ds); + } + + + // check if we were given an inference engine type + nsAutoString infer; + nsCOMPtr<nsIRDFDataSource> db; + root->GetAttr(kNameSpaceID_None, nsGkAtoms::infer, infer); + if (!infer.IsEmpty()) { + nsCString inferCID(NS_RDF_INFER_DATASOURCE_CONTRACTID_PREFIX); + AppendUTF16toUTF8(infer, inferCID); + nsCOMPtr<nsIRDFInferDataSource> inferDB = + do_CreateInstance(inferCID.get()); + + if (inferDB) { + inferDB->SetBaseDataSource(compDB); + db = do_QueryInterface(inferDB); + } + else { + NS_WARNING("failed to construct inference engine specified on template"); + } + } + + if (!db) + db = compDB; + + return CallQueryInterface(db, aResult); +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorRDF::InitializeForBuilding(nsISupports* aDatasource, + nsIXULTemplateBuilder* aBuilder, + nsIDOMNode* aRootNode) +{ + if (!mQueryProcessorRDFInited) { + nsresult rv = InitGlobals(); + if (NS_FAILED(rv)) + return rv; + + mQueryProcessorRDFInited = true; + } + + // don't do anything if generation has already been done + if (mGenerationStarted) + return NS_ERROR_UNEXPECTED; + + mDB = do_QueryInterface(aDatasource); + mBuilder = aBuilder; + + ComputeContainmentProperties(aRootNode); + + // Add ourselves as a datasource observer + if (mDB) + mDB->AddObserver(this); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorRDF::Done() +{ + if (!mQueryProcessorRDFInited) + return NS_OK; + + if (mDB) + mDB->RemoveObserver(this); + + mDB = nullptr; + mBuilder = nullptr; + mRefVariable = nullptr; + mLastRef = nullptr; + + mGenerationStarted = false; + mUpdateBatchNest = 0; + + mContainmentProperties.Clear(); + + for (ReteNodeSet::Iterator node = mAllTests.First(); + node != mAllTests.Last(); ++node) + delete *node; + + mAllTests.Clear(); + mRDFTests.Clear(); + mQueries.Clear(); + + mSimpleRuleMemberTest = nullptr; + + mBindingDependencies.Clear(); + + mMemoryElementToResultMap.Clear(); + + mRuleToBindingsMap.Clear(); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorRDF::CompileQuery(nsIXULTemplateBuilder* aBuilder, + nsIDOMNode* aQueryNode, + nsIAtom* aRefVariable, + nsIAtom* aMemberVariable, + nsISupports** _retval) +{ + RefPtr<nsRDFQuery> query = new nsRDFQuery(this); + if (!query) + return NS_ERROR_OUT_OF_MEMORY; + + query->mRefVariable = aRefVariable; + if (!mRefVariable) + mRefVariable = aRefVariable; + + if (!aMemberVariable) + query->mMemberVariable = NS_Atomize("?"); + else + query->mMemberVariable = aMemberVariable; + + nsresult rv; + TestNode *lastnode = nullptr; + + nsCOMPtr<nsIContent> content = do_QueryInterface(aQueryNode); + + if (content->NodeInfo()->Equals(nsGkAtoms::_template, kNameSpaceID_XUL)) { + // simplified syntax with no rules + + query->SetSimple(); + NS_ASSERTION(!mSimpleRuleMemberTest, + "CompileQuery called twice with the same template"); + if (!mSimpleRuleMemberTest) + rv = CompileSimpleQuery(query, content, &lastnode); + else + rv = NS_ERROR_FAILURE; + } + else if (content->NodeInfo()->Equals(nsGkAtoms::rule, kNameSpaceID_XUL)) { + // simplified syntax with at least one rule + query->SetSimple(); + rv = CompileSimpleQuery(query, content, &lastnode); + } + else { + rv = CompileExtendedQuery(query, content, &lastnode); + } + + if (NS_FAILED(rv)) + return rv; + + query->SetQueryNode(aQueryNode); + + nsInstantiationNode* instnode = new nsInstantiationNode(this, query); + + // this and other functions always add nodes to mAllTests first. That + // way if something fails, the node will just sit harmlessly in mAllTests + // where it can be deleted later. + rv = mAllTests.Add(instnode); + if (NS_FAILED(rv)) { + delete instnode; + return rv; + } + + rv = lastnode->AddChild(instnode); + if (NS_FAILED(rv)) + return rv; + + mQueries.AppendElement(query); + + query.forget(_retval); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorRDF::GenerateResults(nsISupports* aDatasource, + nsIXULTemplateResult* aRef, + nsISupports* aQuery, + nsISimpleEnumerator** aResults) +{ + nsCOMPtr<nsITemplateRDFQuery> rdfquery = do_QueryInterface(aQuery); + if (! rdfquery) + return NS_ERROR_INVALID_ARG; + + mGenerationStarted = true; + + // should be safe to cast here since the query is a + // non-scriptable nsITemplateRDFQuery + nsRDFQuery* query = static_cast<nsRDFQuery *>(aQuery); + + *aResults = nullptr; + + nsCOMPtr<nsISimpleEnumerator> results; + + if (aRef) { + // make sure that cached results were generated for this ref, and if not, + // regenerate them. Otherwise, things will go wrong for templates bound to + // an HTML element as they are not generated lazily. + if (aRef == mLastRef) { + query->UseCachedResults(getter_AddRefs(results)); + } + else { + // clear the cached results + int32_t count = mQueries.Length(); + for (int32_t r = 0; r < count; r++) { + mQueries[r]->ClearCachedResults(); + } + } + + if (! results) { + if (! query->mRefVariable) + query->mRefVariable = NS_Atomize("?uri"); + + nsCOMPtr<nsIRDFResource> refResource; + aRef->GetResource(getter_AddRefs(refResource)); + if (! refResource) + return NS_ERROR_FAILURE; + + // Propagate the assignments through the network + TestNode* root = query->GetRoot(); + + if (query->IsSimple() && mSimpleRuleMemberTest) { + // get the top node in the simple rule tree + root = mSimpleRuleMemberTest->GetParent(); + mLastRef = aRef; + } + + if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) { + nsAutoString id; + aRef->GetId(id); + + nsAutoString rvar; + query->mRefVariable->ToString(rvar); + nsAutoString mvar; + query->mMemberVariable->ToString(mvar); + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + ("QueryProcessor::GenerateResults using ref %s and vars [ ref: %s member: %s]", + NS_ConvertUTF16toUTF8(id).get(), + NS_ConvertUTF16toUTF8(rvar).get(), + NS_ConvertUTF16toUTF8(mvar).get())); + } + + if (root) { + // the seed is the initial instantiation, which has a single + // assignment holding the reference point + Instantiation seed; + seed.AddAssignment(query->mRefVariable, refResource); + + InstantiationSet* instantiations = new InstantiationSet(); + instantiations->Append(seed); + + // if the propagation caused a match, then the results will be + // cached in the query, retrieved below by calling + // UseCachedResults. The matching result set owns the + // instantiations and will delete them when results have been + // iterated over. If the propagation did not match, the + // instantiations need to be deleted. + bool owned = false; + nsresult rv = root->Propagate(*instantiations, false, owned); + if (! owned) + delete instantiations; + if (NS_FAILED(rv)) + return rv; + + query->UseCachedResults(getter_AddRefs(results)); + } + } + } + + if (! results) { + // no results were found so create an empty set + results = new nsXULTemplateResultSetRDF(this, query, nullptr); + } + + results.swap(*aResults); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorRDF::AddBinding(nsIDOMNode* aRuleNode, + nsIAtom* aVar, + nsIAtom* aRef, + const nsAString& aExpr) +{ + // add a <binding> to a rule. When a result is matched, the bindings are + // examined to add additional variable assignments + + // bindings can't be added once result generation has started, otherwise + // the array sizes will get out of sync + if (mGenerationStarted) + return NS_ERROR_UNEXPECTED; + + nsCOMPtr<nsIRDFResource> property; + nsresult rv = gRDFService->GetUnicodeResource(aExpr, getter_AddRefs(property)); + if (NS_FAILED(rv)) + return rv; + + RefPtr<RDFBindingSet> bindings = mRuleToBindingsMap.GetWeak(aRuleNode); + if (!bindings) { + bindings = new RDFBindingSet(); + mRuleToBindingsMap.Put(aRuleNode, bindings); + } + + return bindings->AddBinding(aVar, aRef, property); +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorRDF::TranslateRef(nsISupports* aDatasource, + const nsAString& aRefString, + nsIXULTemplateResult** aRef) +{ + // make sure the RDF service is set up + nsresult rv = InitGlobals(); + if (NS_FAILED(rv)) + return rv; + + nsCOMPtr<nsIRDFResource> uri; + gRDFService->GetUnicodeResource(aRefString, getter_AddRefs(uri)); + + RefPtr<nsXULTemplateResultRDF> refresult = new nsXULTemplateResultRDF(uri); + if (! refresult) + return NS_ERROR_OUT_OF_MEMORY; + + refresult.forget(aRef); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorRDF::CompareResults(nsIXULTemplateResult* aLeft, + nsIXULTemplateResult* aRight, + nsIAtom* aVar, + uint32_t aSortHints, + int32_t* aResult) +{ + NS_ENSURE_ARG_POINTER(aLeft); + NS_ENSURE_ARG_POINTER(aRight); + + *aResult = 0; + + // for natural order sorting, use the index in the RDF container for the + // order. If there is no container, just sort them arbitrarily. + if (!aVar) { + // if a result has a negative index, just sort it first + int32_t leftindex = GetContainerIndexOf(aLeft); + int32_t rightindex = GetContainerIndexOf(aRight); + *aResult = leftindex == rightindex ? 0 : + leftindex > rightindex ? 1 : + -1; + return NS_OK; + } + + nsDependentAtomString sortkey(aVar); + + nsCOMPtr<nsISupports> leftNode, rightNode; + + if (!sortkey.IsEmpty() && sortkey[0] != '?' && + !StringBeginsWith(sortkey, NS_LITERAL_STRING("rdf:")) && + mDB) { + // if the sort key is not a template variable, it should be an RDF + // predicate. Get the targets and compare those instead. + nsCOMPtr<nsIRDFResource> predicate; + nsresult rv = gRDFService->GetUnicodeResource(sortkey, getter_AddRefs(predicate)); + NS_ENSURE_SUCCESS(rv, rv); + + // create a predicate with '?sort=true' appended. This special + // predicate may be used to have a different sort value than the + // displayed value + sortkey.AppendLiteral("?sort=true"); + + nsCOMPtr<nsIRDFResource> sortPredicate; + rv = gRDFService->GetUnicodeResource(sortkey, getter_AddRefs(sortPredicate)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = GetSortValue(aLeft, predicate, sortPredicate, getter_AddRefs(leftNode)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = GetSortValue(aRight, predicate, sortPredicate, getter_AddRefs(rightNode)); + NS_ENSURE_SUCCESS(rv, rv); + } + else { + // get the values for the sort key from the results + aLeft->GetBindingObjectFor(aVar, getter_AddRefs(leftNode)); + aRight->GetBindingObjectFor(aVar, getter_AddRefs(rightNode)); + } + + { + // Literals? + nsCOMPtr<nsIRDFLiteral> l = do_QueryInterface(leftNode); + if (l) { + nsCOMPtr<nsIRDFLiteral> r = do_QueryInterface(rightNode); + if (r) { + const char16_t *lstr, *rstr; + l->GetValueConst(&lstr); + r->GetValueConst(&rstr); + + *aResult = XULSortServiceImpl::CompareValues( + nsDependentString(lstr), + nsDependentString(rstr), aSortHints); + } + + return NS_OK; + } + } + + { + // Dates? + nsCOMPtr<nsIRDFDate> l = do_QueryInterface(leftNode); + if (l) { + nsCOMPtr<nsIRDFDate> r = do_QueryInterface(rightNode); + if (r) { + PRTime ldate, rdate; + l->GetValue(&ldate); + r->GetValue(&rdate); + + int64_t delta = ldate - rdate; + if (delta == 0) + *aResult = 0; + else if (delta >= 0) + *aResult = 1; + else + *aResult = -1; + } + + return NS_OK; + } + } + + { + // Integers? + nsCOMPtr<nsIRDFInt> l = do_QueryInterface(leftNode); + if (l) { + nsCOMPtr<nsIRDFInt> r = do_QueryInterface(rightNode); + if (r) { + int32_t lval, rval; + l->GetValue(&lval); + r->GetValue(&rval); + + *aResult = lval - rval; + } + + return NS_OK; + } + } + + nsICollation* collation = nsXULContentUtils::GetCollation(); + if (collation) { + // Blobs? (We can only compare these reasonably if we have a + // collation object.) + nsCOMPtr<nsIRDFBlob> l = do_QueryInterface(leftNode); + if (l) { + nsCOMPtr<nsIRDFBlob> r = do_QueryInterface(rightNode); + if (r) { + const uint8_t *lval, *rval; + int32_t llen, rlen; + l->GetValue(&lval); + l->GetLength(&llen); + r->GetValue(&rval); + r->GetLength(&rlen); + + collation->CompareRawSortKey(lval, llen, rval, rlen, aResult); + } + } + } + + // if the results are none of the above, just pretend that they are equal + return NS_OK; +} + +//---------------------------------------------------------------------- +// +// nsIRDFObserver interface +// + + +NS_IMETHODIMP +nsXULTemplateQueryProcessorRDF::OnAssert(nsIRDFDataSource* aDataSource, + nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget) +{ + // Ignore updates if we're batching + if (mUpdateBatchNest) + return(NS_OK); + + if (! mBuilder) + return NS_OK; + + LOG("onassert", aSource, aProperty, aTarget); + + Propagate(aSource, aProperty, aTarget); + SynchronizeAll(aSource, aProperty, nullptr, aTarget); + return NS_OK; +} + + + +NS_IMETHODIMP +nsXULTemplateQueryProcessorRDF::OnUnassert(nsIRDFDataSource* aDataSource, + nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget) +{ + // Ignore updates if we're batching + if (mUpdateBatchNest) + return NS_OK; + + if (! mBuilder) + return NS_OK; + + LOG("onunassert", aSource, aProperty, aTarget); + + Retract(aSource, aProperty, aTarget); + SynchronizeAll(aSource, aProperty, aTarget, nullptr); + return NS_OK; +} + + +NS_IMETHODIMP +nsXULTemplateQueryProcessorRDF::OnChange(nsIRDFDataSource* aDataSource, + nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aOldTarget, + nsIRDFNode* aNewTarget) +{ + // Ignore updates if we're batching + if (mUpdateBatchNest) + return NS_OK; + + if (! mBuilder) + return NS_OK; + + LOG("onchange", aSource, aProperty, aNewTarget); + + if (aOldTarget) { + // Pull any old results that were relying on aOldTarget + Retract(aSource, aProperty, aOldTarget); + } + + if (aNewTarget) { + // Fire any new results that are activated by aNewTarget + Propagate(aSource, aProperty, aNewTarget); + } + + // Synchronize any of the content model that may have changed. + SynchronizeAll(aSource, aProperty, aOldTarget, aNewTarget); + return NS_OK; +} + + +NS_IMETHODIMP +nsXULTemplateQueryProcessorRDF::OnMove(nsIRDFDataSource* aDataSource, + nsIRDFResource* aOldSource, + nsIRDFResource* aNewSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget) +{ + // Ignore updates if we're batching + if (mUpdateBatchNest) + return NS_OK; + + NS_NOTYETIMPLEMENTED("write me"); + return NS_ERROR_NOT_IMPLEMENTED; +} + + +NS_IMETHODIMP +nsXULTemplateQueryProcessorRDF::OnBeginUpdateBatch(nsIRDFDataSource* aDataSource) +{ + mUpdateBatchNest++; + return NS_OK; +} + + +NS_IMETHODIMP +nsXULTemplateQueryProcessorRDF::OnEndUpdateBatch(nsIRDFDataSource* aDataSource) +{ + NS_ASSERTION(mUpdateBatchNest > 0, "badly nested update batch"); + if (--mUpdateBatchNest <= 0) { + mUpdateBatchNest = 0; + + if (mBuilder) + mBuilder->Rebuild(); + } + + return NS_OK; +} + +nsresult +nsXULTemplateQueryProcessorRDF::Propagate(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget) +{ + // When a new assertion is added to the graph, determine any new matches + // that must be added to the template builder. First, iterate through all + // the RDF tests (<member> and <triple> tests), and find the topmost test + // that would be affected by the new assertion. + nsresult rv; + + ReteNodeSet livenodes; + + if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) { + const char* sourceStr; + aSource->GetValueConst(&sourceStr); + const char* propertyStr; + aProperty->GetValueConst(&propertyStr); + nsAutoString targetStr; + nsXULContentUtils::GetTextForNode(aTarget, targetStr); + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + ("nsXULTemplateQueryProcessorRDF::Propagate: [%s] -> [%s] -> [%s]\n", + sourceStr, propertyStr, NS_ConvertUTF16toUTF8(targetStr).get())); + } + + { + ReteNodeSet::Iterator last = mRDFTests.Last(); + for (ReteNodeSet::Iterator i = mRDFTests.First(); i != last; ++i) { + nsRDFTestNode* rdftestnode = static_cast<nsRDFTestNode*>(*i); + + Instantiation seed; + if (rdftestnode->CanPropagate(aSource, aProperty, aTarget, seed)) { + rv = livenodes.Add(rdftestnode); + if (NS_FAILED(rv)) + return rv; + } + } + } + + // Now, we'll go through each, and any that aren't dominated by + // another live node will be used to propagate the assertion + // through the rule network + { + ReteNodeSet::Iterator last = livenodes.Last(); + for (ReteNodeSet::Iterator i = livenodes.First(); i != last; ++i) { + nsRDFTestNode* rdftestnode = static_cast<nsRDFTestNode*>(*i); + + // What happens here is we create an instantiation as if we were + // at the found test in the rule network. For example, if the + // found test was a member test (parent => child), the parent + // and child variables are assigned the values provided by the new + // RDF assertion in the graph. The Constrain call is used to go + // up to earlier RDF tests, filling in variables as it goes. + // Constrain will eventually get up to the top node, an + // nsContentTestNode, which takes the value of the reference + // variable and calls the template builder to see if a result has + // been generated already for the reference value. If it hasn't, + // the new assertion couldn't cause a new match. If the result + // exists, call Propagate to continue to the later RDF tests to + // fill in the rest of the variable assignments. + + // Bogus, to get the seed instantiation + Instantiation seed; + rdftestnode->CanPropagate(aSource, aProperty, aTarget, seed); + + InstantiationSet* instantiations = new InstantiationSet(); + instantiations->Append(seed); + + rv = rdftestnode->Constrain(*instantiations); + if (NS_FAILED(rv)) { + delete instantiations; + return rv; + } + + bool owned = false; + if (!instantiations->Empty()) + rv = rdftestnode->Propagate(*instantiations, true, owned); + + // owned should always be false in update mode, but check just + // to be sure + if (!owned) + delete instantiations; + if (NS_FAILED(rv)) + return rv; + } + } + + return NS_OK; +} + + +nsresult +nsXULTemplateQueryProcessorRDF::Retract(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget) +{ + + if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) { + const char* sourceStr; + aSource->GetValueConst(&sourceStr); + const char* propertyStr; + aProperty->GetValueConst(&propertyStr); + nsAutoString targetStr; + nsXULContentUtils::GetTextForNode(aTarget, targetStr); + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + ("nsXULTemplateQueryProcessorRDF::Retract: [%s] -> [%s] -> [%s]\n", + sourceStr, propertyStr, NS_ConvertUTF16toUTF8(targetStr).get())); + } + + // Retract any currently active rules that will no longer be matched. + ReteNodeSet::ConstIterator lastnode = mRDFTests.Last(); + for (ReteNodeSet::ConstIterator node = mRDFTests.First(); node != lastnode; ++node) { + const nsRDFTestNode* rdftestnode = static_cast<const nsRDFTestNode*>(*node); + + rdftestnode->Retract(aSource, aProperty, aTarget); + + // Now fire any newly revealed rules + // XXXwaterson yo. write me. + // The intent here is to handle any rules that might be + // "revealed" by the removal of an assertion from the datasource. + // Waterson doesn't think we support negated conditions in a rule. + // Nor is he sure that this is currently useful. + } + + return NS_OK; +} + +nsresult +nsXULTemplateQueryProcessorRDF::SynchronizeAll(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aOldTarget, + nsIRDFNode* aNewTarget) +{ + // Update each match that contains <aSource, aProperty, aOldTarget>. + + // Get all the matches whose assignments are currently supported + // by aSource and aProperty: we'll need to recompute them. + ResultArray* results; + if (!mBindingDependencies.Get(aSource, &results) || !mBuilder) + return NS_OK; + + uint32_t length = results->Length(); + + for (uint32_t r = 0; r < length; r++) { + nsXULTemplateResultRDF* result = (*results)[r]; + if (result) { + // synchronize the result's bindings and then update the builder + // so that content can be updated + if (result->SyncAssignments(aSource, aProperty, aNewTarget)) { + nsITemplateRDFQuery* query = result->Query(); + if (query) { + nsCOMPtr<nsIDOMNode> querynode; + query->GetQueryNode(getter_AddRefs(querynode)); + + mBuilder->ResultBindingChanged(result); + } + } + } + } + + return NS_OK; +} + +nsresult +nsXULTemplateQueryProcessorRDF::Log(const char* aOperation, + nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget) +{ + if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) { + nsresult rv; + + const char* sourceStr; + rv = aSource->GetValueConst(&sourceStr); + if (NS_FAILED(rv)) + return rv; + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + ("xultemplate[%p] %8s [%s]--", this, aOperation, sourceStr)); + + const char* propertyStr; + rv = aProperty->GetValueConst(&propertyStr); + if (NS_FAILED(rv)) + return rv; + + nsAutoString targetStr; + rv = nsXULContentUtils::GetTextForNode(aTarget, targetStr); + if (NS_FAILED(rv)) + return rv; + + nsAutoCString targetstrC; + targetstrC.AssignWithConversion(targetStr); + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + (" --[%s]-->[%s]", + propertyStr, + targetstrC.get())); + } + return NS_OK; +} + +nsresult +nsXULTemplateQueryProcessorRDF::CheckContainer(nsIRDFResource* aResource, + bool* aIsContainer) +{ + NS_ENSURE_ARG_POINTER(aIsContainer); + NS_ENSURE_STATE(mDB); + + // We have to look at all of the arcs extending out of the + // resource: if any of them are that "containment" property, then + // we know we'll have children. + bool isContainer = false; + + for (nsResourceSet::ConstIterator property = mContainmentProperties.First(); + property != mContainmentProperties.Last(); + property++) { + bool hasArc = false; + mDB->HasArcOut(aResource, *property, &hasArc); + + if (hasArc) { + // Well, it's a container... + isContainer = true; + break; + } + } + + // If we get here, and we're still not sure if it's a container, + // then see if it's an RDF container + if (! isContainer) { + gRDFContainerUtils->IsContainer(mDB, aResource, &isContainer); + } + + *aIsContainer = isContainer; + + return NS_OK; +} + +nsresult +nsXULTemplateQueryProcessorRDF::CheckEmpty(nsIRDFResource* aResource, + bool* aIsEmpty) +{ + NS_ENSURE_STATE(mDB); + *aIsEmpty = true; + + for (nsResourceSet::ConstIterator property = mContainmentProperties.First(); + property != mContainmentProperties.Last(); + property++) { + + nsCOMPtr<nsIRDFNode> dummy; + mDB->GetTarget(aResource, *property, true, getter_AddRefs(dummy)); + + if (dummy) { + *aIsEmpty = false; + break; + } + } + + if (*aIsEmpty){ + return nsXULTemplateQueryProcessorRDF::gRDFContainerUtils-> + IsEmpty(mDB, aResource, aIsEmpty); + } + + return NS_OK; +} + +nsresult +nsXULTemplateQueryProcessorRDF::CheckIsSeparator(nsIRDFResource* aResource, + bool* aIsSeparator) +{ + NS_ENSURE_STATE(mDB); + return mDB->HasAssertion(aResource, kRDF_type, kNC_BookmarkSeparator, + true, aIsSeparator); +} + +//---------------------------------------------------------------------- + +nsresult +nsXULTemplateQueryProcessorRDF::ComputeContainmentProperties(nsIDOMNode* aRootNode) +{ + // The 'containment' attribute on the root node is a + // whitespace-separated list that tells us which properties we + // should use to test for containment. + nsresult rv; + + mContainmentProperties.Clear(); + + nsCOMPtr<nsIContent> content = do_QueryInterface(aRootNode); + + nsAutoString containment; + content->GetAttr(kNameSpaceID_None, nsGkAtoms::containment, containment); + + uint32_t len = containment.Length(); + uint32_t offset = 0; + while (offset < len) { + while (offset < len && nsCRT::IsAsciiSpace(containment[offset])) + ++offset; + + if (offset >= len) + break; + + uint32_t end = offset; + while (end < len && !nsCRT::IsAsciiSpace(containment[end])) + ++end; + + nsAutoString propertyStr; + containment.Mid(propertyStr, offset, end - offset); + + nsCOMPtr<nsIRDFResource> property; + rv = gRDFService->GetUnicodeResource(propertyStr, getter_AddRefs(property)); + if (NS_FAILED(rv)) + return rv; + + rv = mContainmentProperties.Add(property); + if (NS_FAILED(rv)) + return rv; + + offset = end; + } + +#define TREE_PROPERTY_HACK 1 +#if defined(TREE_PROPERTY_HACK) + if (! len) { + // Some ever-present membership tests. + mContainmentProperties.Add(nsXULContentUtils::NC_child); + mContainmentProperties.Add(nsXULContentUtils::NC_Folder); + } +#endif + + return NS_OK; +} + +nsresult +nsXULTemplateQueryProcessorRDF::CompileExtendedQuery(nsRDFQuery* aQuery, + nsIContent* aConditions, + TestNode** aLastNode) +{ + // Compile an extended query's children + nsContentTestNode* idnode = + new nsContentTestNode(this, aQuery->mRefVariable); + + aQuery->SetRoot(idnode); + nsresult rv = mAllTests.Add(idnode); + if (NS_FAILED(rv)) { + delete idnode; + return rv; + } + + TestNode* prevnode = idnode; + + for (nsIContent* condition = aConditions->GetFirstChild(); + condition; + condition = condition->GetNextSibling()) { + + // the <content> condition should always be the first child + if (condition->IsXULElement(nsGkAtoms::content)) { + if (condition != aConditions->GetFirstChild()) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_CONTENT_NOT_FIRST); + continue; + } + + // check for <content tag='tag'/> which indicates that matches + // should only be generated for items inside content with that tag + nsAutoString tagstr; + condition->GetAttr(kNameSpaceID_None, nsGkAtoms::tag, tagstr); + + nsCOMPtr<nsIAtom> tag; + if (! tagstr.IsEmpty()) { + tag = NS_Atomize(tagstr); + } + + nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(condition->GetComposedDoc()); + if (! doc) + return NS_ERROR_FAILURE; + + idnode->SetTag(tag, doc); + continue; + } + + TestNode* testnode = nullptr; + nsresult rv = CompileQueryChild(condition->NodeInfo()->NameAtom(), + aQuery, condition, prevnode, &testnode); + if (NS_FAILED(rv)) + return rv; + + if (testnode) { + rv = prevnode->AddChild(testnode); + if (NS_FAILED(rv)) + return rv; + + prevnode = testnode; + } + } + + *aLastNode = prevnode; + + return NS_OK; +} + +nsresult +nsXULTemplateQueryProcessorRDF::CompileQueryChild(nsIAtom* aTag, + nsRDFQuery* aQuery, + nsIContent* aCondition, + TestNode* aParentNode, + TestNode** aResult) +{ + nsresult rv = NS_OK; + + if (aTag == nsGkAtoms::triple) { + rv = CompileTripleCondition(aQuery, aCondition, aParentNode, aResult); + } + else if (aTag == nsGkAtoms::member) { + rv = CompileMemberCondition(aQuery, aCondition, aParentNode, aResult); + } + else if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Info)) { + nsAutoString tagstr; + aTag->ToString(tagstr); + + nsAutoCString tagstrC; + tagstrC.AssignWithConversion(tagstr); + MOZ_LOG(gXULTemplateLog, LogLevel::Info, + ("xultemplate[%p] unrecognized condition test <%s>", + this, tagstrC.get())); + } + + return rv; +} + +nsresult +nsXULTemplateQueryProcessorRDF::ParseLiteral(const nsString& aParseType, + const nsString& aValue, + nsIRDFNode** aResult) +{ + nsresult rv = NS_OK; + *aResult = nullptr; + + if (aParseType.EqualsLiteral(PARSE_TYPE_INTEGER)) { + nsCOMPtr<nsIRDFInt> intLiteral; + nsresult errorCode; + int32_t intValue = aValue.ToInteger(&errorCode); + if (NS_FAILED(errorCode)) + return NS_ERROR_FAILURE; + rv = gRDFService->GetIntLiteral(intValue, getter_AddRefs(intLiteral)); + if (NS_FAILED(rv)) + return rv; + intLiteral.forget(aResult); + } + else { + nsCOMPtr<nsIRDFLiteral> literal; + rv = gRDFService->GetLiteral(aValue.get(), getter_AddRefs(literal)); + if (NS_FAILED(rv)) + return rv; + literal.forget(aResult); + } + return rv; +} + +nsresult +nsXULTemplateQueryProcessorRDF::CompileTripleCondition(nsRDFQuery* aQuery, + nsIContent* aCondition, + TestNode* aParentNode, + TestNode** aResult) +{ + // Compile a <triple> condition, which must be of the form: + // + // <triple subject="?var1|resource" + // predicate="resource" + // object="?var2|resource|literal" /> + // + // XXXwaterson Some day it would be cool to allow the 'predicate' + // to be bound to a variable. + + // subject + nsAutoString subject; + aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::subject, subject); + + nsCOMPtr<nsIAtom> svar; + nsCOMPtr<nsIRDFResource> sres; + if (subject.IsEmpty()) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_TRIPLE_BAD_SUBJECT); + return NS_OK; + } + if (subject[0] == char16_t('?')) + svar = NS_Atomize(subject); + else + gRDFService->GetUnicodeResource(subject, getter_AddRefs(sres)); + + // predicate + nsAutoString predicate; + aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::predicate, predicate); + + nsCOMPtr<nsIRDFResource> pres; + if (predicate.IsEmpty() || predicate[0] == char16_t('?')) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_TRIPLE_BAD_PREDICATE); + return NS_OK; + } + gRDFService->GetUnicodeResource(predicate, getter_AddRefs(pres)); + + // object + nsAutoString object; + aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::object, object); + + nsCOMPtr<nsIAtom> ovar; + nsCOMPtr<nsIRDFNode> onode; + if (object.IsEmpty()) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_TRIPLE_BAD_OBJECT); + return NS_OK; + } + + if (object[0] == char16_t('?')) { + ovar = NS_Atomize(object); + } + else if (object.FindChar(':') != -1) { // XXXwaterson evil. + // treat as resource + nsCOMPtr<nsIRDFResource> resource; + gRDFService->GetUnicodeResource(object, getter_AddRefs(resource)); + onode = do_QueryInterface(resource); + } + else { + nsAutoString parseType; + aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::parsetype, parseType); + nsresult rv = ParseLiteral(parseType, object, getter_AddRefs(onode)); + if (NS_FAILED(rv)) + return rv; + } + + nsRDFPropertyTestNode* testnode = nullptr; + + if (svar && ovar) { + testnode = new nsRDFPropertyTestNode(aParentNode, this, svar, pres, ovar); + } + else if (svar) { + testnode = new nsRDFPropertyTestNode(aParentNode, this, svar, pres, onode); + } + else if (ovar) { + testnode = new nsRDFPropertyTestNode(aParentNode, this, sres, pres, ovar); + } + else { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_TRIPLE_NO_VAR); + return NS_OK; + } + + // add testnode to mAllTests first. If adding to mRDFTests fails, just + // leave it in the list so that it can be deleted later. + MOZ_ASSERT(testnode); + nsresult rv = mAllTests.Add(testnode); + if (NS_FAILED(rv)) { + delete testnode; + return rv; + } + + rv = mRDFTests.Add(testnode); + if (NS_FAILED(rv)) + return rv; + + *aResult = testnode; + return NS_OK; +} + +nsresult +nsXULTemplateQueryProcessorRDF::CompileMemberCondition(nsRDFQuery* aQuery, + nsIContent* aCondition, + TestNode* aParentNode, + TestNode** aResult) +{ + // Compile a <member> condition, which must be of the form: + // + // <member container="?var1" child="?var2" /> + // + + // container + nsAutoString container; + aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::container, container); + + if (!container.IsEmpty() && container[0] != char16_t('?')) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_MEMBER_NOCONTAINERVAR); + return NS_OK; + } + + nsCOMPtr<nsIAtom> containervar = NS_Atomize(container); + + // child + nsAutoString child; + aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::child, child); + + if (!child.IsEmpty() && child[0] != char16_t('?')) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_MEMBER_NOCHILDVAR); + return NS_OK; + } + + nsCOMPtr<nsIAtom> childvar = NS_Atomize(child); + + TestNode* testnode = + new nsRDFConMemberTestNode(aParentNode, + this, + containervar, + childvar); + + // add testnode to mAllTests first. If adding to mRDFTests fails, just + // leave it in the list so that it can be deleted later. + nsresult rv = mAllTests.Add(testnode); + if (NS_FAILED(rv)) { + delete testnode; + return rv; + } + + rv = mRDFTests.Add(testnode); + if (NS_FAILED(rv)) + return rv; + + *aResult = testnode; + return NS_OK; +} + +nsresult +nsXULTemplateQueryProcessorRDF::AddDefaultSimpleRules(nsRDFQuery* aQuery, + TestNode** aChildNode) +{ + // XXXndeakin should check for tag in query processor instead of builder? + nsContentTestNode* idnode = + new nsContentTestNode(this, + aQuery->mRefVariable); + + // Create (?container ^member ?member) + nsRDFConMemberTestNode* membernode = + new nsRDFConMemberTestNode(idnode, + this, + aQuery->mRefVariable, + aQuery->mMemberVariable); + + // add nodes to mAllTests first. If later calls fail, just leave them in + // the list so that they can be deleted later. + nsresult rv = mAllTests.Add(idnode); + if (NS_FAILED(rv)) { + delete idnode; + delete membernode; + return rv; + } + + rv = mAllTests.Add(membernode); + if (NS_FAILED(rv)) { + delete membernode; + return rv; + } + + rv = mRDFTests.Add(membernode); + if (NS_FAILED(rv)) + return rv; + + rv = idnode->AddChild(membernode); + if (NS_FAILED(rv)) + return rv; + + mSimpleRuleMemberTest = membernode; + *aChildNode = membernode; + + return NS_OK; +} + +nsresult +nsXULTemplateQueryProcessorRDF::CompileSimpleQuery(nsRDFQuery* aQuery, + nsIContent* aQueryElement, + TestNode** aLastNode) +{ + // Compile a "simple" (or old-school style) <template> query. + nsresult rv; + + TestNode* parentNode; + + if (! mSimpleRuleMemberTest) { + rv = AddDefaultSimpleRules(aQuery, &parentNode); + if (NS_FAILED(rv)) + return rv; + } + + bool hasContainerTest = false; + + TestNode* prevnode = mSimpleRuleMemberTest; + + // Add constraints for the LHS + const nsAttrName* name; + for (uint32_t i = 0; (name = aQueryElement->GetAttrNameAt(i)); ++i) { + // Note: some attributes must be skipped on XUL template query subtree + + // never compare against rdf:property, rdf:instanceOf, {}:id or {}:parsetype attribute + if (name->Equals(nsGkAtoms::property, kNameSpaceID_RDF) || + name->Equals(nsGkAtoms::instanceOf, kNameSpaceID_RDF) || + name->Equals(nsGkAtoms::id, kNameSpaceID_None) || + name->Equals(nsGkAtoms::parsetype, kNameSpaceID_None)) { + continue; + } + + int32_t attrNameSpaceID = name->NamespaceID(); + if (attrNameSpaceID == kNameSpaceID_XMLNS) + continue; + nsIAtom* attr = name->LocalName(); + + nsAutoString value; + aQueryElement->GetAttr(attrNameSpaceID, attr, value); + + TestNode* testnode = nullptr; + + if (name->Equals(nsGkAtoms::iscontainer, kNameSpaceID_None) || + name->Equals(nsGkAtoms::isempty, kNameSpaceID_None)) { + // Tests about containerhood and emptiness. These can be + // globbed together, mostly. Check to see if we've already + // added a container test: we only need one. + if (hasContainerTest) + continue; + + nsRDFConInstanceTestNode::Test iscontainer = + nsRDFConInstanceTestNode::eDontCare; + + static nsIContent::AttrValuesArray strings[] = + {&nsGkAtoms::_true, &nsGkAtoms::_false, nullptr}; + switch (aQueryElement->FindAttrValueIn(kNameSpaceID_None, + nsGkAtoms::iscontainer, + strings, eCaseMatters)) { + case 0: iscontainer = nsRDFConInstanceTestNode::eTrue; break; + case 1: iscontainer = nsRDFConInstanceTestNode::eFalse; break; + } + + nsRDFConInstanceTestNode::Test isempty = + nsRDFConInstanceTestNode::eDontCare; + + switch (aQueryElement->FindAttrValueIn(kNameSpaceID_None, + nsGkAtoms::isempty, + strings, eCaseMatters)) { + case 0: isempty = nsRDFConInstanceTestNode::eTrue; break; + case 1: isempty = nsRDFConInstanceTestNode::eFalse; break; + } + + testnode = new nsRDFConInstanceTestNode(prevnode, + this, + aQuery->mMemberVariable, + iscontainer, + isempty); + + rv = mAllTests.Add(testnode); + if (NS_FAILED(rv)) { + delete testnode; + return rv; + } + + rv = mRDFTests.Add(testnode); + if (NS_FAILED(rv)) + return rv; + } + else if (attrNameSpaceID != kNameSpaceID_None || attr != nsGkAtoms::parent) { + // It's a simple RDF test + nsCOMPtr<nsIRDFResource> property; + rv = nsXULContentUtils::GetResource(attrNameSpaceID, attr, getter_AddRefs(property)); + if (NS_FAILED(rv)) + return rv; + + // XXXwaterson this is so manky + nsCOMPtr<nsIRDFNode> target; + if (value.FindChar(':') != -1) { // XXXwaterson WRONG WRONG WRONG! + nsCOMPtr<nsIRDFResource> resource; + rv = gRDFService->GetUnicodeResource(value, getter_AddRefs(resource)); + if (NS_FAILED(rv)) + return rv; + + target = do_QueryInterface(resource); + } + else { + nsAutoString parseType; + aQueryElement->GetAttr(kNameSpaceID_None, nsGkAtoms::parsetype, parseType); + rv = ParseLiteral(parseType, value, getter_AddRefs(target)); + if (NS_FAILED(rv)) + return rv; + } + + testnode = new nsRDFPropertyTestNode(prevnode, this, + aQuery->mMemberVariable, property, target); + rv = mAllTests.Add(testnode); + if (NS_FAILED(rv)) { + delete testnode; + return rv; + } + + rv = mRDFTests.Add(testnode); + if (NS_FAILED(rv)) + return rv; + } + + if (testnode) { + if (prevnode) { + rv = prevnode->AddChild(testnode); + if (NS_FAILED(rv)) + return rv; + } + else { + aQuery->SetRoot(testnode); + } + + prevnode = testnode; + } + } + + *aLastNode = prevnode; + + return NS_OK; +} + +RDFBindingSet* +nsXULTemplateQueryProcessorRDF::GetBindingsForRule(nsIDOMNode* aRuleNode) +{ + return mRuleToBindingsMap.GetWeak(aRuleNode); +} + +void +nsXULTemplateQueryProcessorRDF::AddBindingDependency(nsXULTemplateResultRDF* aResult, + nsIRDFResource* aResource) +{ + ResultArray* arr; + if (!mBindingDependencies.Get(aResource, &arr)) { + arr = new ResultArray(); + + mBindingDependencies.Put(aResource, arr); + } + + int32_t index = arr->IndexOf(aResult); + if (index == -1) + arr->AppendElement(aResult); +} + +void +nsXULTemplateQueryProcessorRDF::RemoveBindingDependency(nsXULTemplateResultRDF* aResult, + nsIRDFResource* aResource) +{ + ResultArray* arr; + if (mBindingDependencies.Get(aResource, &arr)) { + int32_t index = arr->IndexOf(aResult); + if (index >= 0) + arr->RemoveElementAt(index); + } +} + + +nsresult +nsXULTemplateQueryProcessorRDF::AddMemoryElements(const Instantiation& aInst, + nsXULTemplateResultRDF* aResult) +{ + // Add the result to a table indexed by supporting MemoryElement + MemoryElementSet::ConstIterator last = aInst.mSupport.Last(); + for (MemoryElementSet::ConstIterator element = aInst.mSupport.First(); + element != last; ++element) { + + PLHashNumber hash = (element.operator->())->Hash(); + + nsCOMArray<nsXULTemplateResultRDF>* arr; + if (!mMemoryElementToResultMap.Get(hash, &arr)) { + arr = new nsCOMArray<nsXULTemplateResultRDF>(); + mMemoryElementToResultMap.Put(hash, arr); + } + + // results may be added more than once so they will all get deleted properly + arr->AppendObject(aResult); + } + + return NS_OK; +} + +nsresult +nsXULTemplateQueryProcessorRDF::RemoveMemoryElements(const Instantiation& aInst, + nsXULTemplateResultRDF* aResult) +{ + // Remove the results mapped by the supporting MemoryElement + MemoryElementSet::ConstIterator last = aInst.mSupport.Last(); + for (MemoryElementSet::ConstIterator element = aInst.mSupport.First(); + element != last; ++element) { + + PLHashNumber hash = (element.operator->())->Hash(); + + nsCOMArray<nsXULTemplateResultRDF>* arr; + if (mMemoryElementToResultMap.Get(hash, &arr)) { + int32_t index = arr->IndexOf(aResult); + if (index >= 0) + arr->RemoveObjectAt(index); + + uint32_t length = arr->Count(); + if (! length) + mMemoryElementToResultMap.Remove(hash); + } + } + + return NS_OK; +} + +void +nsXULTemplateQueryProcessorRDF::RetractElement(const MemoryElement& aMemoryElement) +{ + if (! mBuilder) + return; + + // when an assertion is removed, look through the memory elements and + // find results that are associated with them. Those results will need + // to be removed because they no longer match. + PLHashNumber hash = aMemoryElement.Hash(); + + nsCOMArray<nsXULTemplateResultRDF>* arr; + if (mMemoryElementToResultMap.Get(hash, &arr)) { + uint32_t length = arr->Count(); + + for (int32_t r = length - 1; r >= 0; r--) { + nsXULTemplateResultRDF* result = (*arr)[r]; + if (result) { + // because the memory elements are hashed by an integer, + // sometimes two different memory elements will have the same + // hash code. In this case we check the result to make sure + // and only remove those that refer to that memory element. + if (result->HasMemoryElement(aMemoryElement)) { + nsITemplateRDFQuery* query = result->Query(); + if (query) { + nsCOMPtr<nsIDOMNode> querynode; + query->GetQueryNode(getter_AddRefs(querynode)); + + mBuilder->RemoveResult(result); + } + + // a call to RemoveMemoryElements may have removed it + if (!mMemoryElementToResultMap.Get(hash, nullptr)) + return; + + // the array should have been reduced by one, but check + // just to make sure + uint32_t newlength = arr->Count(); + if (r > (int32_t)newlength) + r = newlength; + } + } + } + + // if there are no items left, remove the memory element from the hashtable + if (!arr->Count()) + mMemoryElementToResultMap.Remove(hash); + } +} + +int32_t +nsXULTemplateQueryProcessorRDF::GetContainerIndexOf(nsIXULTemplateResult* aResult) +{ + // get the reference variable and look up the container in the result + nsCOMPtr<nsISupports> ref; + nsresult rv = aResult->GetBindingObjectFor(mRefVariable, + getter_AddRefs(ref)); + if (NS_FAILED(rv) || !mDB) + return -1; + + nsCOMPtr<nsIRDFResource> container = do_QueryInterface(ref); + if (container) { + // if the container is an RDF Seq, return the index of the result + // in the container. + bool isSequence = false; + gRDFContainerUtils->IsSeq(mDB, container, &isSequence); + if (isSequence) { + nsCOMPtr<nsIRDFResource> resource; + aResult->GetResource(getter_AddRefs(resource)); + if (resource) { + int32_t index; + gRDFContainerUtils->IndexOf(mDB, container, resource, &index); + return index; + } + } + } + + // if the container isn't a Seq, or the result isn't in the container, + // return -1 indicating no index. + return -1; +} + +nsresult +nsXULTemplateQueryProcessorRDF::GetSortValue(nsIXULTemplateResult* aResult, + nsIRDFResource* aPredicate, + nsIRDFResource* aSortPredicate, + nsISupports** aResultNode) +{ + nsCOMPtr<nsIRDFResource> source; + nsresult rv = aResult->GetResource(getter_AddRefs(source)); + if (NS_FAILED(rv)) + return rv; + + nsCOMPtr<nsIRDFNode> value; + if (source && mDB) { + // first check predicate?sort=true so that datasources may use a + // custom value for sorting + rv = mDB->GetTarget(source, aSortPredicate, true, + getter_AddRefs(value)); + if (NS_FAILED(rv)) + return rv; + + if (!value) { + rv = mDB->GetTarget(source, aPredicate, true, + getter_AddRefs(value)); + if (NS_FAILED(rv)) + return rv; + } + } + + *aResultNode = value; + NS_IF_ADDREF(*aResultNode); + return NS_OK; +} diff --git a/dom/xul/templates/nsXULTemplateQueryProcessorRDF.h b/dom/xul/templates/nsXULTemplateQueryProcessorRDF.h new file mode 100644 index 000000000..30ac34d23 --- /dev/null +++ b/dom/xul/templates/nsXULTemplateQueryProcessorRDF.h @@ -0,0 +1,349 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +#ifndef nsXULTemplateQueryProcessorRDF_h__ +#define nsXULTemplateQueryProcessorRDF_h__ + +#include "nsIRDFContainer.h" +#include "nsIRDFContainerUtils.h" +#include "nsIRDFDataSource.h" +#include "nsIRDFObserver.h" +#include "nsIRDFService.h" +#include "nsIXULTemplateBuilder.h" +#include "nsIXULTemplateQueryProcessor.h" +#include "nsCollationCID.h" + +#include "nsResourceSet.h" +#include "nsRuleNetwork.h" +#include "nsRDFQuery.h" +#include "nsRDFBinding.h" +#include "nsXULTemplateResultSetRDF.h" +#include "nsCOMArray.h" +#include "nsString.h" +#include "nsClassHashtable.h" +#include "nsRefPtrHashtable.h" +#include "nsCycleCollectionParticipant.h" +#include "mozilla/Attributes.h" + +#include "mozilla/Logging.h" +extern mozilla::LazyLogModule gXULTemplateLog; + +class nsIContent; +class nsXULTemplateResultRDF; + +/** + * An object that generates results from a query on an RDF graph + */ +class nsXULTemplateQueryProcessorRDF final : public nsIXULTemplateQueryProcessor, + public nsIRDFObserver +{ +public: + typedef nsTArray<RefPtr<nsXULTemplateResultRDF> > ResultArray; + + nsXULTemplateQueryProcessorRDF(); + + nsresult InitGlobals(); + + // nsISupports interface + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXULTemplateQueryProcessorRDF, + nsIXULTemplateQueryProcessor) + + // nsIXULTemplateQueryProcessor interface + NS_DECL_NSIXULTEMPLATEQUERYPROCESSOR + + // nsIRDFObserver interface + NS_DECL_NSIRDFOBSERVER + + /* + * Propagate all changes through the rule network when an assertion is + * added to the graph, adding any new results. + */ + nsresult + Propagate(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget); + + /* + * Retract all changes through the rule network when an assertion is + * removed from the graph, removing any results that no longer match. + */ + nsresult + Retract(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget); + + /* + * Synchronize results when the graph changes, updating their bindings. + */ + nsresult + SynchronizeAll(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aOldTarget, + nsIRDFNode* aNewTarget); + + /* + * Return true if a resource is a container + */ + nsresult + CheckContainer(nsIRDFResource* aTargetResource, + bool* aIsContainer); + + /* + * Check if a resource does not have any children + */ + nsresult + CheckEmpty(nsIRDFResource* aTargetResource, + bool* aIsEmpty); + + /** + * Check if a resource is a separator + */ + nsresult + CheckIsSeparator(nsIRDFResource* aResource, bool* aIsSeparator); + + /* + * Compute the containment properties which are additional arcs which + * indicate that a node is a container, in additional to the RDF container + * tests. The computed list is stored in mContainmentProperties + */ + nsresult + ComputeContainmentProperties(nsIDOMNode* aRootNode); + + /** + * Compile a query that uses the extended template syntax. The last + * compiled node of the query is returned as aLastNode. This node will + * have been added to mAllTests which owns the node. + */ + nsresult + CompileExtendedQuery(nsRDFQuery* aQuery, + nsIContent* aConditions, + TestNode** aLastNode); + + /** + * Compile a single query child and return the compiled node in aResult. + * This node will have been added to mAllTests which owns the node and + * set as a child of aParentNode. + */ + virtual nsresult + CompileQueryChild(nsIAtom* aTag, + nsRDFQuery* aQuery, + nsIContent* aConditions, + TestNode* aParentNode, + TestNode** aResult); + + /** + * Parse the value of a property test assertion for a condition or a simple + * rule based on the parseType attribute into the appropriate literal type. + */ + nsresult ParseLiteral(const nsString& aParseType, + const nsString& aValue, + nsIRDFNode** aResult); + + /** + * Compile a <triple> condition and return the compiled node in aResult. + * This node will have been added to mAllTests which owns the node and + * set as a child of aParentNode. + */ + nsresult + CompileTripleCondition(nsRDFQuery* aQuery, + nsIContent* aCondition, + TestNode* aParentNode, + TestNode** aResult); + + /** + * Compile a <member> condition and return the compiled node in aResult. + * This node will have been added to mAllTests which owns the node and + * set as a child of aParentNode. + */ + nsresult + CompileMemberCondition(nsRDFQuery* aQuery, + nsIContent* aCondition, + TestNode* aParentNode, + TestNode** aResult); + + /** + * Add the default rules shared by all simple queries. This creates + * the content start node followed by a member test. The member TestNode + * is returned in aChildNode. Both nodes will have been added to mAllTests + * which owns the nodes. + */ + nsresult + AddDefaultSimpleRules(nsRDFQuery* aQuery, + TestNode** aChildNode); + + /** + * Compile a query that's specified using the simple template + * syntax. Each TestNode is created in a chain, the last compiled node + * is returned as aLastNode. All nodes will have been added to mAllTests + * which owns the nodes. + */ + nsresult + CompileSimpleQuery(nsRDFQuery* aQuery, + nsIContent* aQueryElement, + TestNode** aLastNode); + + RDFBindingSet* + GetBindingsForRule(nsIDOMNode* aRule); + + /* + * Indicate that a result is dependant on a particular resource. When an + * assertion is added to or removed from the graph involving that + * resource, that result must be recalculated. + */ + void + AddBindingDependency(nsXULTemplateResultRDF* aResult, + nsIRDFResource* aResource); + + /** + * Remove a dependency a result has on a particular resource. + */ + void + RemoveBindingDependency(nsXULTemplateResultRDF* aResult, + nsIRDFResource* aResource); + + /** + * A memory element is a hash of an RDF triple. One exists for each triple + * that was involved in generating a result. This function adds this to a + * map, keyed by memory element, when the value is a list of results that + * depend on that memory element. When an RDF triple is removed from the + * datasource, RetractElement is called, and this map is examined to + * determine which results are no longer valid. + */ + nsresult + AddMemoryElements(const Instantiation& aInst, + nsXULTemplateResultRDF* aResult); + + /** + * Remove the memory elements associated with a result when the result is + * no longer being used. + */ + nsresult + RemoveMemoryElements(const Instantiation& aInst, + nsXULTemplateResultRDF* aResult); + + /** + * Remove the results associated with a memory element since the + * RDF triple the memory element is a hash of has been removed. + */ + void RetractElement(const MemoryElement& aMemoryElement); + + /** + * Return the index of a result's resource in its RDF container + */ + int32_t + GetContainerIndexOf(nsIXULTemplateResult* aResult); + + /** + * Given a result and a predicate to sort on, get the target value of + * the triple to use for sorting. The sort predicate is the predicate + * with '?sort=true' appended. + */ + nsresult + GetSortValue(nsIXULTemplateResult* aResult, + nsIRDFResource* aPredicate, + nsIRDFResource* aSortPredicate, + nsISupports** aResultNode); + + nsIRDFDataSource* GetDataSource() { return mDB; } + + nsIXULTemplateBuilder* GetBuilder() { return mBuilder; } + + nsResourceSet& ContainmentProperties() { return mContainmentProperties; } + + nsresult + Log(const char* aOperation, + nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget); + +#define LOG(_op, _src, _prop, _targ) \ + Log(_op, _src, _prop, _targ) + +protected: + ~nsXULTemplateQueryProcessorRDF(); + + // We are an observer of the composite datasource. The cycle is + // broken when the document is destroyed. + nsCOMPtr<nsIRDFDataSource> mDB; + + // weak reference to the builder, cleared when the document is destroyed + nsIXULTemplateBuilder* mBuilder; + + // true if the query processor has been initialized + bool mQueryProcessorRDFInited; + + // true if results have been generated. Once set, bindings can no longer + // be added. If they were, the binding value arrays for results that have + // already been generated would be the wrong size + bool mGenerationStarted; + + // nesting level for RDF batch notifications + int32_t mUpdateBatchNest; + + // containment properties that are checked to determine if a resource is + // a container + nsResourceSet mContainmentProperties; + + // the end node of the default simple node hierarchy + TestNode* mSimpleRuleMemberTest; + + // the reference variable + nsCOMPtr<nsIAtom> mRefVariable; + + // the last ref that was calculated, used for simple rules + nsCOMPtr<nsIXULTemplateResult> mLastRef; + + /** + * A map between nsIRDFNodes that form the left-hand side (the subject) of + * a <binding> and an array of nsIXULTemplateResults. When a new assertion + * is added to the graph involving a particular rdf node, it is looked up + * in this binding map. If it exists, the corresponding results must then + * be synchronized. + */ + nsClassHashtable<nsISupportsHashKey, ResultArray> mBindingDependencies; + + /** + * A map between memory elements and an array of nsIXULTemplateResults. + * When a triple is unasserted from the graph, the corresponding results + * no longer match so they must be removed. + */ + nsClassHashtable<nsUint32HashKey, + nsCOMArray<nsXULTemplateResultRDF> > mMemoryElementToResultMap; + + // map of the rules to the bindings for those rules. + // XXXndeakin this might be better just as an array since there is usually + // ten or fewer rules + nsRefPtrHashtable<nsISupportsHashKey, RDFBindingSet> mRuleToBindingsMap; + + /** + * The queries + */ + nsTArray<nsCOMPtr<nsITemplateRDFQuery> > mQueries; + + /** + * All of the RDF tests in the rule network, which are checked when a new + * assertion is added to the graph. This is a subset of mAllTests, which + * also includes non-RDF tests. + */ + ReteNodeSet mRDFTests; + + /** + * All of the tests in the rule network, owned by this list + */ + ReteNodeSet mAllTests; + + // pseudo-constants + static nsrefcnt gRefCnt; + +public: + static nsIRDFService* gRDFService; + static nsIRDFContainerUtils* gRDFContainerUtils; + static nsIRDFResource* kNC_BookmarkSeparator; + static nsIRDFResource* kRDF_type; +}; + +#endif // nsXULTemplateQueryProcessorRDF_h__ diff --git a/dom/xul/templates/nsXULTemplateQueryProcessorStorage.cpp b/dom/xul/templates/nsXULTemplateQueryProcessorStorage.cpp new file mode 100644 index 000000000..a70307bf5 --- /dev/null +++ b/dom/xul/templates/nsXULTemplateQueryProcessorStorage.cpp @@ -0,0 +1,502 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "prprf.h" + +#include "nsIDOMNodeList.h" +#include "nsUnicharUtils.h" + +#include "nsArrayUtils.h" +#include "nsVariant.h" +#include "nsAppDirectoryServiceDefs.h" + +#include "nsIURI.h" +#include "nsIFileChannel.h" +#include "nsIFile.h" +#include "nsGkAtoms.h" +#include "nsContentUtils.h" + +#include "nsXULTemplateBuilder.h" +#include "nsXULTemplateResultStorage.h" +#include "nsXULContentUtils.h" +#include "nsXULSortService.h" + +#include "mozIStorageService.h" +#include "nsIChannel.h" +#include "nsIDocument.h" +#include "nsNetUtil.h" + +//---------------------------------------------------------------------- +// +// nsXULTemplateResultSetStorage +// + +NS_IMPL_ISUPPORTS(nsXULTemplateResultSetStorage, nsISimpleEnumerator) + + +nsXULTemplateResultSetStorage::nsXULTemplateResultSetStorage(mozIStorageStatement* aStatement) + : mStatement(aStatement) +{ + uint32_t count; + nsresult rv = aStatement->GetColumnCount(&count); + if (NS_FAILED(rv)) { + mStatement = nullptr; + return; + } + for (uint32_t c = 0; c < count; c++) { + nsAutoCString name; + rv = aStatement->GetColumnName(c, name); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr<nsIAtom> columnName = NS_Atomize(NS_LITERAL_CSTRING("?") + name); + mColumnNames.AppendObject(columnName); + } + } +} + +NS_IMETHODIMP +nsXULTemplateResultSetStorage::HasMoreElements(bool *aResult) +{ + if (!mStatement) { + *aResult = false; + return NS_OK; + } + + nsresult rv = mStatement->ExecuteStep(aResult); + NS_ENSURE_SUCCESS(rv, rv); + // Because the nsXULTemplateResultSetStorage is owned by many nsXULTemplateResultStorage objects, + // it could live longer than it needed to get results. + // So we destroy the statement to free resources when all results are fetched + if (!*aResult) { + mStatement = nullptr; + } + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultSetStorage::GetNext(nsISupports **aResult) +{ + nsXULTemplateResultStorage* result = + new nsXULTemplateResultStorage(this); + *aResult = result; + NS_ADDREF(result); + return NS_OK; +} + + +int32_t +nsXULTemplateResultSetStorage::GetColumnIndex(nsIAtom* aColumnName) +{ + int32_t count = mColumnNames.Count(); + for (int32_t c = 0; c < count; c++) { + if (mColumnNames[c] == aColumnName) + return c; + } + + return -1; +} + +void +nsXULTemplateResultSetStorage::FillColumnValues(nsCOMArray<nsIVariant>& aArray) +{ + if (!mStatement) + return; + + int32_t count = mColumnNames.Count(); + + for (int32_t c = 0; c < count; c++) { + RefPtr<nsVariant> value = new nsVariant(); + + int32_t type; + mStatement->GetTypeOfIndex(c, &type); + + if (type == mStatement->VALUE_TYPE_INTEGER) { + int64_t val = mStatement->AsInt64(c); + value->SetAsInt64(val); + } + else if (type == mStatement->VALUE_TYPE_FLOAT) { + double val = mStatement->AsDouble(c); + value->SetAsDouble(val); + } + else { + nsAutoString val; + nsresult rv = mStatement->GetString(c, val); + if (NS_FAILED(rv)) + value->SetAsAString(EmptyString()); + else + value->SetAsAString(val); + } + aArray.AppendObject(value); + } +} + + + +//---------------------------------------------------------------------- +// +// nsXULTemplateQueryProcessorStorage +// + +NS_IMPL_ISUPPORTS(nsXULTemplateQueryProcessorStorage, + nsIXULTemplateQueryProcessor) + + +nsXULTemplateQueryProcessorStorage::nsXULTemplateQueryProcessorStorage() + : mGenerationStarted(false) +{ +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorStorage::GetDatasource(nsIArray* aDataSources, + nsIDOMNode* aRootNode, + bool aIsTrusted, + nsIXULTemplateBuilder* aBuilder, + bool* aShouldDelayBuilding, + nsISupports** aReturn) +{ + *aReturn = nullptr; + *aShouldDelayBuilding = false; + + if (!aIsTrusted) { + return NS_OK; + } + + uint32_t length; + nsresult rv = aDataSources->GetLength(&length); + NS_ENSURE_SUCCESS(rv, rv); + + if (length == 0) { + return NS_OK; + } + + // We get only the first uri. This query processor supports + // only one database at a time. + nsCOMPtr<nsIURI> uri; + uri = do_QueryElementAt(aDataSources, 0); + + if (!uri) { + // No uri in the list of datasources + return NS_OK; + } + + nsCOMPtr<mozIStorageService> storage = + do_GetService("@mozilla.org/storage/service;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIFile> databaseFile; + nsAutoCString scheme; + rv = uri->GetScheme(scheme); + NS_ENSURE_SUCCESS(rv, rv); + + if (scheme.EqualsLiteral("profile")) { + + nsAutoCString path; + rv = uri->GetPath(path); + NS_ENSURE_SUCCESS(rv, rv); + + if (path.IsEmpty()) { + return NS_ERROR_FAILURE; + } + + rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, + getter_AddRefs(databaseFile)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = databaseFile->AppendNative(path); + NS_ENSURE_SUCCESS(rv, rv); + } + else { + nsCOMPtr<nsIChannel> channel; + nsCOMPtr<nsINode> node = do_QueryInterface(aRootNode); + + // The following channel is never openend, so it does not matter what + // securityFlags we pass; let's follow the principle of least privilege. + rv = NS_NewChannel(getter_AddRefs(channel), + uri, + node, + nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED, + nsIContentPolicy::TYPE_OTHER); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(channel, &rv); + if (NS_FAILED(rv)) { // if it fails, not a file url + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_BAD_URI); + return rv; + } + + rv = fileChannel->GetFile(getter_AddRefs(databaseFile)); + NS_ENSURE_SUCCESS(rv, rv); + } + + // ok now we have an URI of a sqlite file + nsCOMPtr<mozIStorageConnection> connection; + rv = storage->OpenDatabase(databaseFile, getter_AddRefs(connection)); + if (NS_FAILED(rv)) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_CANNOT_OPEN_DATABASE); + return rv; + } + + connection.forget(aReturn); + return NS_OK; +} + + + +NS_IMETHODIMP +nsXULTemplateQueryProcessorStorage::InitializeForBuilding(nsISupports* aDatasource, + nsIXULTemplateBuilder* aBuilder, + nsIDOMNode* aRootNode) +{ + NS_ENSURE_STATE(!mGenerationStarted); + + mStorageConnection = do_QueryInterface(aDatasource); + if (!mStorageConnection) + return NS_ERROR_INVALID_ARG; + + bool ready; + mStorageConnection->GetConnectionReady(&ready); + if (!ready) + return NS_ERROR_UNEXPECTED; + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorStorage::Done() +{ + mGenerationStarted = false; + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorStorage::CompileQuery(nsIXULTemplateBuilder* aBuilder, + nsIDOMNode* aQueryNode, + nsIAtom* aRefVariable, + nsIAtom* aMemberVariable, + nsISupports** aReturn) +{ + nsCOMPtr<nsIDOMNodeList> childNodes; + aQueryNode->GetChildNodes(getter_AddRefs(childNodes)); + + uint32_t length; + childNodes->GetLength(&length); + + nsCOMPtr<mozIStorageStatement> statement; + nsCOMPtr<nsIContent> queryContent = do_QueryInterface(aQueryNode); + nsAutoString sqlQuery; + + // Let's get all text nodes (which should be the query) + if (!nsContentUtils::GetNodeTextContent(queryContent, false, sqlQuery, fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + nsresult rv = mStorageConnection->CreateStatement(NS_ConvertUTF16toUTF8(sqlQuery), + getter_AddRefs(statement)); + if (NS_FAILED(rv)) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_BAD_QUERY); + return rv; + } + + uint32_t parameterCount = 0; + for (nsIContent* child = queryContent->GetFirstChild(); + child; + child = child->GetNextSibling()) { + + if (child->NodeInfo()->Equals(nsGkAtoms::param, kNameSpaceID_XUL)) { + nsAutoString value; + if (!nsContentUtils::GetNodeTextContent(child, false, value, fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + uint32_t index = parameterCount; + nsAutoString name, indexValue; + + if (child->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name)) { + rv = statement->GetParameterIndex(NS_ConvertUTF16toUTF8(name), + &index); + if (NS_FAILED(rv)) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_UNKNOWN_QUERY_PARAMETER); + return rv; + } + parameterCount++; + } + else if (child->GetAttr(kNameSpaceID_None, nsGkAtoms::index, indexValue)) { + PR_sscanf(NS_ConvertUTF16toUTF8(indexValue).get(),"%d",&index); + if (index > 0) + index--; + } + else { + parameterCount++; + } + + static nsIContent::AttrValuesArray sTypeValues[] = + { &nsGkAtoms::int32, &nsGkAtoms::integer, &nsGkAtoms::int64, + &nsGkAtoms::null, &nsGkAtoms::double_, &nsGkAtoms::string, nullptr }; + + int32_t typeError = 1; + int32_t typeValue = child->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::type, + sTypeValues, eCaseMatters); + rv = NS_ERROR_ILLEGAL_VALUE; + int32_t valInt32 = 0; + int64_t valInt64 = 0; + double valFloat = 0; + + switch (typeValue) { + case 0: + case 1: + typeError = PR_sscanf(NS_ConvertUTF16toUTF8(value).get(),"%d",&valInt32); + if (typeError > 0) + rv = statement->BindInt32ByIndex(index, valInt32); + break; + case 2: + typeError = PR_sscanf(NS_ConvertUTF16toUTF8(value).get(),"%lld",&valInt64); + if (typeError > 0) + rv = statement->BindInt64ByIndex(index, valInt64); + break; + case 3: + rv = statement->BindNullByIndex(index); + break; + case 4: + typeError = PR_sscanf(NS_ConvertUTF16toUTF8(value).get(),"%lf",&valFloat); + if (typeError > 0) + rv = statement->BindDoubleByIndex(index, valFloat); + break; + case 5: + case nsIContent::ATTR_MISSING: + rv = statement->BindStringByIndex(index, value); + break; + default: + typeError = 0; + } + + if (typeError <= 0) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_WRONG_TYPE_QUERY_PARAMETER); + return rv; + } + + if (NS_FAILED(rv)) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_QUERY_PARAMETER_NOT_BOUND); + return rv; + } + } + } + + *aReturn = statement; + NS_IF_ADDREF(*aReturn); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorStorage::GenerateResults(nsISupports* aDatasource, + nsIXULTemplateResult* aRef, + nsISupports* aQuery, + nsISimpleEnumerator** aResults) +{ + mGenerationStarted = true; + + nsCOMPtr<mozIStorageStatement> statement = do_QueryInterface(aQuery); + if (!statement) + return NS_ERROR_FAILURE; + + nsXULTemplateResultSetStorage* results = + new nsXULTemplateResultSetStorage(statement); + *aResults = results; + NS_ADDREF(*aResults); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorStorage::AddBinding(nsIDOMNode* aRuleNode, + nsIAtom* aVar, + nsIAtom* aRef, + const nsAString& aExpr) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorStorage::TranslateRef(nsISupports* aDatasource, + const nsAString& aRefString, + nsIXULTemplateResult** aRef) +{ + nsXULTemplateResultStorage* result = + new nsXULTemplateResultStorage(nullptr); + *aRef = result; + NS_ADDREF(*aRef); + return NS_OK; +} + + +NS_IMETHODIMP +nsXULTemplateQueryProcessorStorage::CompareResults(nsIXULTemplateResult* aLeft, + nsIXULTemplateResult* aRight, + nsIAtom* aVar, + uint32_t aSortHints, + int32_t* aResult) +{ + *aResult = 0; + if (!aVar) + return NS_OK; + + // We're going to see if values are integers or float, to perform + // a suitable comparison + nsCOMPtr<nsISupports> leftValue, rightValue; + if (aLeft) + aLeft->GetBindingObjectFor(aVar, getter_AddRefs(leftValue)); + if (aRight) + aRight->GetBindingObjectFor(aVar, getter_AddRefs(rightValue)); + + if (leftValue && rightValue) { + nsCOMPtr<nsIVariant> vLeftValue = do_QueryInterface(leftValue); + nsCOMPtr<nsIVariant> vRightValue = do_QueryInterface(rightValue); + + if (vLeftValue && vRightValue) { + nsresult rv1, rv2; + uint16_t vtypeL, vtypeR; + vLeftValue->GetDataType(&vtypeL); + vRightValue->GetDataType(&vtypeR); + + if (vtypeL == vtypeR) { + if (vtypeL == nsIDataType::VTYPE_INT64) { + int64_t leftValue, rightValue; + rv1 = vLeftValue->GetAsInt64(&leftValue); + rv2 = vRightValue->GetAsInt64(&rightValue); + if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) { + if (leftValue > rightValue) + *aResult = 1; + else if (leftValue < rightValue) + *aResult = -1; + return NS_OK; + } + } + else if (vtypeL == nsIDataType::VTYPE_DOUBLE) { + double leftValue, rightValue; + rv1 = vLeftValue->GetAsDouble(&leftValue); + rv2 = vRightValue->GetAsDouble(&rightValue); + if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) { + if (leftValue > rightValue) + *aResult = 1; + else if (leftValue < rightValue) + *aResult = -1; + return NS_OK; + } + } + } + } + } + + // Values are not integers or floats, so we just compare them as simple strings + nsAutoString leftVal; + if (aLeft) + aLeft->GetBindingFor(aVar, leftVal); + + nsAutoString rightVal; + if (aRight) + aRight->GetBindingFor(aVar, rightVal); + + *aResult = XULSortServiceImpl::CompareValues(leftVal, rightVal, aSortHints); + return NS_OK; +} diff --git a/dom/xul/templates/nsXULTemplateQueryProcessorStorage.h b/dom/xul/templates/nsXULTemplateQueryProcessorStorage.h new file mode 100644 index 000000000..8c52f139b --- /dev/null +++ b/dom/xul/templates/nsXULTemplateQueryProcessorStorage.h @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsXULTemplateQueryProcessorStorage_h__ +#define nsXULTemplateQueryProcessorStorage_h__ + +#include "nsIXULTemplateBuilder.h" +#include "nsIXULTemplateQueryProcessor.h" + +#include "nsISimpleEnumerator.h" +#include "nsCOMArray.h" +#include "nsIVariant.h" + +#include "mozIStorageValueArray.h" +#include "mozIStorageStatement.h" +#include "mozIStorageConnection.h" +#include "mozilla/Attributes.h" + +class nsXULTemplateQueryProcessorStorage; + +class nsXULTemplateResultSetStorage final : public nsISimpleEnumerator +{ +private: + + nsCOMPtr<mozIStorageStatement> mStatement; + + nsCOMArray<nsIAtom> mColumnNames; + + ~nsXULTemplateResultSetStorage() {} + +public: + + // nsISupports interface + NS_DECL_ISUPPORTS + + // nsISimpleEnumerator interface + NS_DECL_NSISIMPLEENUMERATOR + + explicit nsXULTemplateResultSetStorage(mozIStorageStatement* aStatement); + + int32_t GetColumnIndex(nsIAtom* aColumnName); + + void FillColumnValues(nsCOMArray<nsIVariant>& aArray); + +}; + +class nsXULTemplateQueryProcessorStorage final : public nsIXULTemplateQueryProcessor +{ +public: + + nsXULTemplateQueryProcessorStorage(); + + // nsISupports interface + NS_DECL_ISUPPORTS + + // nsIXULTemplateQueryProcessor interface + NS_DECL_NSIXULTEMPLATEQUERYPROCESSOR + +private: + + ~nsXULTemplateQueryProcessorStorage() {} + + nsCOMPtr<mozIStorageConnection> mStorageConnection; + bool mGenerationStarted; +}; + +#endif // nsXULTemplateQueryProcessorStorage_h__ diff --git a/dom/xul/templates/nsXULTemplateQueryProcessorXML.cpp b/dom/xul/templates/nsXULTemplateQueryProcessorXML.cpp new file mode 100644 index 000000000..1c6fed252 --- /dev/null +++ b/dom/xul/templates/nsXULTemplateQueryProcessorXML.cpp @@ -0,0 +1,449 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsCOMPtr.h" +#include "nsAutoPtr.h" +#include "nsIDOMDocument.h" +#include "nsIDOMNode.h" +#include "nsIDOMElement.h" +#include "nsIDOMEvent.h" +#include "nsIDocument.h" +#include "nsIContent.h" +#include "nsComponentManagerUtils.h" +#include "nsGkAtoms.h" +#include "nsIURI.h" +#include "nsIArray.h" +#include "nsIScriptContext.h" +#include "nsArrayUtils.h" +#include "nsPIDOMWindow.h" +#include "nsXULContentUtils.h" +#include "mozilla/dom/XPathEvaluator.h" +#include "nsXULTemplateQueryProcessorXML.h" +#include "nsXULTemplateResultXML.h" +#include "nsXULSortService.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/XMLHttpRequest.h" + +using namespace mozilla; +using namespace mozilla::dom; + +NS_IMPL_ISUPPORTS(nsXMLQuery, nsXMLQuery) + +//---------------------------------------------------------------------- +// +// nsXULTemplateResultSetXML +// + +NS_IMPL_ISUPPORTS(nsXULTemplateResultSetXML, nsISimpleEnumerator) + +NS_IMETHODIMP +nsXULTemplateResultSetXML::HasMoreElements(bool *aResult) +{ + // if GetSnapshotLength failed, then the return type was not a set of + // nodes, so just return false in this case. + ErrorResult rv; + uint32_t length = mResults->GetSnapshotLength(rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + *aResult = false; + return NS_OK; + } + + *aResult = mPosition < length; + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultSetXML::GetNext(nsISupports **aResult) +{ + ErrorResult rv; + nsINode* node = mResults->SnapshotItem(mPosition, rv); + if (rv.Failed()) { + return rv.StealNSResult(); + } + + nsXULTemplateResultXML* result = + new nsXULTemplateResultXML(mQuery, node->AsContent(), mBindingSet); + + ++mPosition; + *aResult = result; + NS_ADDREF(result); + return NS_OK; +} + + +//---------------------------------------------------------------------- +// +// nsXULTemplateQueryProcessorXML +// + +NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULTemplateQueryProcessorXML) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULTemplateQueryProcessorXML) + tmp->mRuleToBindingsMap.Clear(); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mEvaluator) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateBuilder) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mRequest) +NS_IMPL_CYCLE_COLLECTION_UNLINK_END +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULTemplateQueryProcessorXML) + for (auto it = tmp->mRuleToBindingsMap.Iter(); !it.Done(); it.Next()) { + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mRuleToBindingsMap key"); + cb.NoteXPCOMChild(it.Key()); + } + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvaluator) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateBuilder) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRequest) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULTemplateQueryProcessorXML) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULTemplateQueryProcessorXML) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULTemplateQueryProcessorXML) + NS_INTERFACE_MAP_ENTRY(nsIXULTemplateQueryProcessor) + NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXULTemplateQueryProcessor) +NS_INTERFACE_MAP_END + +/* + * Only the first datasource in aDataSource is used, which should be either an + * nsIURI of an XML document, or a DOM node. If the former, GetDatasource will + * load the document asynchronously and return null in aResult. Once the + * document has loaded, the builder's datasource will be set to the XML + * document. If the datasource is a DOM node, the node will be returned in + * aResult. + */ +NS_IMETHODIMP +nsXULTemplateQueryProcessorXML::GetDatasource(nsIArray* aDataSources, + nsIDOMNode* aRootNode, + bool aIsTrusted, + nsIXULTemplateBuilder* aBuilder, + bool* aShouldDelayBuilding, + nsISupports** aResult) +{ + *aResult = nullptr; + *aShouldDelayBuilding = false; + + nsresult rv; + uint32_t length; + + aDataSources->GetLength(&length); + if (length == 0) + return NS_OK; + + // we get only the first item, because the query processor supports only + // one document as a datasource + + nsCOMPtr<nsIDOMNode> node = do_QueryElementAt(aDataSources, 0); + if (node) { + return CallQueryInterface(node, aResult); + } + + nsCOMPtr<nsIURI> uri = do_QueryElementAt(aDataSources, 0); + if (!uri) + return NS_ERROR_UNEXPECTED; + + nsAutoCString uriStr; + rv = uri->GetSpec(uriStr); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIContent> root = do_QueryInterface(aRootNode); + if (!root) + return NS_ERROR_UNEXPECTED; + + nsCOMPtr<nsIDocument> doc = root->GetUncomposedDoc(); + if (!doc) + return NS_ERROR_UNEXPECTED; + + nsIPrincipal *docPrincipal = doc->NodePrincipal(); + + bool hasHadScriptObject = true; + nsIScriptGlobalObject* scriptObject = + doc->GetScriptHandlingObject(hasHadScriptObject); + NS_ENSURE_STATE(scriptObject); + + nsCOMPtr<nsIXMLHttpRequest> req = + do_CreateInstance(NS_XMLHTTPREQUEST_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = req->Init(docPrincipal, scriptObject, nullptr, nullptr); + NS_ENSURE_SUCCESS(rv, rv); + + rv = req->Open(NS_LITERAL_CSTRING("GET"), uriStr, true, + EmptyString(), EmptyString()); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<EventTarget> target(do_QueryInterface(req)); + rv = target->AddEventListener(NS_LITERAL_STRING("load"), this, false); + NS_ENSURE_SUCCESS(rv, rv); + + rv = target->AddEventListener(NS_LITERAL_STRING("error"), this, false); + NS_ENSURE_SUCCESS(rv, rv); + + rv = req->Send(nullptr); + NS_ENSURE_SUCCESS(rv, rv); + + mTemplateBuilder = aBuilder; + mRequest = req; + + *aShouldDelayBuilding = true; + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorXML::InitializeForBuilding(nsISupports* aDatasource, + nsIXULTemplateBuilder* aBuilder, + nsIDOMNode* aRootNode) +{ + if (mGenerationStarted) + return NS_ERROR_UNEXPECTED; + + // the datasource is either a document or a DOM element + nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDatasource); + if (doc) + mRoot = doc->GetDocumentElement(); + else + mRoot = do_QueryInterface(aDatasource); + NS_ENSURE_STATE(mRoot); + + mEvaluator = new XPathEvaluator(); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorXML::Done() +{ + mGenerationStarted = false; + + mRuleToBindingsMap.Clear(); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorXML::CompileQuery(nsIXULTemplateBuilder* aBuilder, + nsIDOMNode* aQueryNode, + nsIAtom* aRefVariable, + nsIAtom* aMemberVariable, + nsISupports** _retval) +{ + *_retval = nullptr; + + nsCOMPtr<nsIContent> content = do_QueryInterface(aQueryNode); + + nsAutoString expr; + content->GetAttr(kNameSpaceID_None, nsGkAtoms::expr, expr); + + // if an expression is not specified, then the default is to + // just take all of the children + if (expr.IsEmpty()) + expr.Assign('*'); + + ErrorResult rv; + nsAutoPtr<XPathExpression> compiledexpr; + compiledexpr = CreateExpression(expr, content, rv); + if (rv.Failed()) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BAD_XPATH); + return rv.StealNSResult(); + } + + RefPtr<nsXMLQuery> query = + new nsXMLQuery(this, aMemberVariable, Move(compiledexpr)); + + for (nsIContent* condition = content->GetFirstChild(); + condition; + condition = condition->GetNextSibling()) { + + if (condition->NodeInfo()->Equals(nsGkAtoms::assign, + kNameSpaceID_XUL)) { + nsAutoString var; + condition->GetAttr(kNameSpaceID_None, nsGkAtoms::var, var); + + nsAutoString expr; + condition->GetAttr(kNameSpaceID_None, nsGkAtoms::expr, expr); + + // ignore assignments without a variable or an expression + if (!var.IsEmpty() && !expr.IsEmpty()) { + compiledexpr = CreateExpression(expr, condition, rv); + if (rv.Failed()) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BAD_ASSIGN_XPATH); + return rv.StealNSResult(); + } + + nsCOMPtr<nsIAtom> varatom = NS_Atomize(var); + + query->AddBinding(varatom, Move(compiledexpr)); + } + } + } + + query.forget(_retval); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorXML::GenerateResults(nsISupports* aDatasource, + nsIXULTemplateResult* aRef, + nsISupports* aQuery, + nsISimpleEnumerator** aResults) +{ + if (!aQuery) + return NS_ERROR_INVALID_ARG; + + mGenerationStarted = true; + + nsCOMPtr<nsXMLQuery> xmlquery = do_QueryInterface(aQuery); + if (!xmlquery) + return NS_ERROR_INVALID_ARG; + + nsCOMPtr<nsISupports> supports; + nsCOMPtr<nsINode> context; + if (aRef) + aRef->GetBindingObjectFor(xmlquery->GetMemberVariable(), + getter_AddRefs(supports)); + context = do_QueryInterface(supports); + if (!context) + context = mRoot; + + XPathExpression* expr = xmlquery->GetResultsExpression(); + if (!expr) + return NS_ERROR_FAILURE; + + ErrorResult rv; + RefPtr<XPathResult> exprresults = + expr->Evaluate(*context, XPathResult::ORDERED_NODE_SNAPSHOT_TYPE, + nullptr, rv); + if (rv.Failed()) { + return rv.StealNSResult(); + } + + RefPtr<nsXULTemplateResultSetXML> results = + new nsXULTemplateResultSetXML(xmlquery, exprresults.forget(), + xmlquery->GetBindingSet()); + + results.forget(aResults); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorXML::AddBinding(nsIDOMNode* aRuleNode, + nsIAtom* aVar, + nsIAtom* aRef, + const nsAString& aExpr) +{ + if (mGenerationStarted) + return NS_ERROR_FAILURE; + + RefPtr<nsXMLBindingSet> bindings = mRuleToBindingsMap.GetWeak(aRuleNode); + if (!bindings) { + bindings = new nsXMLBindingSet(); + mRuleToBindingsMap.Put(aRuleNode, bindings); + } + + nsCOMPtr<nsINode> ruleNode = do_QueryInterface(aRuleNode); + + ErrorResult rv; + nsAutoPtr<XPathExpression> compiledexpr; + compiledexpr = CreateExpression(aExpr, ruleNode, rv); + if (rv.Failed()) { + rv.SuppressException(); + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BAD_BINDING_XPATH); + return NS_OK; + } + + // aRef isn't currently used for XML query processors + bindings->AddBinding(aVar, Move(compiledexpr)); + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorXML::TranslateRef(nsISupports* aDatasource, + const nsAString& aRefString, + nsIXULTemplateResult** aRef) +{ + *aRef = nullptr; + + // the datasource is either a document or a DOM element + nsCOMPtr<Element> rootElement; + nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDatasource); + if (doc) + rootElement = doc->GetRootElement(); + else + rootElement = do_QueryInterface(aDatasource); + + // if no root element, just return. The document may not have loaded yet + if (!rootElement) + return NS_OK; + + RefPtr<nsXULTemplateResultXML> result = new nsXULTemplateResultXML(nullptr, rootElement, nullptr); + result.forget(aRef); + + return NS_OK; +} + + +NS_IMETHODIMP +nsXULTemplateQueryProcessorXML::CompareResults(nsIXULTemplateResult* aLeft, + nsIXULTemplateResult* aRight, + nsIAtom* aVar, + uint32_t aSortHints, + int32_t* aResult) +{ + *aResult = 0; + if (!aVar) + return NS_OK; + + nsAutoString leftVal; + if (aLeft) + aLeft->GetBindingFor(aVar, leftVal); + + nsAutoString rightVal; + if (aRight) + aRight->GetBindingFor(aVar, rightVal); + + *aResult = XULSortServiceImpl::CompareValues(leftVal, rightVal, aSortHints); + return NS_OK; +} + +nsXMLBindingSet* +nsXULTemplateQueryProcessorXML::GetOptionalBindingsForRule(nsIDOMNode* aRuleNode) +{ + return mRuleToBindingsMap.GetWeak(aRuleNode); +} + +XPathExpression* +nsXULTemplateQueryProcessorXML::CreateExpression(const nsAString& aExpr, + nsINode* aNode, + ErrorResult& aRv) +{ + return mEvaluator->CreateExpression(aExpr, aNode, aRv); +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorXML::HandleEvent(nsIDOMEvent* aEvent) +{ + NS_PRECONDITION(aEvent, "aEvent null"); + nsAutoString eventType; + aEvent->GetType(eventType); + + if (eventType.EqualsLiteral("load") && mTemplateBuilder) { + NS_ASSERTION(mRequest, "request was not set"); + nsCOMPtr<nsIDOMDocument> doc; + if (NS_SUCCEEDED(mRequest->GetResponseXML(getter_AddRefs(doc)))) + mTemplateBuilder->SetDatasource(doc); + + // to avoid leak. we don't need it after... + mTemplateBuilder = nullptr; + mRequest = nullptr; + } + else if (eventType.EqualsLiteral("error")) { + mTemplateBuilder = nullptr; + mRequest = nullptr; + } + + return NS_OK; +} diff --git a/dom/xul/templates/nsXULTemplateQueryProcessorXML.h b/dom/xul/templates/nsXULTemplateQueryProcessorXML.h new file mode 100644 index 000000000..dec508415 --- /dev/null +++ b/dom/xul/templates/nsXULTemplateQueryProcessorXML.h @@ -0,0 +1,170 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsXULTemplateQueryProcessorXML_h__ +#define nsXULTemplateQueryProcessorXML_h__ + +#include "nsIXULTemplateBuilder.h" +#include "nsIXULTemplateQueryProcessor.h" + +#include "nsAutoPtr.h" +#include "nsISimpleEnumerator.h" +#include "nsString.h" +#include "nsCOMArray.h" +#include "nsRefPtrHashtable.h" +#include "nsIDOMEventListener.h" +#include "nsIDOMXPathEvaluator.h" +#include "nsXMLBinding.h" +#include "nsCycleCollectionParticipant.h" +#include "nsIXMLHttpRequest.h" +#include "mozilla/Attributes.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/XPathEvaluator.h" +#include "mozilla/dom/XPathResult.h" + +class nsXULTemplateQueryProcessorXML; + +#define NS_IXMLQUERY_IID \ + {0x0358d692, 0xccce, 0x4a97, \ + { 0xb2, 0x51, 0xba, 0x8f, 0x17, 0x0f, 0x3b, 0x6f }} + +class nsXMLQuery final : public nsISupports +{ + public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IXMLQUERY_IID) + + NS_DECL_ISUPPORTS + + // return a weak reference to the processor the query was created from + nsXULTemplateQueryProcessorXML* Processor() { return mProcessor; } + + // return a weak reference t the member variable for the query + nsIAtom* GetMemberVariable() { return mMemberVariable; } + + // return a weak reference to the expression used to generate results + mozilla::dom::XPathExpression* GetResultsExpression() + { return mResultsExpr; } + + // return a weak reference to the additional required bindings + nsXMLBindingSet* GetBindingSet() { return mRequiredBindings; } + + // add a required binding for the query + void + AddBinding(nsIAtom* aVar, nsAutoPtr<mozilla::dom::XPathExpression>&& aExpr) + { + if (!mRequiredBindings) { + mRequiredBindings = new nsXMLBindingSet(); + } + + mRequiredBindings->AddBinding(aVar, mozilla::Move(aExpr)); + } + + nsXMLQuery(nsXULTemplateQueryProcessorXML* aProcessor, + nsIAtom* aMemberVariable, + nsAutoPtr<mozilla::dom::XPathExpression>&& aResultsExpr) + : mProcessor(aProcessor), + mMemberVariable(aMemberVariable), + mResultsExpr(aResultsExpr) + { } + + protected: + ~nsXMLQuery() {} + + nsXULTemplateQueryProcessorXML* mProcessor; + + nsCOMPtr<nsIAtom> mMemberVariable; + + nsAutoPtr<mozilla::dom::XPathExpression> mResultsExpr; + + RefPtr<nsXMLBindingSet> mRequiredBindings; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsXMLQuery, NS_IXMLQUERY_IID) + +class nsXULTemplateResultSetXML final : public nsISimpleEnumerator +{ +private: + + // reference back to the query + nsCOMPtr<nsXMLQuery> mQuery; + + // the binding set created from <assign> nodes + RefPtr<nsXMLBindingSet> mBindingSet; + + // set of results contained in this enumerator + RefPtr<mozilla::dom::XPathResult> mResults; + + // current position within the list of results + uint32_t mPosition; + + ~nsXULTemplateResultSetXML() {} + +public: + + // nsISupports interface + NS_DECL_ISUPPORTS + + // nsISimpleEnumerator interface + NS_DECL_NSISIMPLEENUMERATOR + + nsXULTemplateResultSetXML(nsXMLQuery* aQuery, + already_AddRefed<mozilla::dom::XPathResult> aResults, + nsXMLBindingSet* aBindingSet) + : mQuery(aQuery), + mBindingSet(aBindingSet), + mResults(aResults), + mPosition(0) + {} +}; + +class nsXULTemplateQueryProcessorXML final : public nsIXULTemplateQueryProcessor, + public nsIDOMEventListener +{ +public: + + nsXULTemplateQueryProcessorXML() + : mGenerationStarted(false) + {} + + // nsISupports interface + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXULTemplateQueryProcessorXML, + nsIXULTemplateQueryProcessor) + + // nsIXULTemplateQueryProcessor interface + NS_DECL_NSIXULTEMPLATEQUERYPROCESSOR + + // nsIDOMEventListener interface + NS_DECL_NSIDOMEVENTLISTENER + + nsXMLBindingSet* + GetOptionalBindingsForRule(nsIDOMNode* aRuleNode); + + // create an XPath expression from aExpr, using aNode for + // resolving namespaces + mozilla::dom::XPathExpression* + CreateExpression(const nsAString& aExpr, + nsINode* aNode, + mozilla::ErrorResult& aRv); + +private: + + ~nsXULTemplateQueryProcessorXML() {} + + bool mGenerationStarted; + + nsRefPtrHashtable<nsISupportsHashKey, nsXMLBindingSet> mRuleToBindingsMap; + + nsCOMPtr<mozilla::dom::Element> mRoot; + + RefPtr<mozilla::dom::XPathEvaluator> mEvaluator; + + nsCOMPtr<nsIXULTemplateBuilder> mTemplateBuilder; + + nsCOMPtr<nsIXMLHttpRequest> mRequest; +}; + + +#endif // nsXULTemplateQueryProcessorXML_h__ diff --git a/dom/xul/templates/nsXULTemplateResultRDF.cpp b/dom/xul/templates/nsXULTemplateResultRDF.cpp new file mode 100644 index 000000000..9d53bab7d --- /dev/null +++ b/dom/xul/templates/nsXULTemplateResultRDF.cpp @@ -0,0 +1,208 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsXULTemplateResultRDF.h" +#include "nsXULContentUtils.h" + +// XXXndeakin for some reason, making this class have classinfo breaks trees. +//#include "nsIDOMClassInfo.h" + +NS_IMPL_CYCLE_COLLECTION(nsXULTemplateResultRDF, mQuery) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULTemplateResultRDF) + NS_INTERFACE_MAP_ENTRY(nsIXULTemplateResult) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULTemplateResultRDF) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULTemplateResultRDF) + +nsXULTemplateResultRDF::nsXULTemplateResultRDF(nsIRDFResource* aNode) + : mQuery(nullptr), + mNode(aNode) +{ +} + +nsXULTemplateResultRDF::nsXULTemplateResultRDF(nsRDFQuery* aQuery, + const Instantiation& aInst, + nsIRDFResource *aNode) + : mQuery(aQuery), + mNode(aNode), + mInst(aInst) +{ +} + +nsXULTemplateResultRDF::~nsXULTemplateResultRDF() +{ +} + +NS_IMETHODIMP +nsXULTemplateResultRDF::GetIsContainer(bool* aIsContainer) +{ + *aIsContainer = false; + + if (mNode) { + nsXULTemplateQueryProcessorRDF* processor = GetProcessor(); + if (processor) + return processor->CheckContainer(mNode, aIsContainer); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultRDF::GetIsEmpty(bool* aIsEmpty) +{ + *aIsEmpty = true; + + if (mNode) { + nsXULTemplateQueryProcessorRDF* processor = GetProcessor(); + if (processor) + return processor->CheckEmpty(mNode, aIsEmpty); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultRDF::GetMayProcessChildren(bool* aMayProcessChildren) +{ + // RDF always allows recursion + *aMayProcessChildren = true; + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultRDF::GetId(nsAString& aId) +{ + if (! mNode) + return NS_ERROR_FAILURE; + + const char* uri; + mNode->GetValueConst(&uri); + + CopyUTF8toUTF16(uri, aId); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultRDF::GetResource(nsIRDFResource** aResource) +{ + *aResource = mNode; + NS_IF_ADDREF(*aResource); + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultRDF::GetType(nsAString& aType) +{ + aType.Truncate(); + + nsresult rv = NS_OK; + + nsXULTemplateQueryProcessorRDF* processor = GetProcessor(); + if (processor) { + bool found; + rv = processor->CheckIsSeparator(mNode, &found); + if (NS_SUCCEEDED(rv) && found) + aType.AssignLiteral("separator"); + } + + return rv; +} + +NS_IMETHODIMP +nsXULTemplateResultRDF::GetBindingFor(nsIAtom* aVar, nsAString& aValue) +{ + nsCOMPtr<nsIRDFNode> val; + GetAssignment(aVar, getter_AddRefs(val)); + + return nsXULContentUtils::GetTextForNode(val, aValue); +} + +NS_IMETHODIMP +nsXULTemplateResultRDF::GetBindingObjectFor(nsIAtom* aVar, nsISupports** aValue) +{ + GetAssignment(aVar, (nsIRDFNode **)aValue); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultRDF::RuleMatched(nsISupports* aQuery, nsIDOMNode* aRuleNode) +{ + // when a rule matches, set the bindings that must be used. + nsXULTemplateQueryProcessorRDF* processor = GetProcessor(); + if (processor) { + RDFBindingSet* bindings = processor->GetBindingsForRule(aRuleNode); + if (bindings) { + nsresult rv = mBindingValues.SetBindingSet(bindings); + if (NS_FAILED(rv)) + return rv; + + bindings->AddDependencies(mNode, this); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultRDF::HasBeenRemoved() +{ + // when a result is no longer used, clean up the dependencies and + // memory elements that refer to it + mBindingValues.RemoveDependencies(mNode, this); + + nsXULTemplateQueryProcessorRDF* processor = GetProcessor(); + if (processor) + processor->RemoveMemoryElements(mInst, this); + + return NS_OK; +} + + +void +nsXULTemplateResultRDF::GetAssignment(nsIAtom* aVar, nsIRDFNode** aValue) +{ + // look up a variable in the assignments map + *aValue = nullptr; + mInst.mAssignments.GetAssignmentFor(aVar, aValue); + + // if not found, look up the variable in the bindings + if (! *aValue) + mBindingValues.GetAssignmentFor(this, aVar, aValue); +} + + +bool +nsXULTemplateResultRDF::SyncAssignments(nsIRDFResource* aSubject, + nsIRDFResource* aPredicate, + nsIRDFNode* aTarget) +{ + // synchronize the bindings when an assertion is added or removed + RDFBindingSet* bindingset = mBindingValues.GetBindingSet(); + if (bindingset) { + return bindingset->SyncAssignments(aSubject, aPredicate, aTarget, + (aSubject == mNode) ? mQuery->GetMemberVariable() : nullptr, + this, mBindingValues); + } + + return false; +} + +bool +nsXULTemplateResultRDF::HasMemoryElement(const MemoryElement& aMemoryElement) +{ + MemoryElementSet::ConstIterator last = mInst.mSupport.Last(); + for (MemoryElementSet::ConstIterator element = mInst.mSupport.First(); + element != last; ++element) { + if ((*element).Equals(aMemoryElement)) + return true; + } + + return false; +} diff --git a/dom/xul/templates/nsXULTemplateResultRDF.h b/dom/xul/templates/nsXULTemplateResultRDF.h new file mode 100644 index 000000000..cb5022420 --- /dev/null +++ b/dom/xul/templates/nsXULTemplateResultRDF.h @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsXULTemplateResultRDF_h__ +#define nsXULTemplateResultRDF_h__ + +#include "nsCOMPtr.h" +#include "nsIRDFResource.h" +#include "nsXULTemplateQueryProcessorRDF.h" +#include "nsRDFQuery.h" +#include "nsRuleNetwork.h" +#include "nsIXULTemplateResult.h" +#include "nsRDFBinding.h" +#include "mozilla/Attributes.h" + +/** + * A single result of a query on an RDF graph + */ +class nsXULTemplateResultRDF final : public nsIXULTemplateResult +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_CLASS(nsXULTemplateResultRDF) + + NS_DECL_NSIXULTEMPLATERESULT + + explicit nsXULTemplateResultRDF(nsIRDFResource* aNode); + + nsXULTemplateResultRDF(nsRDFQuery* aQuery, + const Instantiation& aInst, + nsIRDFResource* aNode); + + nsITemplateRDFQuery* Query() { return mQuery; } + + nsXULTemplateQueryProcessorRDF* GetProcessor() + { + return (mQuery ? mQuery->Processor() : nullptr); + } + + /** + * Get the value of a variable, first by looking in the assignments and + * then the bindings + */ + void + GetAssignment(nsIAtom* aVar, nsIRDFNode** aValue); + + /** + * Synchronize the bindings after a change in the RDF graph. Bindings that + * would be affected will be assigned appropriately based on the change. + */ + bool + SyncAssignments(nsIRDFResource* aSubject, + nsIRDFResource* aPredicate, + nsIRDFNode* aTarget); + + /** + * Return true if the result has an instantiation involving a particular + * memory element. + */ + bool + HasMemoryElement(const MemoryElement& aMemoryElement); + +protected: + ~nsXULTemplateResultRDF(); + + // query that generated the result + nsCOMPtr<nsITemplateRDFQuery> mQuery; + + // resource node + nsCOMPtr<nsIRDFResource> mNode; + + // data computed from query + Instantiation mInst; + + // extra assignments made by rules (<binding> tags) + nsBindingValues mBindingValues; +}; + +#endif // nsXULTemplateResultRDF_h__ diff --git a/dom/xul/templates/nsXULTemplateResultSetRDF.cpp b/dom/xul/templates/nsXULTemplateResultSetRDF.cpp new file mode 100644 index 000000000..1ab9a7959 --- /dev/null +++ b/dom/xul/templates/nsXULTemplateResultSetRDF.cpp @@ -0,0 +1,82 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsXULTemplateResultSetRDF.h" +#include "nsXULTemplateQueryProcessorRDF.h" + +NS_IMPL_ISUPPORTS(nsXULTemplateResultSetRDF, nsISimpleEnumerator) + +NS_IMETHODIMP +nsXULTemplateResultSetRDF::HasMoreElements(bool *aResult) +{ + *aResult = true; + + nsCOMPtr<nsIRDFNode> node; + + if (! mInstantiations || ! mQuery) { + *aResult = false; + return NS_OK; + } + + if (mCheckedNext) { + if (!mCurrent || mCurrent == &(mInstantiations->mHead)) + *aResult = false; + return NS_OK; + } + + mCheckedNext = true; + + do { + if (mCurrent) { + mCurrent = mCurrent->mNext; + if (mCurrent == &(mInstantiations->mHead)) { + *aResult = false; + return NS_OK; + } + } + else { + *aResult = ! mInstantiations->Empty(); + if (*aResult) + mCurrent = mInstantiations->mHead.mNext; + } + + // get the value of the member variable. If it is not set, skip + // the result and move on to the next result + if (mCurrent) { + mCurrent->mInstantiation.mAssignments. + GetAssignmentFor(mQuery->mMemberVariable, getter_AddRefs(node)); + } + + // only resources may be used as results + mResource = do_QueryInterface(node); + } while (! mResource); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultSetRDF::GetNext(nsISupports **aResult) +{ + if (!aResult) + return NS_ERROR_NULL_POINTER; + + if (!mCurrent || !mCheckedNext) + return NS_ERROR_FAILURE; + + RefPtr<nsXULTemplateResultRDF> nextresult = + new nsXULTemplateResultRDF(mQuery, mCurrent->mInstantiation, mResource); + if (!nextresult) + return NS_ERROR_OUT_OF_MEMORY; + + // add the supporting memory elements to the processor's map. These are + // used to remove the results when an assertion is removed from the graph + mProcessor->AddMemoryElements(mCurrent->mInstantiation, nextresult); + + mCheckedNext = false; + + nextresult.forget(aResult); + + return NS_OK; +} diff --git a/dom/xul/templates/nsXULTemplateResultSetRDF.h b/dom/xul/templates/nsXULTemplateResultSetRDF.h new file mode 100644 index 000000000..76aa28774 --- /dev/null +++ b/dom/xul/templates/nsXULTemplateResultSetRDF.h @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsXULTemplateResultSetRDF_h__ +#define nsXULTemplateResultSetRDF_h__ + +#include "nsISimpleEnumerator.h" +#include "nsRuleNetwork.h" +#include "nsRDFQuery.h" +#include "nsXULTemplateResultRDF.h" +#include "mozilla/Attributes.h" + +class nsXULTemplateQueryProcessorRDF; +class nsXULTemplateResultRDF; + +/** + * An enumerator used to iterate over a set of results. + */ +class nsXULTemplateResultSetRDF final : public nsISimpleEnumerator +{ +private: + nsXULTemplateQueryProcessorRDF* mProcessor; + + nsRDFQuery* mQuery; + + const InstantiationSet* mInstantiations; + + nsCOMPtr<nsIRDFResource> mResource; + + InstantiationSet::List *mCurrent; + + bool mCheckedNext; + + ~nsXULTemplateResultSetRDF() + { + delete mInstantiations; + } + +public: + + // nsISupports interface + NS_DECL_ISUPPORTS + + // nsISimpleEnumerator interface + NS_DECL_NSISIMPLEENUMERATOR + + nsXULTemplateResultSetRDF(nsXULTemplateQueryProcessorRDF *aProcessor, + nsRDFQuery* aQuery, + const InstantiationSet* aInstantiations) + : mProcessor(aProcessor), + mQuery(aQuery), + mInstantiations(aInstantiations), + mCurrent(nullptr), + mCheckedNext(false) + { } +}; + +#endif // nsXULTemplateResultSetRDF_h__ diff --git a/dom/xul/templates/nsXULTemplateResultStorage.cpp b/dom/xul/templates/nsXULTemplateResultStorage.cpp new file mode 100644 index 000000000..b840fc2c7 --- /dev/null +++ b/dom/xul/templates/nsXULTemplateResultStorage.cpp @@ -0,0 +1,126 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsIServiceManager.h" +#include "nsRDFCID.h" +#include "nsIRDFService.h" +#include "nsString.h" +#include "nsXULTemplateResultStorage.h" + +NS_IMPL_ISUPPORTS(nsXULTemplateResultStorage, nsIXULTemplateResult) + +nsXULTemplateResultStorage::nsXULTemplateResultStorage(nsXULTemplateResultSetStorage* aResultSet) +{ + static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID); + nsCOMPtr<nsIRDFService> rdfService = do_GetService(kRDFServiceCID); + rdfService->GetAnonymousResource(getter_AddRefs(mNode)); + mResultSet = aResultSet; + if (aResultSet) { + mResultSet->FillColumnValues(mValues); + } +} + +nsXULTemplateResultStorage::~nsXULTemplateResultStorage() +{ +} + +NS_IMETHODIMP +nsXULTemplateResultStorage::GetIsContainer(bool* aIsContainer) +{ + *aIsContainer = false; + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultStorage::GetIsEmpty(bool* aIsEmpty) +{ + *aIsEmpty = true; + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultStorage::GetMayProcessChildren(bool* aMayProcessChildren) +{ + *aMayProcessChildren = false; + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultStorage::GetId(nsAString& aId) +{ + const char* uri = nullptr; + mNode->GetValueConst(&uri); + + aId.Assign(NS_ConvertUTF8toUTF16(uri)); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultStorage::GetResource(nsIRDFResource** aResource) +{ + *aResource = mNode; + NS_IF_ADDREF(*aResource); + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultStorage::GetType(nsAString& aType) +{ + aType.Truncate(); + return NS_OK; +} + + +NS_IMETHODIMP +nsXULTemplateResultStorage::GetBindingFor(nsIAtom* aVar, nsAString& aValue) +{ + NS_ENSURE_ARG_POINTER(aVar); + + aValue.Truncate(); + if (!mResultSet) { + return NS_OK; + } + + int32_t idx = mResultSet->GetColumnIndex(aVar); + if (idx < 0) { + return NS_OK; + } + + nsIVariant * value = mValues[idx]; + if (value) { + value->GetAsAString(aValue); + } + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultStorage::GetBindingObjectFor(nsIAtom* aVar, nsISupports** aValue) +{ + NS_ENSURE_ARG_POINTER(aVar); + + if (mResultSet) { + int32_t idx = mResultSet->GetColumnIndex(aVar); + if (idx >= 0) { + *aValue = mValues[idx]; + NS_IF_ADDREF(*aValue); + return NS_OK; + } + } + *aValue = nullptr; + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultStorage::RuleMatched(nsISupports* aQuery, nsIDOMNode* aRuleNode) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultStorage::HasBeenRemoved() +{ + return NS_OK; +} diff --git a/dom/xul/templates/nsXULTemplateResultStorage.h b/dom/xul/templates/nsXULTemplateResultStorage.h new file mode 100644 index 000000000..75cf7b05a --- /dev/null +++ b/dom/xul/templates/nsXULTemplateResultStorage.h @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsXULTemplateResultStorage_h__ +#define nsXULTemplateResultStorage_h__ + +#include "nsXULTemplateQueryProcessorStorage.h" +#include "nsIRDFResource.h" +#include "nsIXULTemplateResult.h" +#include "mozilla/Attributes.h" + +/** + * A single result of a query from mozstorage + */ +class nsXULTemplateResultStorage final : public nsIXULTemplateResult +{ +public: + NS_DECL_ISUPPORTS + + NS_DECL_NSIXULTEMPLATERESULT + + explicit nsXULTemplateResultStorage(nsXULTemplateResultSetStorage* aResultSet); + +protected: + + ~nsXULTemplateResultStorage(); + + RefPtr<nsXULTemplateResultSetStorage> mResultSet; + + nsCOMArray<nsIVariant> mValues; + + nsCOMPtr<nsIRDFResource> mNode; +}; + +#endif // nsXULTemplateResultStorage_h__ diff --git a/dom/xul/templates/nsXULTemplateResultXML.cpp b/dom/xul/templates/nsXULTemplateResultXML.cpp new file mode 100644 index 000000000..6ac4e6004 --- /dev/null +++ b/dom/xul/templates/nsXULTemplateResultXML.cpp @@ -0,0 +1,189 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsIServiceManager.h" +#include "nsIDOMNode.h" +#include "nsIDOMElement.h" +#include "nsIContent.h" + +#include "nsIRDFService.h" + +#include "nsXULTemplateResultXML.h" +#include "nsXMLBinding.h" + +static uint32_t sTemplateId = 0; + +NS_IMPL_ISUPPORTS(nsXULTemplateResultXML, nsIXULTemplateResult) + +nsXULTemplateResultXML::nsXULTemplateResultXML(nsXMLQuery* aQuery, + nsIContent* aNode, + nsXMLBindingSet* aBindings) + : mQuery(aQuery), mNode(aNode) +{ + // If the node has an id, create the uri from it. Otherwise, there isn't + // anything to identify the node with so just use a somewhat random number. + nsCOMPtr<nsIAtom> id = mNode->GetID(); + if (id) { + nsCOMPtr<nsIURI> uri = mNode->GetBaseURI(); + nsAutoCString spec; + uri->GetSpec(spec); + + mId = NS_ConvertUTF8toUTF16(spec); + + nsAutoString idstr; + id->ToString(idstr); + mId += NS_LITERAL_STRING("#") + idstr; + } + else { + nsAutoString rowid(NS_LITERAL_STRING("row")); + rowid.AppendInt(++sTemplateId); + mId.Assign(rowid); + } + + if (aBindings) + mRequiredValues.SetBindingSet(aBindings); +} + +NS_IMETHODIMP +nsXULTemplateResultXML::GetIsContainer(bool* aIsContainer) +{ + // a node is considered a container if it has children + *aIsContainer = mNode && mNode->HasChildNodes(); + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultXML::GetIsEmpty(bool* aIsEmpty) +{ + // a node is considered empty if it has no elements as children + nsCOMPtr<nsIContent> content = do_QueryInterface(mNode); + if (content) { + for (nsIContent* child = content->GetFirstChild(); + child; + child = child->GetNextSibling()) { + if (child->IsElement()) { + *aIsEmpty = false; + return NS_OK; + } + } + } + + *aIsEmpty = true; + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultXML::GetMayProcessChildren(bool* aMayProcessChildren) +{ + *aMayProcessChildren = true; + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultXML::GetId(nsAString& aId) +{ + aId = mId; + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultXML::GetResource(nsIRDFResource** aResource) +{ + *aResource = nullptr; + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultXML::GetType(nsAString& aType) +{ + aType.Truncate(); + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultXML::GetBindingFor(nsIAtom* aVar, nsAString& aValue) +{ + NS_ENSURE_ARG_POINTER(aVar); + + // get the position of the atom in the variables table + nsXMLBinding* binding; + + int32_t idx = mRequiredValues.LookupTargetIndex(aVar, &binding); + if (idx >= 0) { + mRequiredValues.GetStringAssignmentFor(this, binding, idx, aValue); + return NS_OK; + } + + idx = mOptionalValues.LookupTargetIndex(aVar, &binding); + if (idx >= 0) { + mOptionalValues.GetStringAssignmentFor(this, binding, idx, aValue); + return NS_OK; + } + + // if the variable is not bound, just use the variable name as the name of + // an attribute to retrieve + nsAutoString attr; + aVar->ToString(attr); + + if (attr.Length() > 1) { + nsCOMPtr<nsIDOMElement> element = do_QueryInterface(mNode); + if (element) + return element->GetAttribute(Substring(attr, 1), aValue); + } + + aValue.Truncate(); + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultXML::GetBindingObjectFor(nsIAtom* aVar, nsISupports** aValue) +{ + NS_ENSURE_ARG_POINTER(aVar); + + nsXMLBinding* binding; + nsCOMPtr<nsISupports> node; + + if (mQuery && aVar == mQuery->GetMemberVariable()) { + node = mNode; + } + else { + int32_t idx = mRequiredValues.LookupTargetIndex(aVar, &binding); + if (idx > 0) { + node = mRequiredValues.GetNodeAssignmentFor(this, binding, idx); + } + else { + idx = mOptionalValues.LookupTargetIndex(aVar, &binding); + if (idx > 0) { + node = mOptionalValues.GetNodeAssignmentFor(this, binding, idx); + } + } + } + + node.forget(aValue); + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultXML::RuleMatched(nsISupports* aQueryNode, + nsIDOMNode* aRuleNode) +{ + // when a rule matches, set the bindings that must be used. + nsXULTemplateQueryProcessorXML* processor = mQuery ? mQuery->Processor() : + nullptr; + if (processor) { + nsXMLBindingSet* bindings = + processor->GetOptionalBindingsForRule(aRuleNode); + if (bindings) + mOptionalValues.SetBindingSet(bindings); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultXML::HasBeenRemoved() +{ + return NS_OK; +} diff --git a/dom/xul/templates/nsXULTemplateResultXML.h b/dom/xul/templates/nsXULTemplateResultXML.h new file mode 100644 index 000000000..3c42c0829 --- /dev/null +++ b/dom/xul/templates/nsXULTemplateResultXML.h @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsXULTemplateResultXML_h__ +#define nsXULTemplateResultXML_h__ + +#include "nsCOMPtr.h" +#include "nsIContent.h" +#include "nsIURI.h" +#include "nsIRDFResource.h" +#include "nsXULTemplateQueryProcessorXML.h" +#include "nsIXULTemplateResult.h" +#include "mozilla/Attributes.h" + +/** + * An single result of an query + */ +class nsXULTemplateResultXML final : public nsIXULTemplateResult +{ +public: + NS_DECL_ISUPPORTS + + NS_DECL_NSIXULTEMPLATERESULT + + nsXULTemplateResultXML(nsXMLQuery* aQuery, + nsIContent* aNode, + nsXMLBindingSet* aBindings); + + nsIContent* Node() + { + return mNode; + } + +protected: + + ~nsXULTemplateResultXML() {} + + // ID used for persisting data. It is constructed using the mNode's + // base uri plus the node's id to form 'baseuri#id'. If the node has no + // id, then an id of the form 'row<some number>' is generated. In the + // latter case, persistence will not work as there won't be a unique id. + nsAutoString mId; + + // query that generated the result + nsCOMPtr<nsXMLQuery> mQuery; + + // context node in datasource + nsCOMPtr<nsIContent> mNode; + + // assignments in query + nsXMLBindingValues mRequiredValues; + + // extra assignments made by rules (<binding> tags) + nsXMLBindingValues mOptionalValues; +}; + +#endif // nsXULTemplateResultXML_h__ diff --git a/dom/xul/templates/nsXULTreeBuilder.cpp b/dom/xul/templates/nsXULTreeBuilder.cpp new file mode 100644 index 000000000..b42133484 --- /dev/null +++ b/dom/xul/templates/nsXULTreeBuilder.cpp @@ -0,0 +1,1881 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nscore.h" +#include "nsError.h" +#include "nsIContent.h" +#include "mozilla/dom/NodeInfo.h" +#include "nsIDOMElement.h" +#include "nsIBoxObject.h" +#include "nsITreeBoxObject.h" +#include "nsITreeSelection.h" +#include "nsITreeColumns.h" +#include "nsITreeView.h" +#include "nsTreeUtils.h" +#include "nsIServiceManager.h" +#include "nsReadableUtils.h" +#include "nsQuickSort.h" +#include "nsTreeRows.h" +#include "nsTemplateRule.h" +#include "nsTemplateMatch.h" +#include "nsGkAtoms.h" +#include "nsXULContentUtils.h" +#include "nsXULTemplateBuilder.h" +#include "nsIXULSortService.h" +#include "nsTArray.h" +#include "nsUnicharUtils.h" +#include "nsNameSpaceManager.h" +#include "nsDOMClassInfoID.h" +#include "nsWhitespaceTokenizer.h" +#include "nsTreeContentView.h" +#include "nsIXULStore.h" +#include "mozilla/BinarySearch.h" + +// For security check +#include "nsIDocument.h" + +/** + * A XUL template builder that serves as an tree view, allowing + * (pretty much) arbitrary RDF to be presented in an tree. + */ +class nsXULTreeBuilder : public nsXULTemplateBuilder, + public nsIXULTreeBuilder, + public nsINativeTreeView +{ +public: + // nsISupports + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder) + + // nsIXULTreeBuilder + NS_DECL_NSIXULTREEBUILDER + + // nsITreeView + NS_DECL_NSITREEVIEW + // nsINativeTreeView: Untrusted code can use us + NS_IMETHOD EnsureNative() override { return NS_OK; } + + // nsIMutationObserver + NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED + +protected: + friend nsresult + NS_NewXULTreeBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult); + + friend struct ResultComparator; + + nsXULTreeBuilder(); + ~nsXULTreeBuilder(); + + /** + * Uninitialize the template builder + */ + virtual void Uninit(bool aIsFinal) override; + + /** + * Get sort variables from the active <treecol> + */ + nsresult + EnsureSortVariables(); + + virtual nsresult + RebuildAll() override; + + /** + * Given a row, use the row's match to figure out the appropriate + * <treerow> in the rule's <action>. + */ + nsresult + GetTemplateActionRowFor(int32_t aRow, nsIContent** aResult); + + /** + * Given a row and a column ID, use the row's match to figure out + * the appropriate <treecell> in the rule's <action>. + */ + nsresult + GetTemplateActionCellFor(int32_t aRow, nsITreeColumn* aCol, nsIContent** aResult); + + /** + * Return the resource corresponding to a row in the tree. + */ + nsresult + GetResourceFor(int32_t aRow, nsIRDFResource** aResource); + + /** + * Open a container row, inserting the container's children into + * the view. + */ + nsresult + OpenContainer(int32_t aIndex, nsIXULTemplateResult* aResult); + + /** + * Helper for OpenContainer, recursively open subtrees, remembering + * persisted ``open'' state + */ + nsresult + OpenSubtreeOf(nsTreeRows::Subtree* aSubtree, + int32_t aIndex, + nsIXULTemplateResult *aResult, + int32_t* aDelta); + + nsresult + OpenSubtreeForQuerySet(nsTreeRows::Subtree* aSubtree, + int32_t aIndex, + nsIXULTemplateResult *aResult, + nsTemplateQuerySet* aQuerySet, + int32_t* aDelta, + nsTArray<int32_t>& open); + + /** + * Close a container row, removing the container's childrem from + * the view. + */ + nsresult + CloseContainer(int32_t aIndex); + + /** + * Remove the matches for the rows in a subtree + */ + nsresult + RemoveMatchesFor(nsTreeRows::Subtree& subtree); + + /** + * Helper method that determines if the specified container is open. + */ + bool + IsContainerOpen(nsIXULTemplateResult* aResource); + + /** + * A sorting callback for NS_QuickSort(). + */ + static int + Compare(const void* aLeft, const void* aRight, void* aClosure); + + /** + * The real sort routine + */ + int32_t + CompareResults(nsIXULTemplateResult* aLeft, nsIXULTemplateResult* aRight); + + /** + * Sort the specified subtree, and recursively sort any subtrees + * beneath it. + */ + nsresult + SortSubtree(nsTreeRows::Subtree* aSubtree); + + NS_IMETHOD + HasGeneratedContent(nsIRDFResource* aResource, + nsIAtom* aTag, + bool* aGenerated) override; + + // GetInsertionLocations, ReplaceMatch and SynchronizeResult are inherited + // from nsXULTemplateBuilder + + /** + * Return true if the result can be inserted into the template as a new + * row. + */ + bool + GetInsertionLocations(nsIXULTemplateResult* aResult, + nsCOMArray<nsIContent>** aLocations) override; + + /** + * Implement result replacement + */ + virtual nsresult + ReplaceMatch(nsIXULTemplateResult* aOldResult, + nsTemplateMatch* aNewMatch, + nsTemplateRule* aNewMatchRule, + void* aContext) override; + + /** + * Implement match synchronization + */ + virtual nsresult + SynchronizeResult(nsIXULTemplateResult* aResult) override; + + /** + * The tree's box object, used to communicate with the front-end. + */ + nsCOMPtr<nsITreeBoxObject> mBoxObject; + + /** + * The tree's selection object. + */ + nsCOMPtr<nsITreeSelection> mSelection; + + /** + * The datasource that's used to persist open folder information + */ + nsCOMPtr<nsIRDFDataSource> mPersistStateStore; + + /** + * The rows in the view + */ + nsTreeRows mRows; + + /** + * The currently active sort variable + */ + nsCOMPtr<nsIAtom> mSortVariable; + + enum Direction { + eDirection_Descending = -1, + eDirection_Natural = 0, + eDirection_Ascending = +1 + }; + + /** + * The currently active sort order + */ + Direction mSortDirection; + + /* + * Sort hints (compare case, etc) + */ + uint32_t mSortHints; + + /** + * The builder observers. + */ + nsCOMArray<nsIXULTreeBuilderObserver> mObservers; + + /* + * XUL store for holding open container state + */ + nsCOMPtr<nsIXULStore> mLocalStore; +}; + +//---------------------------------------------------------------------- + +nsresult +NS_NewXULTreeBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult) +{ + *aResult = nullptr; + + NS_PRECONDITION(aOuter == nullptr, "no aggregation"); + if (aOuter) + return NS_ERROR_NO_AGGREGATION; + + nsresult rv; + nsXULTreeBuilder* result = new nsXULTreeBuilder(); + NS_ADDREF(result); // stabilize + + rv = result->InitGlobals(); + + if (NS_SUCCEEDED(rv)) + rv = result->QueryInterface(aIID, aResult); + + NS_RELEASE(result); + return rv; +} + +NS_IMPL_ADDREF_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder) +NS_IMPL_RELEASE_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder) + +NS_IMPL_CYCLE_COLLECTION_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder, + mBoxObject, + mSelection, + mPersistStateStore, + mLocalStore, + mObservers) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsXULTreeBuilder) + NS_INTERFACE_MAP_ENTRY(nsIXULTreeBuilder) + NS_INTERFACE_MAP_ENTRY(nsITreeView) + NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(XULTreeBuilder) +NS_INTERFACE_MAP_END_INHERITING(nsXULTemplateBuilder) + + +nsXULTreeBuilder::nsXULTreeBuilder() + : mSortDirection(eDirection_Natural), mSortHints(0) +{ +} + +nsXULTreeBuilder::~nsXULTreeBuilder() +{ +} + +void +nsXULTreeBuilder::Uninit(bool aIsFinal) +{ + int32_t count = mRows.Count(); + mRows.Clear(); + + if (mBoxObject) { + mBoxObject->BeginUpdateBatch(); + mBoxObject->RowCountChanged(0, -count); + if (mBoxObject) { + mBoxObject->EndUpdateBatch(); + } + } + + nsXULTemplateBuilder::Uninit(aIsFinal); +} + + +//---------------------------------------------------------------------- +// +// nsIXULTreeBuilder methods +// + +NS_IMETHODIMP +nsXULTreeBuilder::GetResourceAtIndex(int32_t aRowIndex, nsIRDFResource** aResult) +{ + if (aRowIndex < 0 || aRowIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + return GetResourceFor(aRowIndex, aResult); +} + +NS_IMETHODIMP +nsXULTreeBuilder::GetIndexOfResource(nsIRDFResource* aResource, int32_t* aResult) +{ + NS_ENSURE_ARG_POINTER(aResource); + nsTreeRows::iterator iter = mRows.FindByResource(aResource); + if (iter == mRows.Last()) + *aResult = -1; + else + *aResult = iter.GetRowIndex(); + return NS_OK; +} + +NS_IMETHODIMP +nsXULTreeBuilder::AddObserver(nsIXULTreeBuilderObserver* aObserver) +{ + return mObservers.AppendObject(aObserver) ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsXULTreeBuilder::RemoveObserver(nsIXULTreeBuilderObserver* aObserver) +{ + return mObservers.RemoveObject(aObserver) ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsXULTreeBuilder::Sort(nsIDOMElement* aElement) +{ + nsCOMPtr<nsIContent> header = do_QueryInterface(aElement); + if (! header) + return NS_ERROR_FAILURE; + + if (header->AttrValueIs(kNameSpaceID_None, nsGkAtoms::sortLocked, + nsGkAtoms::_true, eCaseMatters)) + return NS_OK; + + nsAutoString sort; + header->GetAttr(kNameSpaceID_None, nsGkAtoms::sort, sort); + + if (sort.IsEmpty()) + return NS_OK; + + // Grab the new sort variable + mSortVariable = NS_Atomize(sort); + + nsAutoString hints; + header->GetAttr(kNameSpaceID_None, nsGkAtoms::sorthints, hints); + + bool hasNaturalState = true; + nsWhitespaceTokenizer tokenizer(hints); + while (tokenizer.hasMoreTokens()) { + const nsDependentSubstring& token(tokenizer.nextToken()); + if (token.EqualsLiteral("comparecase")) + mSortHints |= nsIXULSortService::SORT_COMPARECASE; + else if (token.EqualsLiteral("integer")) + mSortHints |= nsIXULSortService::SORT_INTEGER; + else if (token.EqualsLiteral("twostate")) + hasNaturalState = false; + } + + // Cycle the sort direction + nsAutoString dir; + header->GetAttr(kNameSpaceID_None, nsGkAtoms::sortDirection, dir); + + if (dir.EqualsLiteral("ascending")) { + dir.AssignLiteral("descending"); + mSortDirection = eDirection_Descending; + } + else if (hasNaturalState && dir.EqualsLiteral("descending")) { + dir.AssignLiteral("natural"); + mSortDirection = eDirection_Natural; + } + else { + dir.AssignLiteral("ascending"); + mSortDirection = eDirection_Ascending; + } + + // Sort it. + SortSubtree(mRows.GetRoot()); + mRows.InvalidateCachedRow(); + if (mBoxObject) + mBoxObject->Invalidate(); + + nsTreeUtils::UpdateSortIndicators(header, dir); + + return NS_OK; +} + +//---------------------------------------------------------------------- +// +// nsITreeView methods +// + +NS_IMETHODIMP +nsXULTreeBuilder::GetRowCount(int32_t* aRowCount) +{ + *aRowCount = mRows.Count(); + return NS_OK; +} + +NS_IMETHODIMP +nsXULTreeBuilder::GetSelection(nsITreeSelection** aSelection) +{ + NS_IF_ADDREF(*aSelection = mSelection.get()); + return NS_OK; +} + +NS_IMETHODIMP +nsXULTreeBuilder::SetSelection(nsITreeSelection* aSelection) +{ + NS_ENSURE_TRUE(!aSelection || + nsTreeContentView::CanTrustTreeSelection(aSelection), + NS_ERROR_DOM_SECURITY_ERR); + mSelection = aSelection; + return NS_OK; +} + +NS_IMETHODIMP +nsXULTreeBuilder::GetRowProperties(int32_t aIndex, nsAString& aProps) +{ + NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row"); + if (aIndex < 0 || aIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + nsCOMPtr<nsIContent> row; + GetTemplateActionRowFor(aIndex, getter_AddRefs(row)); + if (row) { + nsAutoString raw; + row->GetAttr(kNameSpaceID_None, nsGkAtoms::properties, raw); + + if (!raw.IsEmpty()) { + SubstituteText(mRows[aIndex]->mMatch->mResult, raw, aProps); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTreeBuilder::GetCellProperties(int32_t aRow, nsITreeColumn* aCol, + nsAString& aProps) +{ + NS_ENSURE_ARG_POINTER(aCol); + NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad row"); + if (aRow < 0 || aRow >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + nsCOMPtr<nsIContent> cell; + GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell)); + if (cell) { + nsAutoString raw; + cell->GetAttr(kNameSpaceID_None, nsGkAtoms::properties, raw); + + if (!raw.IsEmpty()) { + SubstituteText(mRows[aRow]->mMatch->mResult, raw, aProps); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTreeBuilder::GetColumnProperties(nsITreeColumn* aCol, nsAString& aProps) +{ + NS_ENSURE_ARG_POINTER(aCol); + // XXX sortactive fu + return NS_OK; +} + +NS_IMETHODIMP +nsXULTreeBuilder::IsContainer(int32_t aIndex, bool* aResult) +{ + NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row"); + if (aIndex < 0 || aIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + nsTreeRows::iterator iter = mRows[aIndex]; + + bool isContainer; + iter->mMatch->mResult->GetIsContainer(&isContainer); + + iter->mContainerType = isContainer + ? nsTreeRows::eContainerType_Container + : nsTreeRows::eContainerType_Noncontainer; + + *aResult = (iter->mContainerType == nsTreeRows::eContainerType_Container); + return NS_OK; +} + +NS_IMETHODIMP +nsXULTreeBuilder::IsContainerOpen(int32_t aIndex, bool* aOpen) +{ + NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row"); + if (aIndex < 0 || aIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + nsTreeRows::iterator iter = mRows[aIndex]; + + if (iter->mContainerState == nsTreeRows::eContainerState_Unknown) { + bool isOpen = IsContainerOpen(iter->mMatch->mResult); + + iter->mContainerState = isOpen + ? nsTreeRows::eContainerState_Open + : nsTreeRows::eContainerState_Closed; + } + + *aOpen = (iter->mContainerState == nsTreeRows::eContainerState_Open); + return NS_OK; +} + +NS_IMETHODIMP +nsXULTreeBuilder::IsContainerEmpty(int32_t aIndex, bool* aResult) +{ + NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row"); + if (aIndex < 0 || aIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + nsTreeRows::iterator iter = mRows[aIndex]; + NS_ASSERTION(iter->mContainerType == nsTreeRows::eContainerType_Container, + "asking for empty state on non-container"); + + // if recursion is disabled, pretend that the container is empty. This + // ensures that folders are still displayed as such, yet won't display + // their children + if ((mFlags & eDontRecurse) && (iter->mMatch->mResult != mRootResult)) { + *aResult = true; + return NS_OK; + } + + if (iter->mContainerFill == nsTreeRows::eContainerFill_Unknown) { + bool isEmpty; + iter->mMatch->mResult->GetIsEmpty(&isEmpty); + + iter->mContainerFill = isEmpty + ? nsTreeRows::eContainerFill_Empty + : nsTreeRows::eContainerFill_Nonempty; + } + + *aResult = (iter->mContainerFill == nsTreeRows::eContainerFill_Empty); + return NS_OK; +} + +NS_IMETHODIMP +nsXULTreeBuilder::IsSeparator(int32_t aIndex, bool* aResult) +{ + NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row"); + if (aIndex < 0 || aIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + nsAutoString type; + nsTreeRows::Row& row = *(mRows[aIndex]); + row.mMatch->mResult->GetType(type); + + *aResult = type.EqualsLiteral("separator"); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTreeBuilder::GetParentIndex(int32_t aRowIndex, int32_t* aResult) +{ + NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < mRows.Count(), "bad row"); + if (aRowIndex < 0 || aRowIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + // Construct a path to the row + nsTreeRows::iterator iter = mRows[aRowIndex]; + + // The parent of the row will be at the top of the path + nsTreeRows::Subtree* parent = iter.GetParent(); + + // Now walk through our previous siblings, subtracting off each + // one's subtree size + int32_t index = iter.GetChildIndex(); + while (--index >= 0) + aRowIndex -= mRows.GetSubtreeSizeFor(parent, index) + 1; + + // Now the parent's index will be the first row's index, less one. + *aResult = aRowIndex - 1; + return NS_OK; +} + +NS_IMETHODIMP +nsXULTreeBuilder::HasNextSibling(int32_t aRowIndex, int32_t aAfterIndex, bool* aResult) +{ + NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < mRows.Count(), "bad row"); + if (aRowIndex < 0 || aRowIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + // Construct a path to the row + nsTreeRows::iterator iter = mRows[aRowIndex]; + + // The parent of the row will be at the top of the path + nsTreeRows::Subtree* parent = iter.GetParent(); + + // We have a next sibling if the child is not the last in the + // subtree. + *aResult = iter.GetChildIndex() != parent->Count() - 1; + return NS_OK; +} + +NS_IMETHODIMP +nsXULTreeBuilder::GetLevel(int32_t aRowIndex, int32_t* aResult) +{ + NS_PRECONDITION(aRowIndex >= 0 && aRowIndex < mRows.Count(), "bad row"); + if (aRowIndex < 0 || aRowIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + // Construct a path to the row; the ``level'' is the path length + // less one. + nsTreeRows::iterator iter = mRows[aRowIndex]; + *aResult = iter.GetDepth() - 1; + return NS_OK; +} + +NS_IMETHODIMP +nsXULTreeBuilder::GetImageSrc(int32_t aRow, nsITreeColumn* aCol, nsAString& aResult) +{ + NS_ENSURE_ARG_POINTER(aCol); + NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index"); + if (aRow < 0 || aRow >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + // Find the <cell> that corresponds to the column we want. + nsCOMPtr<nsIContent> cell; + GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell)); + if (cell) { + nsAutoString raw; + cell->GetAttr(kNameSpaceID_None, nsGkAtoms::src, raw); + + SubstituteText(mRows[aRow]->mMatch->mResult, raw, aResult); + } + else + aResult.Truncate(); + + return NS_OK; +} + + +NS_IMETHODIMP +nsXULTreeBuilder::GetProgressMode(int32_t aRow, nsITreeColumn* aCol, int32_t* aResult) +{ + NS_ENSURE_ARG_POINTER(aCol); + NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index"); + if (aRow < 0 || aRow >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + *aResult = nsITreeView::PROGRESS_NONE; + + // Find the <cell> that corresponds to the column we want. + nsCOMPtr<nsIContent> cell; + GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell)); + if (cell) { + nsAutoString raw; + cell->GetAttr(kNameSpaceID_None, nsGkAtoms::mode, raw); + + nsAutoString mode; + SubstituteText(mRows[aRow]->mMatch->mResult, raw, mode); + + if (mode.EqualsLiteral("normal")) + *aResult = nsITreeView::PROGRESS_NORMAL; + else if (mode.EqualsLiteral("undetermined")) + *aResult = nsITreeView::PROGRESS_UNDETERMINED; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTreeBuilder::GetCellValue(int32_t aRow, nsITreeColumn* aCol, nsAString& aResult) +{ + NS_ENSURE_ARG_POINTER(aCol); + NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index"); + if (aRow < 0 || aRow >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + // Find the <cell> that corresponds to the column we want. + nsCOMPtr<nsIContent> cell; + GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell)); + if (cell) { + nsAutoString raw; + cell->GetAttr(kNameSpaceID_None, nsGkAtoms::value, raw); + + SubstituteText(mRows[aRow]->mMatch->mResult, raw, aResult); + } + else + aResult.Truncate(); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTreeBuilder::GetCellText(int32_t aRow, nsITreeColumn* aCol, nsAString& aResult) +{ + NS_ENSURE_ARG_POINTER(aCol); + NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index"); + if (aRow < 0 || aRow >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + // Find the <cell> that corresponds to the column we want. + nsCOMPtr<nsIContent> cell; + GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell)); + if (cell) { + nsAutoString raw; + cell->GetAttr(kNameSpaceID_None, nsGkAtoms::label, raw); + + SubstituteText(mRows[aRow]->mMatch->mResult, raw, aResult); + + } + else + aResult.Truncate(); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTreeBuilder::SetTree(nsITreeBoxObject* aTree) +{ + mBoxObject = aTree; + + // If this is teardown time, then we're done. + if (!mBoxObject) { + Uninit(false); + return NS_OK; + } + NS_ENSURE_TRUE(mRoot, NS_ERROR_NOT_INITIALIZED); + + // Only use the XUL store if the root's principal is trusted. + bool isTrusted = false; + nsresult rv = IsSystemPrincipal(mRoot->NodePrincipal(), &isTrusted); + if (NS_SUCCEEDED(rv) && isTrusted) { + mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1"); + if(NS_WARN_IF(!mLocalStore)){ + return NS_ERROR_NOT_INITIALIZED; + } + } + + Rebuild(); + + EnsureSortVariables(); + if (mSortVariable) + SortSubtree(mRows.GetRoot()); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTreeBuilder::ToggleOpenState(int32_t aIndex) +{ + if (aIndex < 0 || aIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + nsIXULTemplateResult* result = mRows[aIndex]->mMatch->mResult; + if (! result) + return NS_ERROR_FAILURE; + + if (mFlags & eDontRecurse) + return NS_OK; + + if (result && result != mRootResult) { + // don't open containers if child processing isn't allowed + bool mayProcessChildren; + nsresult rv = result->GetMayProcessChildren(&mayProcessChildren); + if (NS_FAILED(rv) || !mayProcessChildren) + return rv; + } + + uint32_t count = mObservers.Count(); + for (uint32_t i = 0; i < count; ++i) { + nsCOMPtr<nsIXULTreeBuilderObserver> observer = mObservers.SafeObjectAt(i); + if (observer) + observer->OnToggleOpenState(aIndex); + } + + if (mLocalStore && mRoot) { + bool isOpen; + IsContainerOpen(aIndex, &isOpen); + + nsIDocument* doc = mRoot->GetComposedDoc(); + if (!doc) { + return NS_ERROR_FAILURE; + } + + nsIURI* docURI = doc->GetDocumentURI(); + nsTreeRows::Row& row = *(mRows[aIndex]); + nsAutoString nodeid; + nsresult rv = row.mMatch->mResult->GetId(nodeid); + if (NS_FAILED(rv)) { + return rv; + } + + nsAutoCString utf8uri; + rv = docURI->GetSpec(utf8uri); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + NS_ConvertUTF8toUTF16 uri(utf8uri); + + if (isOpen) { + mLocalStore->RemoveValue(uri, nodeid, NS_LITERAL_STRING("open")); + CloseContainer(aIndex); + } else { + mLocalStore->SetValue(uri, nodeid, NS_LITERAL_STRING("open"), + NS_LITERAL_STRING("true")); + + OpenContainer(aIndex, result); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTreeBuilder::CycleHeader(nsITreeColumn* aCol) +{ + NS_ENSURE_ARG_POINTER(aCol); + nsCOMPtr<nsIDOMElement> element; + aCol->GetElement(getter_AddRefs(element)); + + nsAutoString id; + aCol->GetId(id); + + uint32_t count = mObservers.Count(); + for (uint32_t i = 0; i < count; ++i) { + nsCOMPtr<nsIXULTreeBuilderObserver> observer = mObservers.SafeObjectAt(i); + if (observer) + observer->OnCycleHeader(id.get(), element); + } + + return Sort(element); +} + +NS_IMETHODIMP +nsXULTreeBuilder::SelectionChanged() +{ + uint32_t count = mObservers.Count(); + for (uint32_t i = 0; i < count; ++i) { + nsCOMPtr<nsIXULTreeBuilderObserver> observer = mObservers.SafeObjectAt(i); + if (observer) + observer->OnSelectionChanged(); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTreeBuilder::CycleCell(int32_t aRow, nsITreeColumn* aCol) +{ + NS_ENSURE_ARG_POINTER(aCol); + + nsAutoString id; + aCol->GetId(id); + + uint32_t count = mObservers.Count(); + for (uint32_t i = 0; i < count; ++i) { + nsCOMPtr<nsIXULTreeBuilderObserver> observer = mObservers.SafeObjectAt(i); + if (observer) + observer->OnCycleCell(aRow, id.get()); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTreeBuilder::IsEditable(int32_t aRow, nsITreeColumn* aCol, bool* _retval) +{ + *_retval = true; + NS_ENSURE_ARG_POINTER(aCol); + NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index"); + if (aRow < 0 || aRow >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + // Find the <cell> that corresponds to the column we want. + nsCOMPtr<nsIContent> cell; + GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell)); + if (cell) { + nsAutoString raw; + cell->GetAttr(kNameSpaceID_None, nsGkAtoms::editable, raw); + + nsAutoString editable; + SubstituteText(mRows[aRow]->mMatch->mResult, raw, editable); + + if (editable.EqualsLiteral("false")) + *_retval = false; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTreeBuilder::IsSelectable(int32_t aRow, nsITreeColumn* aCol, bool* _retval) +{ + NS_PRECONDITION(aRow >= 0 && aRow < mRows.Count(), "bad index"); + if (aRow < 0 || aRow >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + *_retval = true; + + // Find the <cell> that corresponds to the column we want. + nsCOMPtr<nsIContent> cell; + GetTemplateActionCellFor(aRow, aCol, getter_AddRefs(cell)); + if (cell) { + nsAutoString raw; + cell->GetAttr(kNameSpaceID_None, nsGkAtoms::selectable, raw); + + nsAutoString selectable; + SubstituteText(mRows[aRow]->mMatch->mResult, raw, selectable); + + if (selectable.EqualsLiteral("false")) + *_retval = false; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTreeBuilder::SetCellValue(int32_t aRow, nsITreeColumn* aCol, const nsAString& aValue) +{ + NS_ENSURE_ARG_POINTER(aCol); + return NS_OK; +} + +NS_IMETHODIMP +nsXULTreeBuilder::SetCellText(int32_t aRow, nsITreeColumn* aCol, const nsAString& aValue) +{ + NS_ENSURE_ARG_POINTER(aCol); + return NS_OK; +} + +NS_IMETHODIMP +nsXULTreeBuilder::PerformAction(const char16_t* aAction) +{ + uint32_t count = mObservers.Count(); + for (uint32_t i = 0; i < count; ++i) { + nsCOMPtr<nsIXULTreeBuilderObserver> observer = mObservers.SafeObjectAt(i); + if (observer) + observer->OnPerformAction(aAction); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTreeBuilder::PerformActionOnRow(const char16_t* aAction, int32_t aRow) +{ + uint32_t count = mObservers.Count(); + for (uint32_t i = 0; i < count; ++i) { + nsCOMPtr<nsIXULTreeBuilderObserver> observer = mObservers.SafeObjectAt(i); + if (observer) + observer->OnPerformActionOnRow(aAction, aRow); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTreeBuilder::PerformActionOnCell(const char16_t* aAction, int32_t aRow, nsITreeColumn* aCol) +{ + NS_ENSURE_ARG_POINTER(aCol); + nsAutoString id; + aCol->GetId(id); + + uint32_t count = mObservers.Count(); + for (uint32_t i = 0; i < count; ++i) { + nsCOMPtr<nsIXULTreeBuilderObserver> observer = mObservers.SafeObjectAt(i); + if (observer) + observer->OnPerformActionOnCell(aAction, aRow, id.get()); + } + + return NS_OK; +} + + +void +nsXULTreeBuilder::NodeWillBeDestroyed(const nsINode* aNode) +{ + nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this); + mObservers.Clear(); + + nsXULTemplateBuilder::NodeWillBeDestroyed(aNode); +} + +NS_IMETHODIMP +nsXULTreeBuilder::HasGeneratedContent(nsIRDFResource* aResource, + nsIAtom* aTag, + bool* aGenerated) +{ + *aGenerated = false; + NS_ENSURE_ARG_POINTER(aResource); + + if (!mRootResult) + return NS_OK; + + nsCOMPtr<nsIRDFResource> rootresource; + nsresult rv = mRootResult->GetResource(getter_AddRefs(rootresource)); + if (NS_FAILED(rv)) + return rv; + + if (aResource == rootresource || + mRows.FindByResource(aResource) != mRows.Last()) + *aGenerated = true; + + return NS_OK; +} + +bool +nsXULTreeBuilder::GetInsertionLocations(nsIXULTemplateResult* aResult, + nsCOMArray<nsIContent>** aLocations) +{ + *aLocations = nullptr; + + // Get the reference point and check if it is an open container. Rows + // should not be generated otherwise. + + nsAutoString ref; + nsresult rv = aResult->GetBindingFor(mRefVariable, ref); + if (NS_FAILED(rv) || ref.IsEmpty()) + return false; + + nsCOMPtr<nsIRDFResource> container; + rv = gRDFService->GetUnicodeResource(ref, getter_AddRefs(container)); + if (NS_FAILED(rv)) + return false; + + // Can always insert into the root resource + if (container == mRows.GetRootResource()) + return true; + + nsTreeRows::iterator iter = mRows.FindByResource(container); + if (iter == mRows.Last()) + return false; + + return (iter->mContainerState == nsTreeRows::eContainerState_Open); +} + +struct ResultComparator +{ + nsXULTreeBuilder* const mTreebuilder; + nsIXULTemplateResult* const mResult; + ResultComparator(nsXULTreeBuilder* aTreebuilder, nsIXULTemplateResult* aResult) + : mTreebuilder(aTreebuilder), mResult(aResult) {} + int operator()(const nsTreeRows::Row& aSubtree) const { + return mTreebuilder->CompareResults(mResult, aSubtree.mMatch->mResult); + } +}; + +nsresult +nsXULTreeBuilder::ReplaceMatch(nsIXULTemplateResult* aOldResult, + nsTemplateMatch* aNewMatch, + nsTemplateRule* aNewMatchRule, + void *aLocation) +{ + if (! mBoxObject) + return NS_OK; + + if (aOldResult) { + // Grovel through the rows looking for oldresult. + nsTreeRows::iterator iter = mRows.Find(aOldResult); + + NS_ASSERTION(iter != mRows.Last(), "couldn't find row"); + if (iter == mRows.Last()) + return NS_ERROR_FAILURE; + + // Remove the rows from the view + int32_t row = iter.GetRowIndex(); + + // If the row contains children, remove the matches from the + // children so that they can be regenerated again if the element + // gets added back. + int32_t delta = mRows.GetSubtreeSizeFor(iter); + if (delta) + RemoveMatchesFor(*(iter->mSubtree)); + + if (mRows.RemoveRowAt(iter) == 0 && iter.GetRowIndex() >= 0) { + + // In this case iter now points to its parent + // Invalidate the row's cached fill state + iter->mContainerFill = nsTreeRows::eContainerFill_Unknown; + + nsCOMPtr<nsITreeColumns> cols; + mBoxObject->GetColumns(getter_AddRefs(cols)); + if (cols) { + nsCOMPtr<nsITreeColumn> primaryCol; + cols->GetPrimaryColumn(getter_AddRefs(primaryCol)); + if (primaryCol) + mBoxObject->InvalidateCell(iter.GetRowIndex(), primaryCol); + } + } + + // Notify the box object + mBoxObject->RowCountChanged(row, -delta - 1); + } + + if (aNewMatch && aNewMatch->mResult) { + // Insertion. + int32_t row = -1; + nsTreeRows::Subtree* parent = nullptr; + nsIXULTemplateResult* result = aNewMatch->mResult; + + nsAutoString ref; + nsresult rv = result->GetBindingFor(mRefVariable, ref); + if (NS_FAILED(rv) || ref.IsEmpty()) + return rv; + + nsCOMPtr<nsIRDFResource> container; + rv = gRDFService->GetUnicodeResource(ref, getter_AddRefs(container)); + if (NS_FAILED(rv)) + return rv; + + if (container != mRows.GetRootResource()) { + nsTreeRows::iterator iter = mRows.FindByResource(container); + row = iter.GetRowIndex(); + + NS_ASSERTION(iter != mRows.Last(), "couldn't find container row"); + if (iter == mRows.Last()) + return NS_ERROR_FAILURE; + + // Use the persist store to remember if the container + // is open or closed. + bool open = false; + IsContainerOpen(row, &open); + + // If it's open, make sure that we've got a subtree structure ready. + if (open) + parent = mRows.EnsureSubtreeFor(iter); + + // We know something has just been inserted into the + // container, so whether its open or closed, make sure + // that we've got our tree row's state correct. + if ((iter->mContainerType != nsTreeRows::eContainerType_Container) || + (iter->mContainerFill != nsTreeRows::eContainerFill_Nonempty)) { + iter->mContainerType = nsTreeRows::eContainerType_Container; + iter->mContainerFill = nsTreeRows::eContainerFill_Nonempty; + mBoxObject->InvalidateRow(iter.GetRowIndex()); + } + } + else { + parent = mRows.GetRoot(); + } + + if (parent) { + // If we get here, then we're inserting into an open + // container. By default, place the new element at the + // end of the container + size_t index = parent->Count(); + + if (mSortVariable) { + // Figure out where to put the new element through + // binary search. + mozilla::BinarySearchIf(*parent, 0, parent->Count(), + ResultComparator(this, result), &index); + } + + nsTreeRows::iterator iter = + mRows.InsertRowAt(aNewMatch, parent, index); + + mBoxObject->RowCountChanged(iter.GetRowIndex(), +1); + + // See if this newly added row is open; in which case, + // recursively add its children to the tree, too. + + if (mFlags & eDontRecurse) + return NS_OK; + + if (result != mRootResult) { + // don't open containers if child processing isn't allowed + bool mayProcessChildren; + nsresult rv = result->GetMayProcessChildren(&mayProcessChildren); + if (NS_FAILED(rv) || ! mayProcessChildren) return NS_OK; + } + + if (IsContainerOpen(result)) { + OpenContainer(iter.GetRowIndex(), result); + } + } + } + + return NS_OK; +} + +nsresult +nsXULTreeBuilder::SynchronizeResult(nsIXULTemplateResult* aResult) +{ + if (mBoxObject) { + // XXX we could be more conservative and just invalidate the cells + // that got whacked... + + nsTreeRows::iterator iter = mRows.Find(aResult); + + NS_ASSERTION(iter != mRows.Last(), "couldn't find row"); + if (iter == mRows.Last()) + return NS_ERROR_FAILURE; + + int32_t row = iter.GetRowIndex(); + if (row >= 0) + mBoxObject->InvalidateRow(row); + + MOZ_LOG(gXULTemplateLog, LogLevel::Debug, + ("xultemplate[%p] => row %d", this, row)); + } + + return NS_OK; +} + +//---------------------------------------------------------------------- + +nsresult +nsXULTreeBuilder::EnsureSortVariables() +{ + // Grovel through <treecols> kids to find the <treecol> + // with the sort attributes. + nsCOMPtr<nsIContent> treecols; + + nsXULContentUtils::FindChildByTag(mRoot, kNameSpaceID_XUL, + nsGkAtoms::treecols, + getter_AddRefs(treecols)); + + if (!treecols) + return NS_OK; + + for (nsIContent* child = treecols->GetFirstChild(); + child; + child = child->GetNextSibling()) { + + if (child->NodeInfo()->Equals(nsGkAtoms::treecol, + kNameSpaceID_XUL)) { + if (child->AttrValueIs(kNameSpaceID_None, nsGkAtoms::sortActive, + nsGkAtoms::_true, eCaseMatters)) { + nsAutoString sort; + child->GetAttr(kNameSpaceID_None, nsGkAtoms::sort, sort); + if (! sort.IsEmpty()) { + mSortVariable = NS_Atomize(sort); + + static nsIContent::AttrValuesArray strings[] = + {&nsGkAtoms::ascending, &nsGkAtoms::descending, nullptr}; + switch (child->FindAttrValueIn(kNameSpaceID_None, + nsGkAtoms::sortDirection, + strings, eCaseMatters)) { + case 0: mSortDirection = eDirection_Ascending; break; + case 1: mSortDirection = eDirection_Descending; break; + default: mSortDirection = eDirection_Natural; break; + } + } + break; + } + } + } + + return NS_OK; +} + +nsresult +nsXULTreeBuilder::RebuildAll() +{ + NS_ENSURE_TRUE(mRoot, NS_ERROR_NOT_INITIALIZED); + + nsCOMPtr<nsIDocument> doc = mRoot->GetComposedDoc(); + + // Bail out early if we are being torn down. + if (!doc) + return NS_OK; + + if (! mQueryProcessor) + return NS_OK; + + if (mBoxObject) { + mBoxObject->BeginUpdateBatch(); + } + + if (mQueriesCompiled) { + Uninit(false); + } + else if (mBoxObject) { + int32_t count = mRows.Count(); + mRows.Clear(); + mBoxObject->RowCountChanged(0, -count); + } + + nsresult rv = CompileQueries(); + if (NS_SUCCEEDED(rv) && mQuerySets.Length() > 0) { + // Seed the rule network with assignments for the tree row variable + nsAutoString ref; + mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::ref, ref); + if (!ref.IsEmpty()) { + rv = mQueryProcessor->TranslateRef(mDataSource, ref, + getter_AddRefs(mRootResult)); + if (NS_SUCCEEDED(rv) && mRootResult) { + OpenContainer(-1, mRootResult); + + nsCOMPtr<nsIRDFResource> rootResource; + GetResultResource(mRootResult, getter_AddRefs(rootResource)); + + mRows.SetRootResource(rootResource); + } + } + } + + if (mBoxObject) { + mBoxObject->EndUpdateBatch(); + } + + return rv; +} + +nsresult +nsXULTreeBuilder::GetTemplateActionRowFor(int32_t aRow, nsIContent** aResult) +{ + // Get the template in the DOM from which we're supposed to + // generate text + nsTreeRows::Row& row = *(mRows[aRow]); + + // The match stores the indices of the rule and query to use. Use these + // to look up the right nsTemplateRule and use that rule's action to get + // the treerow in the template. + int16_t ruleindex = row.mMatch->RuleIndex(); + if (ruleindex >= 0) { + nsTemplateQuerySet* qs = mQuerySets[row.mMatch->QuerySetPriority()]; + nsTemplateRule* rule = qs->GetRuleAt(ruleindex); + if (rule) { + nsCOMPtr<nsIContent> children; + nsXULContentUtils::FindChildByTag(rule->GetAction(), kNameSpaceID_XUL, + nsGkAtoms::treechildren, + getter_AddRefs(children)); + if (children) { + nsCOMPtr<nsIContent> item; + nsXULContentUtils::FindChildByTag(children, kNameSpaceID_XUL, + nsGkAtoms::treeitem, + getter_AddRefs(item)); + if (item) + return nsXULContentUtils::FindChildByTag(item, + kNameSpaceID_XUL, + nsGkAtoms::treerow, + aResult); + } + } + } + + *aResult = nullptr; + return NS_OK; +} + +nsresult +nsXULTreeBuilder::GetTemplateActionCellFor(int32_t aRow, + nsITreeColumn* aCol, + nsIContent** aResult) +{ + *aResult = nullptr; + + if (!aCol) return NS_ERROR_INVALID_ARG; + + nsCOMPtr<nsIContent> row; + GetTemplateActionRowFor(aRow, getter_AddRefs(row)); + if (row) { + nsCOMPtr<nsIAtom> colAtom; + int32_t colIndex; + aCol->GetAtom(getter_AddRefs(colAtom)); + aCol->GetIndex(&colIndex); + + uint32_t j = 0; + for (nsIContent* child = row->GetFirstChild(); + child; + child = child->GetNextSibling()) { + + if (child->NodeInfo()->Equals(nsGkAtoms::treecell, + kNameSpaceID_XUL)) { + if (colAtom && + child->AttrValueIs(kNameSpaceID_None, nsGkAtoms::ref, + colAtom, eCaseMatters)) { + *aResult = child; + break; + } + else if (j == (uint32_t)colIndex) + *aResult = child; + j++; + } + } + } + NS_IF_ADDREF(*aResult); + + return NS_OK; +} + +nsresult +nsXULTreeBuilder::GetResourceFor(int32_t aRow, nsIRDFResource** aResource) +{ + nsTreeRows::Row& row = *(mRows[aRow]); + return GetResultResource(row.mMatch->mResult, aResource); +} + +nsresult +nsXULTreeBuilder::OpenContainer(int32_t aIndex, nsIXULTemplateResult* aResult) +{ + // A row index of -1 in this case means ``open tree body'' + NS_ASSERTION(aIndex >= -1 && aIndex < mRows.Count(), "bad row"); + if (aIndex < -1 || aIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + nsTreeRows::Subtree* container; + + if (aIndex >= 0) { + nsTreeRows::iterator iter = mRows[aIndex]; + container = mRows.EnsureSubtreeFor(iter.GetParent(), + iter.GetChildIndex()); + + iter->mContainerState = nsTreeRows::eContainerState_Open; + } + else + container = mRows.GetRoot(); + + if (! container) + return NS_ERROR_OUT_OF_MEMORY; + + int32_t count; + OpenSubtreeOf(container, aIndex, aResult, &count); + + // Notify the box object + if (mBoxObject) { + if (aIndex >= 0) + mBoxObject->InvalidateRow(aIndex); + + if (count) + mBoxObject->RowCountChanged(aIndex + 1, count); + } + + return NS_OK; +} + +nsresult +nsXULTreeBuilder::OpenSubtreeOf(nsTreeRows::Subtree* aSubtree, + int32_t aIndex, + nsIXULTemplateResult *aResult, + int32_t* aDelta) +{ + AutoTArray<int32_t, 8> open; + int32_t count = 0; + + int32_t rulecount = mQuerySets.Length(); + + for (int32_t r = 0; r < rulecount; r++) { + nsTemplateQuerySet* queryset = mQuerySets[r]; + OpenSubtreeForQuerySet(aSubtree, aIndex, aResult, queryset, &count, open); + } + + // Now recursively deal with any open sub-containers that just got + // inserted. We need to do this back-to-front to avoid skewing offsets. + for (int32_t i = open.Length() - 1; i >= 0; --i) { + int32_t index = open[i]; + + nsTreeRows::Subtree* child = + mRows.EnsureSubtreeFor(aSubtree, index); + + nsIXULTemplateResult* result = (*aSubtree)[index].mMatch->mResult; + + int32_t delta; + OpenSubtreeOf(child, aIndex + index, result, &delta); + count += delta; + } + + // Sort the container. + if (mSortVariable) { + NS_QuickSort(mRows.GetRowsFor(aSubtree), + aSubtree->Count(), + sizeof(nsTreeRows::Row), + Compare, + this); + } + + *aDelta = count; + return NS_OK; +} + +nsresult +nsXULTreeBuilder::OpenSubtreeForQuerySet(nsTreeRows::Subtree* aSubtree, + int32_t aIndex, + nsIXULTemplateResult* aResult, + nsTemplateQuerySet* aQuerySet, + int32_t* aDelta, + nsTArray<int32_t>& open) +{ + int32_t count = *aDelta; + + nsCOMPtr<nsISimpleEnumerator> results; + nsresult rv = mQueryProcessor->GenerateResults(mDataSource, aResult, + aQuerySet->mCompiledQuery, + getter_AddRefs(results)); + if (NS_FAILED(rv)) + return rv; + + bool hasMoreResults; + rv = results->HasMoreElements(&hasMoreResults); + + for (; NS_SUCCEEDED(rv) && hasMoreResults; + rv = results->HasMoreElements(&hasMoreResults)) { + nsCOMPtr<nsISupports> nr; + rv = results->GetNext(getter_AddRefs(nr)); + if (NS_FAILED(rv)) + return rv; + + nsCOMPtr<nsIXULTemplateResult> nextresult = do_QueryInterface(nr); + if (!nextresult) + return NS_ERROR_UNEXPECTED; + + nsCOMPtr<nsIRDFResource> resultid; + rv = GetResultResource(nextresult, getter_AddRefs(resultid)); + if (NS_FAILED(rv)) + return rv; + + if (! resultid) + continue; + + // check if there is already an existing match. If so, a previous + // query already generated content so the match is just added to the + // end of the set of matches. + + bool generateContent = true; + + nsTemplateMatch* prevmatch = nullptr; + nsTemplateMatch* existingmatch = nullptr; + if (mMatchMap.Get(resultid, &existingmatch)){ + // check if there is an existing match that matched a rule + while (existingmatch) { + if (existingmatch->IsActive()) + generateContent = false; + prevmatch = existingmatch; + existingmatch = existingmatch->mNext; + } + } + + nsTemplateMatch *newmatch = + nsTemplateMatch::Create(aQuerySet->Priority(), nextresult, nullptr); + if (!newmatch) + return NS_ERROR_OUT_OF_MEMORY; + + if (generateContent) { + // Don't allow cyclic graphs to get our knickers in a knot. + bool cyclic = false; + + if (aIndex >= 0) { + for (nsTreeRows::iterator iter = mRows[aIndex]; iter.GetDepth() > 0; iter.Pop()) { + nsCOMPtr<nsIRDFResource> parentid; + rv = GetResultResource(iter->mMatch->mResult, getter_AddRefs(parentid)); + if (NS_FAILED(rv)) { + nsTemplateMatch::Destroy(newmatch, false); + return rv; + } + + if (resultid == parentid) { + cyclic = true; + break; + } + } + } + + if (cyclic) { + NS_WARNING("tree cannot handle cyclic graphs"); + nsTemplateMatch::Destroy(newmatch, false); + continue; + } + + int16_t ruleindex; + nsTemplateRule* matchedrule = nullptr; + rv = DetermineMatchedRule(nullptr, nextresult, aQuerySet, + &matchedrule, &ruleindex); + if (NS_FAILED(rv)) { + nsTemplateMatch::Destroy(newmatch, false); + return rv; + } + + if (matchedrule) { + rv = newmatch->RuleMatched(aQuerySet, matchedrule, ruleindex, + nextresult); + if (NS_FAILED(rv)) { + nsTemplateMatch::Destroy(newmatch, false); + return rv; + } + + // Remember that this match applied to this row + mRows.InsertRowAt(newmatch, aSubtree, count); + + // If this is open, then remember it so we can recursively add + // *its* rows to the tree. + if (IsContainerOpen(nextresult)) { + if (open.AppendElement(count) == nullptr) + return NS_ERROR_OUT_OF_MEMORY; + } + + ++count; + } + + if (mFlags & eLoggingEnabled) + OutputMatchToLog(resultid, newmatch, true); + + } + + if (prevmatch) { + prevmatch->mNext = newmatch; + } + else { + mMatchMap.Put(resultid, newmatch); + } + } + + *aDelta = count; + return rv; +} + +nsresult +nsXULTreeBuilder::CloseContainer(int32_t aIndex) +{ + NS_ASSERTION(aIndex >= 0 && aIndex < mRows.Count(), "bad row"); + if (aIndex < 0 || aIndex >= mRows.Count()) + return NS_ERROR_INVALID_ARG; + + nsTreeRows::iterator iter = mRows[aIndex]; + + if (iter->mSubtree) + RemoveMatchesFor(*iter->mSubtree); + + + int32_t count = mRows.GetSubtreeSizeFor(iter); + mRows.RemoveSubtreeFor(iter); + + iter->mContainerState = nsTreeRows::eContainerState_Closed; + + if (mBoxObject) { + mBoxObject->InvalidateRow(aIndex); + + if (count) + mBoxObject->RowCountChanged(aIndex + 1, -count); + } + + return NS_OK; +} + +nsresult +nsXULTreeBuilder::RemoveMatchesFor(nsTreeRows::Subtree& subtree) +{ + for (int32_t i = subtree.Count() - 1; i >= 0; --i) { + nsTreeRows::Row& row = subtree[i]; + + nsTemplateMatch* match = row.mMatch; + + nsCOMPtr<nsIRDFResource> id; + nsresult rv = GetResultResource(match->mResult, getter_AddRefs(id)); + if (NS_FAILED(rv)) + return rv; + + nsTemplateMatch* existingmatch; + if (mMatchMap.Get(id, &existingmatch)) { + while (existingmatch) { + nsTemplateMatch* nextmatch = existingmatch->mNext; + nsTemplateMatch::Destroy(existingmatch, true); + existingmatch = nextmatch; + } + + mMatchMap.Remove(id); + } + + if ((row.mContainerState == nsTreeRows::eContainerState_Open) && row.mSubtree) + RemoveMatchesFor(*(row.mSubtree)); + } + + return NS_OK; +} + + +bool +nsXULTreeBuilder::IsContainerOpen(nsIXULTemplateResult *aResult) +{ + // items are never open if recursion is disabled + if ((mFlags & eDontRecurse) && aResult != mRootResult) { + return false; + } + + if (!mLocalStore) { + return false; + } + + nsIDocument* doc = mRoot->GetComposedDoc(); + if (!doc) { + return false; + } + + nsIURI* docURI = doc->GetDocumentURI(); + + nsAutoString nodeid; + nsresult rv = aResult->GetId(nodeid); + if (NS_FAILED(rv)) { + return false; + } + + nsAutoCString utf8uri; + rv = docURI->GetSpec(utf8uri); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + NS_ConvertUTF8toUTF16 uri(utf8uri); + + nsAutoString val; + mLocalStore->GetValue(uri, nodeid, NS_LITERAL_STRING("open"), val); + return val.EqualsLiteral("true"); +} + +int +nsXULTreeBuilder::Compare(const void* aLeft, const void* aRight, void* aClosure) +{ + nsXULTreeBuilder* self = static_cast<nsXULTreeBuilder*>(aClosure); + + nsTreeRows::Row* left = static_cast<nsTreeRows::Row*> + (const_cast<void*>(aLeft)); + + nsTreeRows::Row* right = static_cast<nsTreeRows::Row*> + (const_cast<void*>(aRight)); + + return self->CompareResults(left->mMatch->mResult, right->mMatch->mResult); +} + +int32_t +nsXULTreeBuilder::CompareResults(nsIXULTemplateResult* aLeft, nsIXULTemplateResult* aRight) +{ + // this is an extra check done for RDF queries such that results appear in + // the order they appear in their containing Seq + if (mSortDirection == eDirection_Natural && mDB) { + // If the sort order is ``natural'', then see if the container + // is an RDF sequence. If so, we'll try to use the ordinal + // properties to determine order. + // + // XXX the problem with this is, it doesn't always get the + // *real* container; e.g., + // + // <treerow uri="?uri" /> + // + // <triple subject="?uri" + // predicate="http://home.netscape.com/NC-rdf#subheadings" + // object="?subheadings" /> + // + // <member container="?subheadings" child="?subheading" /> + // + // In this case mRefVariable is bound to ?uri, not + // ?subheadings. (The ``container'' in the template sense != + // container in the RDF sense.) + + nsCOMPtr<nsISupports> ref; + nsresult rv = aLeft->GetBindingObjectFor(mRefVariable, getter_AddRefs(ref)); + if (NS_FAILED(rv)) + return 0; + + nsCOMPtr<nsIRDFResource> container = do_QueryInterface(ref); + if (container) { + bool isSequence = false; + gRDFContainerUtils->IsSeq(mDB, container, &isSequence); + if (isSequence) { + // Determine the indices of the left and right elements + // in the container. + int32_t lindex = 0, rindex = 0; + + nsCOMPtr<nsIRDFResource> leftitem; + aLeft->GetResource(getter_AddRefs(leftitem)); + if (leftitem) { + gRDFContainerUtils->IndexOf(mDB, container, leftitem, &lindex); + if (lindex < 0) + return 0; + } + + nsCOMPtr<nsIRDFResource> rightitem; + aRight->GetResource(getter_AddRefs(rightitem)); + if (rightitem) { + gRDFContainerUtils->IndexOf(mDB, container, rightitem, &rindex); + if (rindex < 0) + return 0; + } + + return lindex - rindex; + } + } + } + + int32_t sortorder; + if (!mQueryProcessor) + return 0; + + mQueryProcessor->CompareResults(aLeft, aRight, mSortVariable, mSortHints, &sortorder); + + if (sortorder) + sortorder = sortorder * mSortDirection; + return sortorder; +} + +nsresult +nsXULTreeBuilder::SortSubtree(nsTreeRows::Subtree* aSubtree) +{ + NS_QuickSort(mRows.GetRowsFor(aSubtree), + aSubtree->Count(), + sizeof(nsTreeRows::Row), + Compare, + this); + + for (int32_t i = aSubtree->Count() - 1; i >= 0; --i) { + nsTreeRows::Subtree* child = (*aSubtree)[i].mSubtree; + if (child) + SortSubtree(child); + } + + return NS_OK; +} + + +NS_IMETHODIMP +nsXULTreeBuilder::CanDrop(int32_t index, int32_t orientation, + nsIDOMDataTransfer* dataTransfer, bool *_retval) +{ + *_retval = false; + uint32_t count = mObservers.Count(); + for (uint32_t i = 0; i < count; ++i) { + nsCOMPtr<nsIXULTreeBuilderObserver> observer = mObservers.SafeObjectAt(i); + if (observer) { + observer->CanDrop(index, orientation, dataTransfer, _retval); + if (*_retval) + break; + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTreeBuilder::Drop(int32_t row, int32_t orient, nsIDOMDataTransfer* dataTransfer) +{ + uint32_t count = mObservers.Count(); + for (uint32_t i = 0; i < count; ++i) { + nsCOMPtr<nsIXULTreeBuilderObserver> observer = mObservers.SafeObjectAt(i); + if (observer) { + bool canDrop = false; + observer->CanDrop(row, orient, dataTransfer, &canDrop); + if (canDrop) + observer->OnDrop(row, orient, dataTransfer); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTreeBuilder::IsSorted(bool *_retval) +{ + *_retval = (mSortVariable != nullptr); + return NS_OK; +} + diff --git a/dom/xul/templates/tests/chrome/animals.rdf b/dom/xul/templates/tests/chrome/animals.rdf new file mode 100644 index 000000000..06fee7ac5 --- /dev/null +++ b/dom/xul/templates/tests/chrome/animals.rdf @@ -0,0 +1,224 @@ +<?xml version="1.0"?> + +<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:NC="http://home.netscape.com/NC-rdf#" + xmlns:ANIMALS="http://www.some-fictitious-zoo.com/rdf#"> + + <ANIMALS:Class RDF:about="http://www.some-fictitious-zoo.com/arachnids"> + <ANIMALS:name>Arachnids</ANIMALS:name> + </ANIMALS:Class> + + <RDF:Description RDF:about="http://www.some-fictitious-zoo.com/arachnids/tarantula" ANIMALS:specimens="3"> + <ANIMALS:name>Tarantula</ANIMALS:name> + <ANIMALS:species>Avicularia avicularia</ANIMALS:species> + </RDF:Description> + + <ANIMALS:Class RDF:about="http://www.some-fictitious-zoo.com/birds"> + <ANIMALS:name>Birds</ANIMALS:name> + <ANIMALS:keeper resource="http://www.some-fictitious-zoo.com/humans/sarah"/> + </ANIMALS:Class> + + <RDF:Description RDF:about="http://www.some-fictitious-zoo.com/birds/emu" ANIMALS:specimens="12"> + <ANIMALS:name>Emu</ANIMALS:name> + <ANIMALS:species>Dromaius novaehollandiae</ANIMALS:species> + </RDF:Description> + + <RDF:Description RDF:about="http://www.some-fictitious-zoo.com/birds/barnowl" ANIMALS:specimens="4"> + <ANIMALS:name>Barn Owl</ANIMALS:name> + <ANIMALS:species>Tyto alba</ANIMALS:species> + </RDF:Description> + + <RDF:Description RDF:about="http://www.some-fictitious-zoo.com/birds/raven" ANIMALS:specimens="0"> + <ANIMALS:name>Raven</ANIMALS:name> + <ANIMALS:species>Corvus corax</ANIMALS:species> + </RDF:Description> + + <ANIMALS:Class RDF:about="http://www.some-fictitious-zoo.com/crustaceans"> + <ANIMALS:name>Crustaceans</ANIMALS:name> + <ANIMALS:keeper resource="http://www.some-fictitious-zoo.com/humans/robert"/> + </ANIMALS:Class> + + <ANIMALS:Class RDF:about="http://www.some-fictitious-zoo.com/fish"> + <ANIMALS:name>Fish</ANIMALS:name> + </ANIMALS:Class> + + <RDF:Description RDF:about="http://www.some-fictitious-zoo.com/fish/cod" ANIMALS:specimens="0"> + <ANIMALS:name>Cod</ANIMALS:name> + <ANIMALS:species>Gadus morhua</ANIMALS:species> + </RDF:Description> + + <RDF:Description RDF:about="http://www.some-fictitious-zoo.com/fish/swordfish" ANIMALS:specimens="3"> + <ANIMALS:name>Swordfish</ANIMALS:name> + <ANIMALS:species>Xiphias gladius</ANIMALS:species> + </RDF:Description> + + <ANIMALS:Class RDF:about="http://www.some-fictitious-zoo.com/mammals"> + <ANIMALS:name>Mammals</ANIMALS:name> + </ANIMALS:Class> + + <RDF:Description RDF:about="http://www.some-fictitious-zoo.com/mammals/lion"> + <ANIMALS:name>Lion</ANIMALS:name> + <ANIMALS:species>Panthera leo</ANIMALS:species> + <ANIMALS:specimens NC:parseType="Integer">4</ANIMALS:specimens> + <ANIMALS:specimensAsString>4</ANIMALS:specimensAsString> + </RDF:Description> + + <RDF:Description RDF:about="http://www.some-fictitious-zoo.com/mammals/hippopotamus"> + <ANIMALS:name>HIPPOPOTAMUS</ANIMALS:name> + <ANIMALS:species>Hippopotamus amphibius</ANIMALS:species> + <ANIMALS:specimens NC:parseType="Integer">2</ANIMALS:specimens> + <ANIMALS:specimensAsString>2</ANIMALS:specimensAsString> + </RDF:Description> + + <RDF:Description RDF:about="http://www.some-fictitious-zoo.com/mammals/africanelephant"> + <ANIMALS:name>African Elephant</ANIMALS:name> + <ANIMALS:species>Loxodonta africana</ANIMALS:species> + <ANIMALS:specimens NC:parseType="Integer">14</ANIMALS:specimens> + <ANIMALS:specimensAsString>14</ANIMALS:specimensAsString> + </RDF:Description> + + <RDF:Description RDF:about="http://www.some-fictitious-zoo.com/mammals/llama"> + <ANIMALS:name>LLAMA</ANIMALS:name> + <ANIMALS:species>Lama glama</ANIMALS:species> + <ANIMALS:specimens NC:parseType="Integer">5</ANIMALS:specimens> + <ANIMALS:specimensAsString>5</ANIMALS:specimensAsString> + </RDF:Description> + + <RDF:Description RDF:about="http://www.some-fictitious-zoo.com/mammals/polarbear"> + <ANIMALS:name>Polar Bear</ANIMALS:name> + <ANIMALS:species>Thalarctos maritimus</ANIMALS:species> + <ANIMALS:specimens NC:parseType="Integer">20</ANIMALS:specimens> + <ANIMALS:specimensAsString>20</ANIMALS:specimensAsString> + </RDF:Description> + + <RDF:Description RDF:about="http://www.some-fictitious-zoo.com/mammals/aardvark"> + <ANIMALS:name>aardvark</ANIMALS:name> + <ANIMALS:species>Orycteropus afer</ANIMALS:species> + <ANIMALS:specimens NC:parseType="Integer">2</ANIMALS:specimens> + <ANIMALS:specimensAsString>2</ANIMALS:specimensAsString> + </RDF:Description> + + <RDF:Description RDF:about="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo"> + <ANIMALS:name>Nine-banded Armadillo</ANIMALS:name> + <ANIMALS:species>Dasypus novemcinctus</ANIMALS:species> + <ANIMALS:specimens NC:parseType="Integer">1</ANIMALS:specimens> + <ANIMALS:specimensAsString>1</ANIMALS:specimensAsString> + </RDF:Description> + + <RDF:Description RDF:about="http://www.some-fictitious-zoo.com/mammals/gorilla"> + <ANIMALS:name>Gorilla</ANIMALS:name> + <ANIMALS:species>Gorilla gorilla</ANIMALS:species> + <ANIMALS:specimens NC:parseType="Integer">7</ANIMALS:specimens> + <ANIMALS:specimensAsString>7</ANIMALS:specimensAsString> + </RDF:Description> + + <ANIMALS:Class RDF:about="http://www.some-fictitious-zoo.com/reptiles"> + <ANIMALS:name>Reptiles</ANIMALS:name> + <ANIMALS:keeper resource="http://www.some-fictitious-zoo.com/humans/robert"/> + </ANIMALS:Class> + + <RDF:Description RDF:about="http://www.some-fictitious-zoo.com/reptiles/anaconda" ANIMALS:specimens="1"> + <ANIMALS:name>Anaconda</ANIMALS:name> + <ANIMALS:species>Eunectes murinus</ANIMALS:species> + </RDF:Description> + + <RDF:Description RDF:about="http://www.some-fictitious-zoo.com/reptiles/chameleon" ANIMALS:specimens="2"> + <ANIMALS:name>Chameleon</ANIMALS:name> + <ANIMALS:species>Chamaeleo chamaelon</ANIMALS:species> + </RDF:Description> + + <RDF:Seq RDF:about="http://www.some-fictitious-zoo.com/some-animals" ANIMALS:name="Zoo Animals"> + <RDF:li RDF:resource="http://www.some-fictitious-zoo.com/arachnids"/> + <RDF:li RDF:resource="http://www.some-fictitious-zoo.com/birds"/> + </RDF:Seq> + + <RDF:Seq RDF:about="http://www.some-fictitious-zoo.com/all-animals" ANIMALS:name="Zoo Animals"> + <RDF:li> + <RDF:Seq RDF:about="http://www.some-fictitious-zoo.com/arachnids"> + <RDF:li RDF:resource="http://www.some-fictitious-zoo.com/arachnids/tarantula"/> + </RDF:Seq> + </RDF:li> + <RDF:li> + <RDF:Seq RDF:about="http://www.some-fictitious-zoo.com/birds"> + <RDF:li RDF:resource="http://www.some-fictitious-zoo.com/birds/emu"/> + <RDF:li RDF:resource="http://www.some-fictitious-zoo.com/birds/barnowl"/> + <RDF:li RDF:resource="http://www.some-fictitious-zoo.com/birds/raven"/> + </RDF:Seq> + </RDF:li> + <RDF:li> + <RDF:Seq RDF:about="http://www.some-fictitious-zoo.com/crustaceans"/> + </RDF:li> + <RDF:li> + <RDF:Seq RDF:about="http://www.some-fictitious-zoo.com/fish"> + <RDF:li RDF:resource="http://www.some-fictitious-zoo.com/fish/cod"/> + <RDF:li RDF:resource="http://www.some-fictitious-zoo.com/fish/swordfish"/> + </RDF:Seq> + </RDF:li> + <RDF:li> + <RDF:Seq RDF:about="http://www.some-fictitious-zoo.com/mammals"> + <RDF:li RDF:resource="http://www.some-fictitious-zoo.com/mammals/lion"/> + <RDF:li RDF:resource="http://www.some-fictitious-zoo.com/mammals/hippopotamus"/> + <RDF:li RDF:resource="http://www.some-fictitious-zoo.com/mammals/africanelephant"/> + <RDF:li RDF:resource="http://www.some-fictitious-zoo.com/mammals/llama"/> + <RDF:li RDF:resource="http://www.some-fictitious-zoo.com/mammals/polarbear"/> + <RDF:li RDF:resource="http://www.some-fictitious-zoo.com/mammals/aardvark"/> + <RDF:li RDF:resource="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo"/> + <RDF:li RDF:resource="http://www.some-fictitious-zoo.com/mammals/gorilla"/> + </RDF:Seq> + </RDF:li> + <RDF:li> + <RDF:Seq RDF:about="http://www.some-fictitious-zoo.com/reptiles"> + <RDF:li RDF:resource="http://www.some-fictitious-zoo.com/reptiles/anaconda"/> + <RDF:li RDF:resource="http://www.some-fictitious-zoo.com/reptiles/chameleon"/> + </RDF:Seq> + </RDF:li> + </RDF:Seq> + + <RDF:Seq RDF:about="http://www.some-fictitious-zoo.com/humans" ANIMALS:name="Humans"> + <RDF:li RDF:resource="http://www.some-fictitious-zoo.com/humans/sarah"/> + <RDF:li RDF:resource="http://www.some-fictitious-zoo.com/humans/robert"/> + </RDF:Seq> + + <RDF:Seq RDF:about="http://www.some-fictitious-zoo.com/sarahs-pets" ANIMALS:name="Sarah's Pets"> + <RDF:li RDF:resource="http://www.some-fictitious-zoo.com/birds/emu"/> + <RDF:li RDF:resource="http://www.some-fictitious-zoo.com/arachnids/tarantula"/> + </RDF:Seq> + + <RDF:Seq RDF:about="http://www.some-fictitious-zoo.com/roberts-pets" ANIMALS:name="Robert's Pets"> + <RDF:li RDF:resource="http://www.some-fictitious-zoo.com/reptiles/chameleon"/> + <RDF:li RDF:resource="http://www.some-fictitious-zoo.com/arachnids/tarantula"/> + <RDF:li RDF:resource="http://www.some-fictitious-zoo.com/mammals/llama"/> + </RDF:Seq> + + <RDF:Description RDF:about="http://www.some-fictitious-zoo.com/humans/sarah" ANIMALS:name="Sarah"> + <ANIMALS:pets resource="http://www.some-fictitious-zoo.com/sarahs-pets"/> + <ANIMALS:favoriteAnimal resource="http://www.some-fictitious-zoo.com/birds/emu"/> + <ANIMALS:favoriteAnimal resource="http://www.some-fictitious-zoo.com/mammals/polarbear"/> + <ANIMALS:favoriteAnimal resource="http://www.some-fictitious-zoo.com/arachnids/tarantula"/> + <ANIMALS:description> + Sarah became caretaker of the Fictitious Zoo's emu exhibit in 2001. With so + many emus living there, she has a lot to do! + </ANIMALS:description> + </RDF:Description> + + <RDF:Description RDF:about="http://www.some-fictitious-zoo.com/humans/robert" ANIMALS:name="Robert"> + <ANIMALS:pets resource="http://www.some-fictitious-zoo.com/roberts-pets"/> + <ANIMALS:favoriteAnimal resource="http://www.some-fictitious-zoo.com/arachnids/tarantula"/> + <ANIMALS:favoriteAnimal resource="http://www.some-fictitious-zoo.com/reptiles/anaconda"/> + <ANIMALS:favoriteAnimal resource="http://www.some-fictitious-zoo.com/reptiles/chameleon"/> + <ANIMALS:favoriteAnimal resource="http://www.some-fictitious-zoo.com/mammals/africanelephant"/> + <ANIMALS:description> + Robert helps visitors to the Fictitious Zoo's reptile pavilion learn + more about some of the more unusual creatures that live there. + </ANIMALS:description> + <ANIMALS:lastName>Sanderson</ANIMALS:lastName> + </RDF:Description> + + <RDF:Seq RDF:about="http://www.some-fictitious-zoo.com/marked" ANIMALS:name="Marked"> + <RDF:li RDF:resource="http://www.some-fictitious-zoo.com/humans/sarah"/> + <RDF:li RDF:resource="http://www.some-fictitious-zoo.com/mammals"/> + <RDF:li RDF:resource="http://www.some-fictitious-zoo.com/crustaceans"/> + <RDF:li RDF:resource="http://www.some-fictitious-zoo.com/birds/emu"/> + </RDF:Seq> + +</RDF:RDF> diff --git a/dom/xul/templates/tests/chrome/animals.sqlite b/dom/xul/templates/tests/chrome/animals.sqlite Binary files differnew file mode 100644 index 000000000..7ba88ff74 --- /dev/null +++ b/dom/xul/templates/tests/chrome/animals.sqlite diff --git a/dom/xul/templates/tests/chrome/animals.xml b/dom/xul/templates/tests/chrome/animals.xml new file mode 100644 index 000000000..f73ec718b --- /dev/null +++ b/dom/xul/templates/tests/chrome/animals.xml @@ -0,0 +1,19 @@ +<?xml version="1.0"?> + +<!DOCTYPE zoo [ + <!ATTLIST species id ID #REQUIRED> +]> + +<zoo> + <class> + <name>Reptiles</name> + <species id="Chamaeleo-chamaelon" name="Chameleon" specimens="2"/> + </class> + <class> + <name>Birds</name> + <species id="Dromaius-novaehollandiae" name="Emu" specimens="12"/> + <species id="Tyto-alba" name="Barn Owl" specimens="4"/> + <species id="Corvus-corax" name="Raven" specimens="0"/> + <location>Aviary</location> + </class> +</zoo> diff --git a/dom/xul/templates/tests/chrome/bug441785-1.rdf b/dom/xul/templates/tests/chrome/bug441785-1.rdf new file mode 100644 index 000000000..4be55657f --- /dev/null +++ b/dom/xul/templates/tests/chrome/bug441785-1.rdf @@ -0,0 +1,263 @@ +<?xml version="1.0" encoding="utf-8"?> +<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:row="http://dummy/rdf#" xmlns:NC="http://home.netscape.com/NC-rdf#"> + <RDF:Bag about="urn:data:row"> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">111110</row:id> + <row:title>FILE 1 -- A</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">111111</row:id> + <row:title>FILE 1 -- B</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">111110</row:id> + <row:title>FILE 1 -- C</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">111111</row:id> + <row:title>FILE 1 -- D</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">111112</row:id> + <row:title>FILE 1 -- E</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">111110</row:id> + <row:title>FILE 1 -- F</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">111114</row:id> + <row:title>FILE 1 -- G</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">111111</row:id> + <row:title>FILE 1 -- H</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">111118</row:id> + <row:title>FILE 1 -- I</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">111116</row:id> + <row:title>FILE 1 -- J</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">111110</row:id> + <row:title>FILE 1 -- K</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">111119</row:id> + <row:title>FILE 1 -- L</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">111116</row:id> + <row:title>FILE 1 -- M</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">1111111</row:id> + <row:title>FILE 1 -- N</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">111113</row:id> + <row:title>FILE 1 -- O</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">111111</row:id> + <row:title>FILE 1 -- P</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">111117</row:id> + <row:title>FILE 1 -- Q</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">1111110</row:id> + <row:title>FILE 1 -- R</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">111113</row:id> + <row:title>FILE 1 -- S</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">1111112</row:id> + <row:title>FILE 1 -- T</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">111116</row:id> + <row:title>FILE 1 -- U</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">1111111</row:id> + <row:title>FILE 1 -- V</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">1111121</row:id> + <row:title>FILE 1 -- W</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">111114</row:id> + <row:title>FILE 1 -- X</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">1111122</row:id> + <row:title>FILE 1 -- Y</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">1111113</row:id> + <row:title>FILE 1 -- Z</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">1111119</row:id> + <row:title>FILE 1 -- AA</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">1111117</row:id> + <row:title>FILE 1 -- BB</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">111117</row:id> + <row:title>FILE 1 -- CC</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">111117</row:id> + <row:title>FILE 1 -- DD</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">111116</row:id> + <row:title>FILE 1 -- EE</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">1111124</row:id> + <row:title>FILE 1 -- FF</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">111118</row:id> + <row:title>FILE 1 -- GG</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">1111117</row:id> + <row:title>FILE 1 -- HH</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">111118</row:id> + <row:title>FILE 1 -- II</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">1111125</row:id> + <row:title>FILE 1 -- JJ</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">1111124</row:id> + <row:title>FILE 1 -- KK</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">1111132</row:id> + <row:title>FILE 1 -- LL</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">1111136</row:id> + <row:title>FILE 1 -- MM</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">1111123</row:id> + <row:title>FILE 1 -- NN</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">1111122</row:id> + <row:title>FILE 1 -- OO</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">111110</row:id> + <row:title>FILE 1 -- PP</row:title> + </RDF:Description> + </RDF:li> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">1111116</row:id> + <row:title>FILE 1 -- QQ</row:title> + </RDF:Description> + </RDF:li> + </RDF:Bag> +</RDF:RDF> diff --git a/dom/xul/templates/tests/chrome/bug441785-2.rdf b/dom/xul/templates/tests/chrome/bug441785-2.rdf new file mode 100644 index 000000000..dca97ba78 --- /dev/null +++ b/dom/xul/templates/tests/chrome/bug441785-2.rdf @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:row="http://dummy/rdf#" xmlns:NC="http://home.netscape.com/NC-rdf#"> + <RDF:Bag about="urn:data:row"> + <RDF:li> + <RDF:Description> + <row:id NC:parseType="Integer">222220</row:id> + <row:title>FILE 2 -- A</row:title> + </RDF:Description> + </RDF:li> + </RDF:Bag> +</RDF:RDF> diff --git a/dom/xul/templates/tests/chrome/chrome.ini b/dom/xul/templates/tests/chrome/chrome.ini new file mode 100644 index 000000000..97989723c --- /dev/null +++ b/dom/xul/templates/tests/chrome/chrome.ini @@ -0,0 +1,225 @@ +[DEFAULT] +skip-if = os == 'android' +support-files = + animals.rdf + animals.sqlite + animals.xml + bug441785-1.rdf + bug441785-2.rdf + templates_shared.js + +[test_bug329335.xul] +[test_bug330010.xul] +skip-if = os == "win" +support-files = file_bug330010.rdf +[test_bug397148.xul] +skip-if = true # Bug 879531 +[test_bug441785.xul] +[test_bug476634.xul] +[test_sortservice.xul] +[test_tmpl_bindingsextendedsyntax.xul] +[test_tmpl_bindingsmultiple.xul] +[test_tmpl_bindingsquerysyntax.xul] +[test_tmpl_bindingsreversed.xul] +[test_tmpl_bindingssameastriple.xul] +[test_tmpl_containerandmembervariablechanged.xul] +[test_tmpl_containervariablechanged.xul] +[test_tmpl_containmentattribute.xul] +[test_tmpl_defaultcontainervariableisuri.xul] +[test_tmpl_errors.xul] +[test_tmpl_extendedsyntax.xul] +[test_tmpl_extendedsyntaxemptyconditions.xul] +[test_tmpl_extendedsyntaxotherrefvariable.xul] +[test_tmpl_extendedsyntaxremoveunmatched.xul] +[test_tmpl_extendedsyntaxsimplevariablesubstitution.xul] +[test_tmpl_extendedsyntaxtworulesrecurse.xul] +[test_tmpl_extendedsyntaxusinganinterveningcontainer.xul] +[test_tmpl_extendedvariablesubstitution.xul] +[test_tmpl_gridelement.xul] +[test_tmpl_htmlelementextendedsyntaxwithbinding.xul] +[test_tmpl_htmlelementquerysyntaxrecursive.xul] +[test_tmpl_htmlelementquerysyntaxwithmultiplerules.xul] +[test_tmpl_htmlelementsimplesyntax.xul] +[test_tmpl_htmlelementsimplesyntaxusingatextnode.xul] +[test_tmpl_invalidqp.xul] +[test_tmpl_listboxelement.xul] +[test_tmpl_literalasmember.xul] +[test_tmpl_membervariablechanged.xul] +[test_tmpl_membervariablesubstitution.xul] +[test_tmpl_menuelement.xul] +[test_tmpl_menuelementrecursive.xul] +[test_tmpl_menulistelement.xul] +[test_tmpl_mixedsyntaxiscontainer.xul] +[test_tmpl_mixedsyntaxiscontainerisempty.xul] +[test_tmpl_mixedsyntaxisempty.xul] +[test_tmpl_noaction.xul] +[test_tmpl_noactionuriattribute.xul] +[test_tmpl_parentconditions.xul] +[test_tmpl_parentcontenttag.xul] +[test_tmpl_parentsimplesyntax.xul] +[test_tmpl_query3triples.xul] +[test_tmpl_query3tripleswherecontains.xul] +[test_tmpl_querymember3tripleswhereequals.xul] +[test_tmpl_querymemberandtwotriples.xul] +[test_tmpl_querymembertriplemembertriple.xul] +[test_tmpl_queryresourcematch.xul] +[test_tmpl_queryreversetriple.xul] +[test_tmpl_queryselfwithtriple.xul] +[test_tmpl_querysetone.xul] +[test_tmpl_querysettwo.xul] +[test_tmpl_querysettwowithcondition.xul] +[test_tmpl_querysyntax.xul] +[test_tmpl_querysyntaxmultiplerules.xul] +[test_tmpl_querysyntaxmultiplerulesfirstconditionall.xul] +[test_tmpl_querysyntaxmultiplerulestwoconditions.xul] +[test_tmpl_querytripleandmembermerge.xul] +[test_tmpl_querytripleobjecttosubject.xul] +[test_tmpl_querytwomembers.xul] +[test_tmpl_querytwomembersfiltered.xul] +[test_tmpl_querytwotriples.xul] +[test_tmpl_queryupwardsmember.xul] +[test_tmpl_queryupwardsmembertripleandfilteringtriple.xul] +[test_tmpl_querywithemptyconditions.xul] +[test_tmpl_referenceasmember.xul] +[test_tmpl_regenerate.xul] +[test_tmpl_selfgenerationextendedsyntax.xul] +[test_tmpl_selfgenerationsimplesyntax.xul] +[test_tmpl_simplesyntaxenclosedinacontainer.xul] +[test_tmpl_simplesyntaxenclosedinacontainerwitharule.xul] +[test_tmpl_simplesyntaxfilter.xul] +[test_tmpl_simplesyntaxfilterwithmultiplerules.xul] +[test_tmpl_simplesyntaxfilterwithrule.xul] +[test_tmpl_simplesyntaxiteratingoverasinglevalue.xul] +[test_tmpl_simplesyntaxusinganinterveningcontainer.xul] +[test_tmpl_simplesyntaxusingatextnode.xul] +[test_tmpl_simplesyntaxusingcontainerasthegenerationelement.xul] +[test_tmpl_simplesyntaxusingdontrecurse.xul] +[test_tmpl_simplesyntaxusingrecursivegeneration.xul] +[test_tmpl_simplesyntaxusingrecursivegenerationagain.xul] +[test_tmpl_simplesyntaxwithtwovariablesused.xul] +[test_tmpl_simplevariablesubstitutioncaretsatbeginningandend.xul] +[test_tmpl_simplevariablesubstitutioncaretsubstitution.xul] +[test_tmpl_simplevariablesubstitutionnovariable.xul] +[test_tmpl_simplevariablesubstitutionquestionmarkaspartofvariable.xul] +[test_tmpl_simplevariablesubstitutionquestionmarksubstitution.xul] +[test_tmpl_simplevariablesubstitutiontextandvariable.xul] +[test_tmpl_simplevariablesubstitutionvariableandtextconcatenated.xul] +[test_tmpl_simplevariablesubstitutionvariablesconcatenated.xul] +[test_tmpl_sortascendinginteger.xul] +[test_tmpl_sortascendingquerysyntax.xul] +[test_tmpl_sortascendingtworulesquerysyntax.xul] +[test_tmpl_sortascendingtworuleswithcontainerquerysyntax.xul] +[test_tmpl_sortascendingtworuleswithdifferentcontainerquerysyntax.xul] +[test_tmpl_sortdescendingquerysyntax.xul] +[test_tmpl_sortquerymemberandtwotriples.xul] +[test_tmpl_sortresource2descendingsimplesyntax.xul] +[test_tmpl_sortresource2settopredicateascendingquerysyntax.xul] +[test_tmpl_sortresource2settopredicatedescendingquerysyntax.xul] +[test_tmpl_sortresourceascendingquerysyntax.xul] +[test_tmpl_sortresourcedescendingquerysyntax.xul] +[test_tmpl_sortresourcesettopredicateascendingquerysyntax.xul] +[test_tmpl_sortresourcesettopredicatedescendingquerysyntax.xul] +[test_tmpl_sorttworesourcesasstringsettopredicatedescendingquerysyntax.xul] +[test_tmpl_sorttworesourcessettopredicateascendingquerysyntax.xul] +[test_tmpl_sorttwovariablesascendingquerysyntax.xul] +[test_tmpl_sorttwovariablesascendingsimplesyntax.xul] +[test_tmpl_sorttwovariablesdescendingquerysyntax.xul] +[test_tmpl_sortunknownascendingquerysyntax.xul] +[test_tmpl_storage_bad_parameters.xul] +[test_tmpl_storage_bad_parameters_2.xul] +[test_tmpl_storage_bad_parameters_3.xul] +[test_tmpl_storage_baddatasource.xul] +[test_tmpl_storage_badquery.xul] +[test_tmpl_storage_dynamicparameters.xul] +[test_tmpl_storage_listbox.xul] +[test_tmpl_storage_multiqueries.xul] +[test_tmpl_storage_parameters.xul] +[test_tmpl_storage_rule.xul] +[test_tmpl_storage_simple.xul] +[test_tmpl_storage_sortintegerasc.xul] +[test_tmpl_storage_sortintegerdesc.xul] +[test_tmpl_storage_sortstringasc.xul] +[test_tmpl_storage_sortstringdesc.xul] +[test_tmpl_storage_tree.xul] +[test_tmpl_treeelementquerysyntax.xul] +[test_tmpl_treeelementquerysyntaxnotrecursive.xul] +[test_tmpl_treeelementquerysyntaxnotrecursivetreebuilder.xul] +[test_tmpl_treeelementquerysyntaxrecursive.xul] +[test_tmpl_treeelementquerysyntaxrecursivemultiplerules.xul] +[test_tmpl_treeelementquerysyntaxrecursivemultiplerulestreebuilder.xul] +[test_tmpl_treeelementquerysyntaxrecursivetreebuilder.xul] +[test_tmpl_treeelementquerysyntaxtreebuilder.xul] +[test_tmpl_treeelementsimplesyntaxnotrecursive.xul] +[test_tmpl_treeelementsimplesyntaxnotrecursivetreebuilder.xul] +[test_tmpl_treeelementsimplesyntaxrecursive.xul] +[test_tmpl_treeelementsimplesyntaxrecursivetreebuilder.xul] +[test_tmpl_treeelementtreecell.xul] +[test_tmpl_treeelementtreecellsortascending.xul] +[test_tmpl_treeelementtreecellsortascendingtreebuilder.xul] +[test_tmpl_treeelementtreecelltreebuilder.xul] +[test_tmpl_treeelementtreeitemonly.xul] +[test_tmpl_treeelementtreeitemsortascending.xul] +[test_tmpl_twogenerationnodes.xul] +[test_tmpl_whereafterignorecase.xul] +[test_tmpl_whereafterlowercase.xul] +[test_tmpl_whereafternegation.xul] +[test_tmpl_whereafteruppercase.xul] +[test_tmpl_wherebeforeignorecase.xul] +[test_tmpl_wherebeforelowercase.xul] +[test_tmpl_wherebeforenegation.xul] +[test_tmpl_wherebeforeuppercase.xul] +[test_tmpl_wherecontains.xul] +[test_tmpl_wherecontainsignorecase.xul] +[test_tmpl_wherecontainsnegation.xul] +[test_tmpl_wherecontainsnumber.xul] +[test_tmpl_wherecontainsnumberstring.xul] +[test_tmpl_wherecontainsresource.xul] +[test_tmpl_wherecontainstwo.xul] +[test_tmpl_whereendswith.xul] +[test_tmpl_whereendswithignorecase.xul] +[test_tmpl_whereendswithnegation.xul] +[test_tmpl_whereequals.xul] +[test_tmpl_whereequalsignorecase.xul] +[test_tmpl_whereequalsmultiple.xul] +[test_tmpl_whereequalsmultiplenegation.xul] +[test_tmpl_whereequalsmultiplenegationignorecase.xul] +[test_tmpl_whereequalsnegation.xul] +[test_tmpl_whereequalsnegationignorecase.xul] +[test_tmpl_whereequalsnegationwrongcase.xul] +[test_tmpl_whereequalsnumber.xul] +[test_tmpl_whereequalsothervariable.xul] +[test_tmpl_whereequalsresource.xul] +[test_tmpl_whereequalssamevariable.xul] +[test_tmpl_whereequalswrongcase.xul] +[test_tmpl_wheregreater.xul] +[test_tmpl_wheregreaternegation.xul] +[test_tmpl_wheregreaternegationstring.xul] +[test_tmpl_wheregreaterstring.xul] +[test_tmpl_whereless.xul] +[test_tmpl_wherelessnegation.xul] +[test_tmpl_wherelessnegationstring.xul] +[test_tmpl_wherelessstring.xul] +[test_tmpl_wherenorel.xul] +[test_tmpl_wherenosubject.xul] +[test_tmpl_wherenovalue.xul] +[test_tmpl_wherestartswith.xul] +[test_tmpl_wherestartswithignorecase.xul] +[test_tmpl_wherestartswithmultiple.xul] +[test_tmpl_wherestartswithnegation.xul] +[test_tmpl_wherestartswithunknownvariable.xul] +[test_tmpl_wherestartswithvariable.xul] +[test_tmpl_wheresubjectequalsvariable.xul] +[test_tmpl_wheresubjectstartswithvariable.xul] +[test_tmpl_xmlquerysimple.xul] +[test_tmpl_xmlquerywithassign.xul] +[test_tmpl_xmlquerywithassignmentandcondition.xul] +[test_tmpl_xmlquerywithassignmentandconditiondontrecurse.xul] +[test_tmpl_xmlquerywithbindinginbindings.xul] +[test_tmpl_xmlquerywithbindinginrule.xul] +[test_tmpl_xmlquerywithdifferentmember.xul] +[test_tmpl_xmlquerywithinlinedata.xul] +[test_tmpl_xmlquerywithinlinedatawithmultiplequeries.xul] +[test_tmpl_xmlquerywithmultiplequeries.xul] +[test_tmpl_xmlquerywithothertypes.xul] +[test_tmpl_xmlquerywithsort.xul] +[test_tmpl_xmlquerywithsortotherfield.xul] diff --git a/dom/xul/templates/tests/chrome/file_bug330010.rdf b/dom/xul/templates/tests/chrome/file_bug330010.rdf new file mode 100644 index 000000000..428f1045f --- /dev/null +++ b/dom/xul/templates/tests/chrome/file_bug330010.rdf @@ -0,0 +1,13 @@ +<?xml version="1.0"?> +<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:s="urn:croczilla:xulsvg1:"> + <rdf:Description about="urn:root"> + <s:shapes> + <rdf:Bag> + <rdf:li> + <rdf:Description /> + </rdf:li> + </rdf:Bag> + </s:shapes> + </rdf:Description> +</rdf:RDF> diff --git a/dom/xul/templates/tests/chrome/templates_shared.js b/dom/xul/templates/tests/chrome/templates_shared.js new file mode 100644 index 000000000..85c49e52e --- /dev/null +++ b/dom/xul/templates/tests/chrome/templates_shared.js @@ -0,0 +1,488 @@ +/** + * This script is used for testing XUL templates. Call test_template within + * a load event handler. + * + * A test should have a root node with the datasources attribute with the + * id 'root', and a few global variables defined in the test's XUL file: + * + * testid: the testid, used when outputting test results + * expectedOutput: e4x data containing the expected output. It can optionally + * be enclosed in an <output> element as most tests generate + * more than one node of output. + * isTreeBuilder: true for dont-build-content trees, false otherwise + * queryType: 'rdf', 'xml', etc. + * needsOpen: true for menu tests where the root menu must be opened before + * comparing results + * notWorkingYet: true if this test isn't working yet, outputs todo results + * notWorkingYetDynamic: true if the dynamic changes portion of the test + * isn't working yet, outputs todo results + * changes: an array of functions to perform in sequence to test dynamic changes + * to the datasource. + * + * If the <output> element has an unordered attribute set to true, the + * children within it must all appear to match, but may appear in any order. + * If the unordered attribute is not set, the children must appear in the same + * order. + * + * If the 'changes' array is used, it should be an array of functions. Each + * function will be called in order and a comparison of the output will be + * performed. This allows changes to be made to the datasource to ensure that + * the generated template output has been updated. Within the expected output + * XML, the step attribute may be set to a number on an element to indicate + * that an element only applies before or after a particular change. If step + * is set to a positive number, that element will only exist after that step in + * the list of changes made. If step is set to a negative number, that element + * will only exist until that step. Steps are numbered starting at 1. For + * example: + * <label value="Cat"/> + * <label step="2" value="Dog"/> + * <label step="-5" value="Mouse"/> + * The first element will always exist. The second element will only appear + * after the second change is made. The third element will only appear until + * the fifth change and it will no longer be present at later steps. + * + * If the anyid attribute is set to true on an element in the expected output, + * then the value of the id attribute on that element is not compared for a + * match. This is used, for example, for xml datasources, where the ids set on + * the generated output are pseudo-random. + */ + +const ZOO_NS = "http://www.some-fictitious-zoo.com/"; +const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; +const debug = false; + +var expectedConsoleMessages = []; +var expectLoggedMessages = null; + +function get_RDF() { + try { + return Components.classes["@mozilla.org/rdf/rdf-service;1"]. + getService(Components.interfaces.nsIRDFService); + } catch (ex) { } +} + +function get_ContainerUtils() +{ + try { + return Components.classes["@mozilla.org/rdf/container-utils;1"]. + getService(Components.interfaces.nsIRDFContainerUtils); + } catch(ex) { } +} + +const RDF = get_RDF(); +const ContainerUtils = get_ContainerUtils(); + +var xmlDoc; + +function test_template() +{ + var root = document.getElementById("root"); + + var ds; + if (queryType == "rdf" && RDF) { + var ioService = Components.classes["@mozilla.org/network/io-service;1"]. + getService(Components.interfaces.nsIIOService); + + var src = window.location.href.replace(/test_tmpl.*xul/, "animals.rdf"); + ds = RDF.GetDataSourceBlocking(src); + + if (expectLoggedMessages) { + Components.classes["@mozilla.org/consoleservice;1"]. + getService(Components.interfaces.nsIConsoleService).reset(); + } + + if (root.getAttribute("datasources") == "rdf:null") + root.setAttribute("datasources", "animals.rdf"); + } + else if (queryType == "xml") { + var src = window.location.href.replace(/test_tmpl.*xul/, "animals.xml"); + xmlDoc = new XMLHttpRequest(); + xmlDoc.open("get", src, false); + xmlDoc.send(null); + } + + // open menus if necessary + if (needsOpen) + root.open = true; + + if (expectLoggedMessages) + expectLoggedMessages(); + + checkResults(root, 0); + + if (changes.length) { + var usedds = ds; + // within these chrome tests, RDF datasources won't be modifiable unless + // an in-memory datasource is used instead. Call copyRDFDataSource to + // copy the datasource. + if (queryType == "rdf") + usedds = copyRDFDataSource(root, ds); + if (needsOpen) + root.open = true; + setTimeout(iterateChanged, 0, root, usedds); + } + else { + if (needsOpen) + root.open = false; + if (expectedConsoleMessages.length) + compareConsoleMessages(); + SimpleTest.finish(); + } +} + +function iterateChanged(root, ds) +{ + Components.classes["@mozilla.org/consoleservice;1"]. + getService(Components.interfaces.nsIConsoleService).reset(); + + for (var c = 0; c < changes.length; c++) { + changes[c](ds, root); + checkResults(root, c + 1); + } + + if (needsOpen) + root.open = false; + if (expectedConsoleMessages.length) + compareConsoleMessages(); + SimpleTest.finish(); +} + +function checkResults(root, step) +{ + var output = expectedOutput.cloneNode(true); + setForCurrentStep(output, step); + + var error; + var actualoutput = root; + if (isTreeBuilder) { + // convert the tree's view data into the equivalent DOM structure + // for easier comparison + actualoutput = treeViewToDOM(root); + var treechildrenElements = [...output.children].filter((e) => e.localName === "treechildren"); + error = compareOutput(actualoutput, treechildrenElements[0], false); + } + else { + error = compareOutput(actualoutput, output, true); + } + + var adjtestid = testid; + if (step > 0) + adjtestid += " dynamic step " + step; + + var stilltodo = ((step == 0 && notWorkingYet) || (step > 0 && notWorkingYetDynamic)); + if (stilltodo) + todo(false, adjtestid); + else + ok(!error, adjtestid); + + if ((!stilltodo && error) || debug) { + // for debugging, serialize the XML output + var serializedXML = ""; + var rootNodes = actualoutput.childNodes; + for (var n = 0; n < rootNodes.length; n++) { + var node = rootNodes[n]; + if (node.localName != "template") + serializedXML += ((new XMLSerializer()).serializeToString(node)); + } + + // remove the XUL namespace declarations to make the output more readable + const nsrepl = new RegExp("xmlns=\"" + XUL_NS + "\" ", "g"); + serializedXML = serializedXML.replace(nsrepl, ""); + if (debug) + dump("-------- " + adjtestid + " " + error + ":\n" + serializedXML + "\n"); + if (!stilltodo && error) + is(serializedXML, "Same", "Error is: " + error); + } +} + +/** + * Adjust the expected output to acccount for any step attributes. + */ +function setForCurrentStep(content, currentStep) +{ + var todelete = []; + for (var child of content.childNodes) { + if (child.nodeType === Node.ELEMENT_NODE) { + var stepstr = child.getAttribute("step") || ""; + var stepsarr = stepstr.split(","); + for (var s = 0; s < stepsarr.length; s++) { + var step = parseInt(stepsarr[s]); + if ((step > 0 && step > currentStep) || + (step < 0 && -step <= currentStep)) { + todelete.push(child); + } + } + } else if (child.nodeType === Node.TEXT_NODE) { + // Drop empty text nodes. + if (child.nodeValue.trim() === "") + todelete.push(child); + } + } + + for (var e of todelete) + content.removeChild(e); + + for (var child of content.children) { + child.removeAttribute("step"); + setForCurrentStep(child, currentStep); + } +} + +/** + * Compares the 'actual' DOM output with the 'expected' output. This function + * is called recursively, with isroot true if actual refers to the root of the + * template. Returns a null string if they are equal and an error string if + * they are not equal. This function is called recursively as it iterates + * through each node in the DOM tree. + */ +function compareOutput(actual, expected, isroot) +{ + if (isroot && expected.localName != "data") + return "expected must be a <data> element"; + + var t; + + // compare text nodes + if (expected.nodeType == Node.TEXT_NODE) { + if (actual.nodeValue !== expected.nodeValue.trim()) + return "Text " + actual.nodeValue + " doesn't match " + expected.nodeValue; + return ""; + } + + if (!isroot) { + var anyid = false; + // make sure that the tags match + if (actual.localName != expected.localName) + return "Tag name " + expected.localName + " not found"; + + // loop through the attributes in the expected node and compare their + // values with the corresponding attribute on the actual node + + var expectedAttrs = expected.attributes; + for (var a = 0; a < expectedAttrs.length; a++) { + var attr = expectedAttrs[a]; + var expectval = attr.value; + // skip checking the id when anyid="true", however make sure to + // ensure that the id is actually present. + if (attr.name == "anyid" && expectval == "true") { + anyid = true; + if (!actual.hasAttribute("id")) + return "expected id attribute"; + } + else if (actual.getAttribute(attr.name) != expectval) { + return "attribute " + attr.name + " is '" + + actual.getAttribute(attr.name) + "' instead of '" + expectval + "'"; + } + } + + // now loop through the actual attributes and make sure that there aren't + // any extra attributes that weren't expected + var length = actual.attributes.length; + for (t = 0; t < length; t++) { + var aattr = actual.attributes[t]; + var expectval = expected.getAttribute(aattr.name); + // ignore some attributes that don't matter + if (expectval != actual.getAttribute(aattr.name) && + aattr.name != "staticHint" && aattr.name != "xmlns" && + (aattr.name != "id" || !anyid)) + return "extra attribute " + aattr.name; + } + } + + // ensure that the node has the right number of children. Subtract one for + // the root node to account for the <template> node. + length = actual.childNodes.length - (isroot ? 1 : 0); + if (length != expected.childNodes.length) + return "incorrect child node count of " + actual.localName + " " + length + + " expected " + expected.childNodes.length; + + // if <data unordered="true"> is used, then the child nodes may be in any order + var unordered = (expected.localName == "data" && expected.getAttribute("unordered") == "true"); + + // next, loop over the children and call compareOutput recursively on each one + var adj = 0; + for (t = 0; t < actual.childNodes.length; t++) { + var actualnode = actual.childNodes[t]; + // skip the <template> element, and add one to the indices when looking + // at the later nodes to account for it + if (isroot && actualnode.localName == "template") { + adj++; + } + else { + var output = "unexpected"; + if (unordered) { + var expectedChildren = expected.childNodes; + for (var e = 0; e < expectedChildren.length; e++) { + output = compareOutput(actualnode, expectedChildren[e], false); + if (!output) + break; + } + } + else { + output = compareOutput(actualnode, expected.childNodes[t - adj], false); + } + + // an error was returned, so return early + if (output) + return output; + } + } + + return ""; +} + +/* + * copy the datasource into an in-memory datasource so that it can be modified + */ +function copyRDFDataSource(root, sourceds) +{ + var dsourcesArr = []; + var composite = root.database; + var dsources = composite.GetDataSources(); + while (dsources.hasMoreElements()) { + sourceds = dsources.getNext().QueryInterface(Components.interfaces.nsIRDFDataSource); + dsourcesArr.push(sourceds); + } + + for (var d = 0; d < dsourcesArr.length; d++) + composite.RemoveDataSource(dsourcesArr[d]); + + var newds = Components.classes["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"]. + createInstance(Components.interfaces.nsIRDFDataSource); + + var sourcelist = sourceds.GetAllResources(); + while (sourcelist.hasMoreElements()) { + var source = sourcelist.getNext(); + var props = sourceds.ArcLabelsOut(source); + while (props.hasMoreElements()) { + var prop = props.getNext(); + if (prop instanceof Components.interfaces.nsIRDFResource) { + var targets = sourceds.GetTargets(source, prop, true); + while (targets.hasMoreElements()) + newds.Assert(source, prop, targets.getNext(), true); + } + } + } + + composite.AddDataSource(newds); + root.builder.rebuild(); + + return newds; +} + +/** + * Converts a tree view (nsITreeView) into the equivalent DOM tree. + * Returns the treechildren + */ +function treeViewToDOM(tree) +{ + var treechildren = document.createElement("treechildren"); + + if (tree.view) + treeViewToDOMInner(tree.columns, treechildren, tree.view, tree.builder, 0, 0); + + return treechildren; +} + +function treeViewToDOMInner(columns, treechildren, view, builder, start, level) +{ + var end = view.rowCount; + + for (var i = start; i < end; i++) { + if (view.getLevel(i) < level) + return i - 1; + + var id = builder ? builder.getResourceAtIndex(i).Value : "id" + i; + var item = document.createElement("treeitem"); + item.setAttribute("id", id); + treechildren.appendChild(item); + + var row = document.createElement("treerow"); + item.appendChild(row); + + for (var c = 0; c < columns.length; c++) { + var cell = document.createElement("treecell"); + var label = view.getCellText(i, columns[c]); + if (label) + cell.setAttribute("label", label); + row.appendChild(cell); + } + + if (view.isContainer(i)) { + item.setAttribute("container", "true"); + item.setAttribute("empty", view.isContainerEmpty(i) ? "true" : "false"); + + if (!view.isContainerEmpty(i) && view.isContainerOpen(i)) { + item.setAttribute("open", "true"); + + var innertreechildren = document.createElement("treechildren"); + item.appendChild(innertreechildren); + + i = treeViewToDOMInner(columns, innertreechildren, view, builder, i + 1, level + 1); + } + } + } + + return i; +} + +function expectConsoleMessage(ref, id, isNew, isActive, extra) +{ + var message = "In template with id root" + + (ref ? " using ref " + ref : "") + "\n " + + (isNew ? "New " : "Removed ") + (isActive ? "active" : "inactive") + + " result for query " + extra + ": " + id; + expectedConsoleMessages.push(message); +} + +function compareConsoleMessages() +{ + var consoleService = Components.classes["@mozilla.org/consoleservice;1"]. + getService(Components.interfaces.nsIConsoleService); + var messages = consoleService.getMessageArray() || []; + messages = messages.map(m => m.message); + // Copy to avoid modifying expectedConsoleMessages + var expect = expectedConsoleMessages.concat(); + for (var m = 0; m < messages.length; m++) { + if (messages[m] == expect[0]) { + ok(true, "found message " + expect.shift()); + } + } + if (expect.length != 0) { + ok(false, "failed to find expected console messages: " + expect); + } +} + +function copyToProfile(filename) +{ + if (Cc === undefined) { + var Cc = Components.classes; + var Ci = Components.interfaces; + } + + var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"] + .getService(Ci.mozIJSSubScriptLoader); + loader.loadSubScript("chrome://mochikit/content/chrome-harness.js"); + + var file = Cc["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIProperties) + .get("ProfD", Ci.nsIFile); + file.append(filename); + + var parentURI = getResolvedURI(getRootDirectory(window.location.href)); + if (parentURI.JARFile) { + parentURI = extractJarToTmp(parentURI); + } else { + var fileHandler = Cc["@mozilla.org/network/protocol;1?name=file"]. + getService(Ci.nsIFileProtocolHandler); + parentURI = fileHandler.getFileFromURLSpec(parentURI.spec); + } + + parentURI = parentURI.QueryInterface(Ci.nsILocalFile); + parentURI.append(filename); + try { + var retVal = parentURI.copyToFollowingLinks(file.parent, filename); + } catch (ex) { + //ignore this error as the file could exist already + } +} diff --git a/dom/xul/templates/tests/chrome/test_bug329335.xul b/dom/xul/templates/tests/chrome/test_bug329335.xul new file mode 100644 index 000000000..75190c17b --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_bug329335.xul @@ -0,0 +1,28 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:svg="http://www.w3.org/2000/svg"> + + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + <script> + + SimpleTest.waitForExplicitFinish(); + + function init() + { + document.documentElement.appendChild(document.getElementById("svg")); + ok(true, "Didn't crash"); + SimpleTest.finish(); + } + + window.addEventListener("load", init, false); + + </script> + + + <svg:svg datasources="" id="svg"/> + + +</window> diff --git a/dom/xul/templates/tests/chrome/test_bug330010.xul b/dom/xul/templates/tests/chrome/test_bug330010.xul new file mode 100644 index 000000000..67cc81931 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_bug330010.xul @@ -0,0 +1,51 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:html="http://www.w3.org/1999/xhtml" + onload="boom();"> +<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> +<script type="text/javascript"> +<![CDATA[ + +SimpleTest.waitForExplicitFinish(); +function boom() +{ + const RDF = Components.classes["@mozilla.org/rdf/rdf-service;1"]. + getService(Components.interfaces.nsIRDFService); + var src = window.location.href.replace(/test_bug330010.xul/, "file_bug330010.rdf"); + + var ds = RDF.GetDataSourceBlocking(src); + + var s = document.getElementById("s"); + s.setAttribute("datasources", "file_bug330010.rdf"); + + var x = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "hbox"); + var generatedShape = s.childNodes[3]; + generatedShape.appendChild(x); + document.documentElement.removeChild(document.getElementById("s")); + ok(true, "Didn't crash"); + SimpleTest.finish(); +} + +]]> +</script> + + <html:div datasources="rdf:null" ref="urn:root" flex="1" id="s"> + <template> + <rule> + <conditions> + <content uri="?root"/> + <triple subject="?root" + predicate="urn:croczilla:xulsvg1:shapes" + object="?shapes"/> + <member container="?shapes" child="?shape" id="m"/> + </conditions> + <action> + <hbox id="p" uri="?shape" /> + </action> + </rule> + </template> + </html:div> +</window> diff --git a/dom/xul/templates/tests/chrome/test_bug397148.xul b/dom/xul/templates/tests/chrome/test_bug397148.xul new file mode 100644 index 000000000..0f15cfa29 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_bug397148.xul @@ -0,0 +1,19 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" onload="boom();"> +<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + +<script type="text/javascript"> +SimpleTest.waitForExplicitFinish(); + +function boom() { + ok(true, "Didn't crash"); + SimpleTest.finish(); +} +</script> + + +<html:span datasources="0" /> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_bug441785.xul b/dom/xul/templates/tests/chrome/test_bug441785.xul new file mode 100644 index 000000000..7be6df198 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_bug441785.xul @@ -0,0 +1,148 @@ +<?xml version="1.0" ?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <tree flex="20" id="t" ref="urn:data:row" datasources="rdf:null" seltype="single"> + <treecols> + <treecol flex="1" id="id" label="id" sort="rdf:http://dummy/rdf#id" /> + <splitter class="tree-splitter"/> + <treecol flex="1" id="title" label="title" sort="rdf:http://dummy/rdf#title" sortActive="true" sortDirection="ascending" /><splitter class="tree-splitter"/> + </treecols> + <template> + <treechildren> + <treeitem uri="rdf:*" seltype="single"> + <treerow > + <treecell label="rdf:http://dummy/rdf#id"/> + <treecell label="rdf:http://dummy/rdf#title"/> + </treerow> + </treeitem> + </treechildren> + </template> + </tree> + <tree flex="20" id="tc" ref="urn:data:row" datasources="rdf:null" seltype="single" flags="dont-build-content"> + <treecols> + <treecol flex="1" id="idc" label="id" sort="rdf:http://dummy/rdf#id" /> + <splitter class="tree-splitter"/> + <treecol flex="1" id="titlec" label="title" sort="rdf:http://dummy/rdf#title" sortActive="true" sortDirection="ascending" /><splitter class="tree-splitter"/> + </treecols> + <template> + <treechildren> + <treeitem uri="rdf:*" seltype="single"> + <treerow > + <treecell label="rdf:http://dummy/rdf#id"/> + <treecell label="rdf:http://dummy/rdf#title"/> + </treerow> + </treeitem> + </treechildren> + </template> + </tree> + +<body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + +<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + +<script type="application/x-javascript"> +<![CDATA[ + +var buildCount = 0; + +SimpleTest.waitForExplicitFinish(); + +var TemplateBuilderListener = { + willRebuild: function(aBuilder) { + }, + + didRebuild: function(aBuilder) { + ++buildCount; + var remove = false; + if (buildCount == 2) { + remove =true; + setTimeout(nextDataSource, 0); + } else if (buildCount == 4) { + remove = true; + setTimeout(continueTest, 0); + } + if (remove) { + var tree = document.getElementById('t'); + var treec = document.getElementById('tc'); + tree.builder.removeListener(TemplateBuilderListener); + treec.builder.removeListener(TemplateBuilderListener); + } + }, + + QueryInterface: function (aIID) + { + if (!aIID.equals(Components.interfaces.nsIXULBuilderListener) && + !aIID.equals(Components.interfaces.nsISupports)) + throw Components.results.NS_ERROR_NO_INTERFACE; + return this; + } +}; + +function runTest() { + var tree = document.getElementById('t'); + var treec = document.getElementById('tc'); + + try { + var rdfService = Components.classes["@mozilla.org/rdf/rdf-service;1"]. + getService(Components.interfaces.nsIRDFService); + + var s1 = window.location.href.replace(/test_bug441785.xul/, "bug441785-1.rdf"); + var ds1 = rdfService.GetDataSourceBlocking(s1); + + var s2 = window.location.href.replace(/test_bug441785.xul/, "bug441785-2.rdf"); + var ds2 = rdfService.GetDataSourceBlocking(s2); + } catch (ex) { } + + tree.builder.addListener(TemplateBuilderListener); + treec.builder.addListener(TemplateBuilderListener); + tree.setAttribute('datasources', 'bug441785-1.rdf'); + treec.setAttribute('datasources', 'bug441785-1.rdf'); +} + +var oldtreefirstrow, oldtreecfirstrow; + +function nextDataSource() +{ + var tree = document.getElementById('t'); + var treec = document.getElementById('tc'); + tree.treeBoxObject.scrollToRow(10); + treec.treeBoxObject.scrollToRow(10); + + is(tree.treeBoxObject.getFirstVisibleRow(), 10, "first tree row count datasource 1"); + is(treec.treeBoxObject.getFirstVisibleRow(), 10, "second tree row count datasource 1"); + + tree.builder.addListener(TemplateBuilderListener); + treec.builder.addListener(TemplateBuilderListener); + tree.setAttribute('datasources', 'bug441785-2.rdf'); + treec.setAttribute('datasources', 'bug441785-2.rdf'); +} + +function continueTest() { + var tree = document.getElementById('t'); + var treec = document.getElementById('tc'); + + is(tree.treeBoxObject.getFirstVisibleRow(), 0, "first tree row count datasource 2"); + is(treec.treeBoxObject.getFirstVisibleRow(), 0, "second tree row count datasource 2"); + + try { + window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsIDOMWindowUtils) + .garbageCollect(); + } + catch (e) { } + + // Hit the bug, crash + // (not exactly the same kind of crash as 441785, but from the same cause) + tree.parentNode.removeChild(tree); + treec.parentNode.removeChild(treec); + + SimpleTest.finish(); +} + +window.addEventListener("load", runTest, false); + +]]> +</script> +</window> diff --git a/dom/xul/templates/tests/chrome/test_bug476634.xul b/dom/xul/templates/tests/chrome/test_bug476634.xul new file mode 100644 index 000000000..cee01d41a --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_bug476634.xul @@ -0,0 +1,76 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet + href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=476634 +--> +<window title="Mozilla Bug 476634" onload="startup()" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + +<body xmlns="http://www.w3.org/1999/xhtml"> +<a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=304188">Mozilla Bug 476634</a> +<p id="display"></p> +<div id="content" style="display: none"> +</div> +<pre id="test"> +</pre> +</body> +<template id="test-template"> + <query>SELECT id,value FROM test</query> + <action> + <label uri="?" id="?id" value="?value"/> + </action> +</template> +<vbox id="results-list" datasources="rdf:null" querytype="storage" ref="*" + template="test-template"/> + +<script class="testbody" type="application/javascript"> +<![CDATA[ +function startup() { + var ss = Components.classes["@mozilla.org/storage/service;1"] + .getService(Components.interfaces.mozIStorageService); + var db = ss.openSpecialDatabase("memory"); + + db.createTable("test", "id TEXT, value INTEGER"); + var stmt = db.createStatement("INSERT INTO test (id, value) VALUES (?,?)"); + stmt.bindByIndex(0, "test1"); + stmt.bindByIndex(1, 0); + stmt.execute(); + stmt.bindByIndex(0, "test2"); + stmt.bindByIndex(1, 2147483647); + stmt.execute(); + stmt.bindByIndex(0, "test3"); + stmt.bindByIndex(1, -2147483648); + stmt.execute(); + stmt.bindByIndex(0, "test4"); + stmt.bindByIndex(1, 0); + stmt.execute(); + stmt.bindByIndex(0, "test5"); + stmt.bindByIndex(1, 3147483647); + stmt.execute(); + stmt.bindByIndex(0, "test6"); + stmt.bindByIndex(1, -3147483648); + stmt.execute(); + stmt.finalize(); + + var list = document.getElementById("results-list"); + list.builder.datasource = db; + + is(list.childNodes.length, 6, "Should be 6 generated elements"); + is(list.childNodes[0].value, "0", "Should have seen the correct value"); + is(list.childNodes[1].value, "2147483647", "Should have seen the correct value"); + is(list.childNodes[2].value, "-2147483648", "Should have seen the correct value"); + is(list.childNodes[3].value, "0", "Should have seen the correct value"); + is(list.childNodes[4].value, "3147483647", "Should have seen the correct value"); + is(list.childNodes[5].value, "-3147483648", "Should have seen the correct value"); +} +]]> +</script> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_sortservice.xul b/dom/xul/templates/tests/chrome/test_sortservice.xul new file mode 100644 index 000000000..af1de3f0e --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_sortservice.xul @@ -0,0 +1,70 @@ +<?xml version="1.0" ?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + +<vbox id="box"/> + +<body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + +<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + +<script type="application/x-javascript"> +<![CDATA[ + +var tests = [ + [["One", "Two", "Three", "Four"], "", "Four One Three Two"], + [["One", "Two", "Three", "Four"], "integer", "Four One Three Two"], + [["One", "Two", "Three", "Four"], "descending", "Two Three One Four"], + [["One", "Two", "Three", "Four"], "descending integer", "Two Three One Four"], + [["One", "Two", "Three", "Four"], "integer cat descending", "Two Three One Four"], + [["1", "13", "2", "7", "12", "240", "2", "170", "222", "98"], "", "1 12 13 170 2 2 222 240 7 98"], + [["1", "13", "2", "7", "12", "240", "2", "170", "222", "98"], "integer", "1 2 2 7 12 13 98 170 222 240"], + [["1", "13", "2", "7", "12", "240", "2", "170", "222", "98"], "ascending integer", "1 2 2 7 12 13 98 170 222 240"], + [["1", "13", "2", "7", "12", "240", "2", "170", "222", "98"], "integer descending", "240 222 170 98 13 12 7 2 2 1"], + [["Cat", "cat", "Candy", "candy"], "comparecase", "Candy Cat candy cat"], + [["1", "102", "22", "One", "40", "Two"], "integer", "1 22 40 102 One Two"], +]; + +SimpleTest.waitForExplicitFinish(); + +function doTests() +{ + var box = document.getElementById("box"); + + const sortService = Components.classes["@mozilla.org/xul/xul-sort-service;1"]. + getService(Components.interfaces.nsIXULSortService); + + for (let t = 0; t < tests.length; t++) { + var test = tests[t]; + + for (let e = 0; e < test[0].length; e++) { + var label = document.createElement("label"); + label.setAttribute("value", test[0][e]); + box.appendChild(label); + } + + sortService.sort(box, "value", test[1]); + + var actual = ""; + for (let e = 0; e < box.childNodes.length; e++) { + if (actual) + actual += " "; + actual += box.childNodes[e].getAttribute("value"); + } + is(actual, test[2], "sorted step " + (t + 1)); + + while(box.hasChildNodes()) + box.removeChild(box.firstChild); + box.removeAttribute("sortDirection"); + } + + SimpleTest.finish(); +} + +window.addEventListener("load", doTests, false); + +]]> +</script> +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_bindingsextendedsyntax.xul b/dom/xul/templates/tests/chrome/test_tmpl_bindingsextendedsyntax.xul new file mode 100644 index 000000000..42e9a1633 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_bindingsextendedsyntax.xul @@ -0,0 +1,73 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + bindings - extended syntax +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label step="-2" id="http://www.some-fictitious-zoo.com/humans/sarah" value="Sarah "/> + <label step="2" id="http://www.some-fictitious-zoo.com/humans/sarah" value="Sarah Yarmouth"/> + <label step="-1" id="http://www.some-fictitious-zoo.com/humans/robert" value="Robert Sanderson"/> + <label step="1" id="http://www.some-fictitious-zoo.com/humans/robert" value="Robert "/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="bindings - extended syntax"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + targetds.Unassert(RDF.GetResource(ZOO_NS + 'humans/robert'), + RDF.GetResource(ZOO_NS + 'rdf#lastName'), + RDF.GetLiteral('Sanderson'), true); + }, + // step 2 + function(targetds, root) { + targetds.Assert(RDF.GetResource(ZOO_NS + 'humans/sarah'), + RDF.GetResource(ZOO_NS + 'rdf#lastName'), + RDF.GetLiteral('Yarmouth'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/humans"> +<template id="template"> +<rule> +<conditions> +<content uri="?uri"/> +<member container="?uri" child="?child"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</conditions> +<bindings> +<binding subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#lastName" object="?lastname"/> +</bindings> +<action> +<label uri="?child" value="?name ?lastname"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_bindingsmultiple.xul b/dom/xul/templates/tests/chrome/test_tmpl_bindingsmultiple.xul new file mode 100644 index 000000000..2af4f89aa --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_bindingsmultiple.xul @@ -0,0 +1,80 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + bindings - multiple +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <button id="http://www.some-fictitious-zoo.com/arachnids" container="true" empty="false" label="Arachnids "/> + <button step="-2" id="http://www.some-fictitious-zoo.com/birds" container="true" empty="false" label="Birds Sarah "/> + <button step="2" id="http://www.some-fictitious-zoo.com/birds" container="true" empty="false" label="Birds Sarah Yarmouth"/> + <button step="-1" id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="true" label="Crustaceans Robert Sanderson"/> + <button step="1" id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="true" label="Crustaceans Robert "/> + <button id="http://www.some-fictitious-zoo.com/fish" container="true" empty="false" label="Fish "/> + <button id="http://www.some-fictitious-zoo.com/mammals" container="true" empty="false" label="Mammals "/> + <button step="-1" id="http://www.some-fictitious-zoo.com/reptiles" container="true" empty="false" label="Reptiles Robert Sanderson"/> + <button step="1" id="http://www.some-fictitious-zoo.com/reptiles" container="true" empty="false" label="Reptiles Robert "/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="bindings - multiple"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + targetds.Unassert(RDF.GetResource(ZOO_NS + 'humans/robert'), + RDF.GetResource(ZOO_NS + 'rdf#lastName'), + RDF.GetLiteral('Sanderson'), true); + }, + // step 2 + function(targetds, root) { + targetds.Assert(RDF.GetResource(ZOO_NS + 'humans/sarah'), + RDF.GetResource(ZOO_NS + 'rdf#lastName'), + RDF.GetLiteral('Yarmouth'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/all-animals"> +<template> +<query> +<content uri="?uri"/> +<member container="?uri" child="?child"/> +</query> +<rule> +<bindings> +<binding subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#keeper" object="?keeper"/> +<binding subject="?keeper" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?keepername"/> +<binding subject="?keeper" predicate="http://www.some-fictitious-zoo.com/rdf#lastName" object="?lastname"/> +<binding subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</bindings> +<action> +<button uri="?child" label="?name ?keepername ?lastname"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_bindingsquerysyntax.xul b/dom/xul/templates/tests/chrome/test_tmpl_bindingsquerysyntax.xul new file mode 100644 index 000000000..d8b6110be --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_bindingsquerysyntax.xul @@ -0,0 +1,73 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + bindings - query syntax +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label step="-2" id="http://www.some-fictitious-zoo.com/humans/sarah" value="First Name: Sarah Last Name: "/> + <label step="2" id="http://www.some-fictitious-zoo.com/humans/sarah" value="First Name: Sarah Last Name: Yarmouth"/> + <label step="-1" id="http://www.some-fictitious-zoo.com/humans/robert" value="First Name: Robert Last Name: Sanderson"/> + <label step="1" id="http://www.some-fictitious-zoo.com/humans/robert" value="First Name: Robert Last Name: "/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="bindings - query syntax"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + targetds.Unassert(RDF.GetResource(ZOO_NS + 'humans/robert'), + RDF.GetResource(ZOO_NS + 'rdf#lastName'), + RDF.GetLiteral('Sanderson'), true); + }, + // step 2 + function(targetds, root) { + targetds.Assert(RDF.GetResource(ZOO_NS + 'humans/sarah'), + RDF.GetResource(ZOO_NS + 'rdf#lastName'), + RDF.GetLiteral('Yarmouth'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/humans"> +<template id="template"> +<query> +<content uri="?uri"/> +<member container="?uri" child="?child"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule id="rule"> +<bindings id="bindings"> +<binding subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#lastName" object="?lastname"/> +</bindings> +<action> +<label uri="?child" value="First Name: ?name Last Name: ?lastname"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_bindingsreversed.xul b/dom/xul/templates/tests/chrome/test_tmpl_bindingsreversed.xul new file mode 100644 index 000000000..a7bd190a7 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_bindingsreversed.xul @@ -0,0 +1,58 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + bindings - reversed +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/humans/sarah" value="First Name: Sarah Last Name: "/> + <label id="http://www.some-fictitious-zoo.com/humans/robert" value="First Name: Robert Last Name: "/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="bindings - reversed"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/humans"> +<template id="template"> +<query> +<content uri="?uri"/> +<member container="?uri" child="?child"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule id="rule"> +<bindings id="bindings"> +<binding subject="?lastname" predicate="http://www.some-fictitious-zoo.com/rdf#lastName" object="?child"/> +</bindings> +<action> +<label uri="?child" value="First Name: ?name Last Name: ?lastname"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_bindingssameastriple.xul b/dom/xul/templates/tests/chrome/test_tmpl_bindingssameastriple.xul new file mode 100644 index 000000000..514979d59 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_bindingssameastriple.xul @@ -0,0 +1,58 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + bindings - same as triple +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/humans/sarah" value="First Name: Sarah Last Name: "/> + <label id="http://www.some-fictitious-zoo.com/humans/robert" value="First Name: Robert Last Name: "/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="bindings - same as triple"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/humans"> +<template id="template"> +<query> +<content uri="?uri"/> +<member container="?uri" child="?child"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule id="rule"> +<bindings id="bindings"> +<binding subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</bindings> +<action> +<label uri="?child" value="First Name: ?name Last Name: ?lastname"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_containerandmembervariablechanged.xul b/dom/xul/templates/tests/chrome/test_tmpl_containerandmembervariablechanged.xul new file mode 100644 index 000000000..7b90d64b1 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_containerandmembervariablechanged.xul @@ -0,0 +1,88 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + container and member variable changed +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <button step="3" id="http://www.some-fictitious-zoo.com/birds/wren" label="http://www.some-fictitious-zoo.com/birds http://www.some-fictitious-zoo.com/birds/wren"/> + <button id="http://www.some-fictitious-zoo.com/birds/emu" label="http://www.some-fictitious-zoo.com/birds http://www.some-fictitious-zoo.com/birds/emu"/> + <button step="-4" id="http://www.some-fictitious-zoo.com/birds/barnowl" label="http://www.some-fictitious-zoo.com/birds http://www.some-fictitious-zoo.com/birds/barnowl"/> + <button id="http://www.some-fictitious-zoo.com/birds/raven" label="http://www.some-fictitious-zoo.com/birds http://www.some-fictitious-zoo.com/birds/raven"/> + <button step="2" id="http://www.some-fictitious-zoo.com/birds/archaeopteryx" label="http://www.some-fictitious-zoo.com/birds http://www.some-fictitious-zoo.com/birds/archaeopteryx"/> + <button step="1" id="http://www.some-fictitious-zoo.com/birds/emperorpenguin" label="http://www.some-fictitious-zoo.com/birds http://www.some-fictitious-zoo.com/birds/emperorpenguin"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="container and member variable changed"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/emperorpenguin'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Emperor Penguin'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Archaeopteryx'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/wren'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Wren'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + var removednode = container.RemoveElementAt('3', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Barn Owl'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds"> +<template container="?parent" member="?child"> +<rule> +<button uri="?" label="?parent ?child"/> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_containervariablechanged.xul b/dom/xul/templates/tests/chrome/test_tmpl_containervariablechanged.xul new file mode 100644 index 000000000..f9100047f --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_containervariablechanged.xul @@ -0,0 +1,49 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + container variable changed +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <button id="http://www.some-fictitious-zoo.com/birds/emu" label="http://www.some-fictitious-zoo.com/birds "/> + <button id="http://www.some-fictitious-zoo.com/birds/barnowl" label="http://www.some-fictitious-zoo.com/birds "/> + <button id="http://www.some-fictitious-zoo.com/birds/raven" label="http://www.some-fictitious-zoo.com/birds "/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="container variable changed"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds"> +<template container="?parent"> +<rule> +<button uri="?" label="?parent ?child"/> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_containmentattribute.xul b/dom/xul/templates/tests/chrome/test_tmpl_containmentattribute.xul new file mode 100644 index 000000000..909a05e34 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_containmentattribute.xul @@ -0,0 +1,71 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + containment attribute +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output" unordered="true"> + <checkbox step="-1" id="http://www.some-fictitious-zoo.com/arachnids/tarantula" label="Tarantula"/> + <checkbox step="2" id="http://www.some-fictitious-zoo.com/mammals/lion" label="Lion"/> + <checkbox id="http://www.some-fictitious-zoo.com/reptiles/anaconda" label="Anaconda"/> + <checkbox id="http://www.some-fictitious-zoo.com/reptiles/chameleon" label="Chameleon"/> + <checkbox id="http://www.some-fictitious-zoo.com/mammals/africanelephant" label="African Elephant"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="containment attribute"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + targetds.Unassert(RDF.GetResource(ZOO_NS + 'humans/robert'), + RDF.GetResource(ZOO_NS + 'rdf#favoriteAnimal'), + RDF.GetResource(ZOO_NS + 'arachnids/tarantula'), true); + }, + // step 2 + function(targetds, root) { + targetds.Assert(RDF.GetResource(ZOO_NS + 'humans/robert'), + RDF.GetResource(ZOO_NS + 'rdf#favoriteAnimal'), + RDF.GetResource(ZOO_NS + 'mammals/lion'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/humans/robert" containment="http://www.some-fictitious-zoo.com/rdf#favoriteAnimal"> +<template id="template"> +<rule id="rule"> +<conditions> +<content uri="?uri"/> +<member container="?uri" child="?child"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</conditions> +<action> +<checkbox uri="?child" label="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_defaultcontainervariableisuri.xul b/dom/xul/templates/tests/chrome/test_tmpl_defaultcontainervariableisuri.xul new file mode 100644 index 000000000..933c51bc0 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_defaultcontainervariableisuri.xul @@ -0,0 +1,49 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + default container variable is ?uri +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <button id="http://www.some-fictitious-zoo.com/birds/emu" label="http://www.some-fictitious-zoo.com/birds"/> + <button id="http://www.some-fictitious-zoo.com/birds/barnowl" label="http://www.some-fictitious-zoo.com/birds"/> + <button id="http://www.some-fictitious-zoo.com/birds/raven" label="http://www.some-fictitious-zoo.com/birds"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="default container variable is ?uri"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds"> +<template> +<rule> +<button uri="?uri" label="?uri"/> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_errors.xul b/dom/xul/templates/tests/chrome/test_tmpl_errors.xul new file mode 100644 index 000000000..f3b58502d --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_errors.xul @@ -0,0 +1,280 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + tests for templates with invalid syntax +--> + +<window title="XUL Invalid Template Tests" width="500" height="600" + onload="runTest();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var consoleService = Components.classes["@mozilla.org/consoleservice;1"]. + getService(Components.interfaces.nsIConsoleService); + +function checkConsole(expectedError) +{ + var message = consoleService.getMessageArray()[0].message; + is(message, expectedError, "logged message " + expectedError); +} + +// each test consists of a pre function executed before the template build, an +// expected error message, and a post function executed after the template build +var tests = [ + +// <queryset> used in invalid location +{ + pre: template => template.insertBefore(document.createElement("queryset"), template.lastChild), + error: "Error parsing template: unexpected <queryset> element", + post: queryset => queryset.parentNode.removeChild(queryset) +}, + +// no member variable found +{ + pre: template => $("action").firstChild.removeAttribute("uri"), + error: "Error parsing template: no member variable found. Action body should have an element with uri attribute", + post: () => $("action").firstChild.setAttribute("uri", "?child") +}, + +// bad binding subject +{ + pre: template => $("binding").removeAttribute("subject"), + error: "Error parsing template: <binding> requires a variable for its subject attribute", + post: () => $("binding").setAttribute("subject", "?child"), +}, + +// bad binding predicate +{ + pre: template => $("binding").removeAttribute("predicate"), + error: "Error parsing template: <binding> element is missing a predicate attribute", + post: () => $("binding").setAttribute("predicate", "http://www.some-fictitious-zoo.com/rdf#name"), +}, + +// bad binding object +{ + pre: template => $("binding").setAttribute("object", "blah"), + error: "Error parsing template: <binding> requires a variable for its object attribute", + post: () => $("binding").setAttribute("object", "?name"), +}, + +// where condition missing a subject +{ + pre: function(template) { var rule = $("rule"); + var where = document.createElement("where"); + where.setAttribute("subject", ""); + where.setAttribute("rel", "equals"); + where.setAttribute("value", "Raven"); + rule.appendChild(where); + return where; }, + error: "Error parsing template: <where> element is missing a subject attribute", + post: function(where) { where.parentNode.removeChild(where); } +}, + +// where condition missing a rel +{ + pre: function(template) { var rule = $("rule"); + var where = document.createElement("where"); + where.setAttribute("subject", "?name"); + where.setAttribute("rel", ""); + where.setAttribute("value", "Raven"); + rule.appendChild(where); + return where; }, + error: "Error parsing template: <where> element is missing a rel attribute", + post: function(where) { where.parentNode.removeChild(where); } +}, + +// where condition missing a value +{ + pre: function(template) { var rule = $("rule"); + var where = document.createElement("where"); + where.setAttribute("subject", "?name"); + where.setAttribute("rel", "equals"); + where.setAttribute("value", ""); + rule.appendChild(where); + return where; }, + error: "Error parsing template: <where> element is missing a value attribute", + post: function(where) { where.parentNode.removeChild(where); } +}, + +// where condition missing a variable +{ + pre: function(template) { var rule = $("rule"); + var where = document.createElement("where"); + where.setAttribute("subject", "name"); + where.setAttribute("rel", "equals"); + where.setAttribute("value", "Raven"); + rule.appendChild(where); + return where; }, + error: "Error parsing template: <where> element must have at least one variable as a subject or value", + post: function(where) { where.parentNode.removeChild(where); } +}, + +// bad member container +{ + pre: template => $("member").setAttribute("container", "blah"), + error: "Error parsing template: <member> requires a variable for its container attribute", + post: () => $("member").setAttribute("container", "?uri"), +}, + +// bad member child +{ + pre: template => $("member").setAttribute("child", "blah"), + error: "Error parsing template: <member> requires a variable for its child attribute", + post: () => $("member").setAttribute("child", "?child"), +}, + +// bad triple subject +{ + pre: template => $("triple").removeAttribute("subject"), + error: "Error parsing template: <triple> requires a variable for its subject attribute", + post: () => $("triple").setAttribute("subject", "?child"), +}, + +// missing triple predicate +{ + pre: template => $("triple").removeAttribute("predicate"), + error: "Error parsing template: <triple> should have a non-variable value as a predicate", + post: () => $("triple").setAttribute("predicate", "http://www.some-fictitious-zoo.com/rdf#name"), +}, + +// bad triple predicate +{ + pre: template => $("triple").setAttribute("predicate", "?predicate"), + error: "Error parsing template: <triple> should have a non-variable value as a predicate", + post: () => $("triple").setAttribute("predicate", "http://www.some-fictitious-zoo.com/rdf#name"), +}, + +// bad triple object +{ + pre: template => $("triple").removeAttribute("object"), + error: "Error parsing template: <triple> requires a variable for its object attribute", + post: () => $("triple").setAttribute("object", "?name"), +}, + +// content not first element in query +{ + pre: function(template) { var content = $("content"); content.parentNode.appendChild(content); return content; }, + error: "Error parsing template: expected <content> to be first", + post: content => content.parentNode.insertBefore(content, content.parentNode.firstChild), +}, + +// member container variable not bound +{ + pre: template => $("member").removeAttribute("container"), + error: "Error parsing template: neither container or child variables of <member> has a value", + post: () => $("member").setAttribute("container", "?uri"), +}, + +// neither triple subject or object variable are bound +{ + pre: template => $("triple").setAttribute("subject", "?blah"), + error: "Error parsing template: neither subject or object variables of <triple> has a value", + post: () => $("triple").setAttribute("subject", "?child"), +}, + +// neither triple subject or object variable are bound +{ + pre: function(template) { var triple = $("triple"); triple.setAttribute("subject", "blah"); + triple.setAttribute("object", "blah"); }, + error: "Error parsing template: <triple> should have at least one variable as a subject or object", + post: function() { var triple = $("triple"); triple.setAttribute("subject", "?uri"); + triple.setAttribute("object", "?uri") } +}, + +// could not parse xml query expression +{ + firstXMLTest: true, + pre: function(template) { $("query").setAttribute("expr", "something()"); }, + error: "Error parsing template: XPath expression in query could not be parsed", + post: function() { } +}, + +// could not parse xml assign expression +{ + pre: function(template) { var query = $("query"); + query.setAttribute("expr", "*"); + var assign = document.createElement("assign"); + assign.setAttribute("var", "?name"); + assign.setAttribute("expr", "something()"); + query.appendChild(assign); + return assign; }, + error: "Error parsing template: XPath expression in <assign> could not be parsed", + post: function(assign) { assign.parentNode.removeChild(assign); } +}, + +// could not parse xml binding expression +{ + pre: function(template) { $("binding").setAttribute("predicate", "something()"); }, + error: "Error parsing template: XPath expression in <binding> could not be parsed", + post: function() { $("binding").setAttribute("predicate", "[name]"); }, +}, + +]; + +function runTest() +{ + var root = $("root"); + var template = $("template"); + while (test = tests.shift()) { + consoleService.reset(); + var context = test.pre(template); + root.builder.rebuild(); + checkConsole(test.error); + test.post(context); + + // preload and set up for the xml datasource query error tests + if (tests.length && tests[0].firstXMLTest) { + var src = window.location.href.replace(/test_tmpl.*xul/, "animals.xml"); + xmlDoc = new XMLHttpRequest(); + xmlDoc.open("get", src, false); + xmlDoc.send(null); + + var root = $("root"); + root.setAttribute("querytype", "xml"); + root.setAttribute("datasources", "animals.xml"); + $("binding").setAttribute("predicate", "[name]"); + + function waitForDatasource() { + // wait for the datasource to be available before continuing the test + if (root.builder.datasource instanceof XMLDocument) + runTest(); + else + setTimeout(waitForDatasource, 100); + } + + setTimeout(waitForDatasource, 0); + return; + } + } + SimpleTest.finish(); +} + +]]> +</script> + +<vbox id="root" datasources="animals.rdf" ref="http://www.some-fictitious-zoo.com/birds"> +<template id="template"> + <query id="query"> + <content id="content" uri="?uri"/> + <member id="member" container="?uri" child="?child"/> + <triple id="triple" subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> + </query> + <rule id="rule"> + <binding id="binding" subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> + <action id="action"> + <label uri="?child" value="?name"/> + </action> + </rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_extendedsyntax.xul b/dom/xul/templates/tests/chrome/test_tmpl_extendedsyntax.xul new file mode 100644 index 000000000..1bd31525e --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_extendedsyntax.xul @@ -0,0 +1,95 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + extended syntax +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label step="3" id="http://www.some-fictitious-zoo.com/birds/wren" value="Wren"/> + <label id="http://www.some-fictitious-zoo.com/birds/emu" value="Emu"/> + <label step="-4" id="http://www.some-fictitious-zoo.com/birds/barnowl" value="Barn Owl"/> + <label id="http://www.some-fictitious-zoo.com/birds/raven" value="Raven"/> + <label step="2" id="http://www.some-fictitious-zoo.com/birds/archaeopteryx" value="Archaeopteryx"/> + <label step="1" id="http://www.some-fictitious-zoo.com/birds/emperorpenguin" value="Emperor Penguin"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="extended syntax"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/emperorpenguin'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Emperor Penguin'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Archaeopteryx'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/wren'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Wren'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + var removednode = container.RemoveElementAt('3', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Barn Owl'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds"> +<template id="template"> +<rule id="rule"> +<conditions id="conditions"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_extendedsyntaxemptyconditions.xul b/dom/xul/templates/tests/chrome/test_tmpl_extendedsyntaxemptyconditions.xul new file mode 100644 index 000000000..8e0e37c3a --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_extendedsyntaxemptyconditions.xul @@ -0,0 +1,48 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + extended syntax - empty conditions +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"/> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="extended syntax - empty conditions"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds"> +<template id="template"> +<rule id="rule"> +<conditions id="conditions"/> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_extendedsyntaxotherrefvariable.xul b/dom/xul/templates/tests/chrome/test_tmpl_extendedsyntaxotherrefvariable.xul new file mode 100644 index 000000000..6895034f2 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_extendedsyntaxotherrefvariable.xul @@ -0,0 +1,95 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + extended syntax - other ref variable +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label step="3" id="http://www.some-fictitious-zoo.com/birds/wren" value="Wren"/> + <label id="http://www.some-fictitious-zoo.com/birds/emu" value="Emu"/> + <label step="-4" id="http://www.some-fictitious-zoo.com/birds/barnowl" value="Barn Owl"/> + <label id="http://www.some-fictitious-zoo.com/birds/raven" value="Raven"/> + <label step="2" id="http://www.some-fictitious-zoo.com/birds/archaeopteryx" value="Archaeopteryx"/> + <label step="1" id="http://www.some-fictitious-zoo.com/birds/emperorpenguin" value="Emperor Penguin"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="extended syntax - other ref variable"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/emperorpenguin'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Emperor Penguin'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Archaeopteryx'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/wren'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Wren'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + var removednode = container.RemoveElementAt('3', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Barn Owl'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds"> +<template id="template"> +<rule id="rule"> +<conditions id="conditions"> +<content uri="?start"/> +<member container="?start" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_extendedsyntaxremoveunmatched.xul b/dom/xul/templates/tests/chrome/test_tmpl_extendedsyntaxremoveunmatched.xul new file mode 100644 index 000000000..b2ef77d9e --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_extendedsyntaxremoveunmatched.xul @@ -0,0 +1,55 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + extended syntax, remove unmatched +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/humans/robert" value="Robert Sanderson"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="extended syntax, remove unmatched"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/humans"> +<template id="template"> +<rule> +<conditions> +<content uri="?uri"/> +<member container="?uri" child="?human"/> +<triple subject="?human" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +<triple subject="?human" predicate="http://www.some-fictitious-zoo.com/rdf#lastName" object="?lastname"/> +</conditions> +<action id="action"> +<label uri="?human" value="?name ?lastname"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_extendedsyntaxsimplevariablesubstitution.xul b/dom/xul/templates/tests/chrome/test_tmpl_extendedsyntaxsimplevariablesubstitution.xul new file mode 100644 index 000000000..d38bd96c0 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_extendedsyntaxsimplevariablesubstitution.xul @@ -0,0 +1,54 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + extended syntax - simple variable substitution +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/arachnids/tarantula" value=""/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="extended syntax - simple variable substitution"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/arachnids"> +<template id="template"> +<rule id="rule"> +<conditions id="conditions"> +<content uri="?start"/> +<member container="?start" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</conditions> +<action id="action"> +<label uri="?animal" value="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_extendedsyntaxtworulesrecurse.xul b/dom/xul/templates/tests/chrome/test_tmpl_extendedsyntaxtworulesrecurse.xul new file mode 100644 index 000000000..d0e4faadf --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_extendedsyntaxtworulesrecurse.xul @@ -0,0 +1,81 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + extended syntax - two rules recurse +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <vbox id="http://www.some-fictitious-zoo.com/humans/sarah"> + <label value="The favorite animals of Sarah"/> + <label id="http://www.some-fictitious-zoo.com/birds/emu" class="indent" value="Emu which belongs to the class Birds"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" class="indent" value="Polar Bear which belongs to the class Mammals"/> + <label id="http://www.some-fictitious-zoo.com/arachnids/tarantula" class="indent" value="Tarantula which belongs to the class Arachnids"/> + </vbox> + <vbox id="http://www.some-fictitious-zoo.com/humans/robert"> + <label value="The favorite animals of Robert"/> + <label id="http://www.some-fictitious-zoo.com/arachnids/tarantula" class="indent" value="Tarantula which belongs to the class Arachnids"/> + <label id="http://www.some-fictitious-zoo.com/reptiles/anaconda" class="indent" value="Anaconda which belongs to the class Reptiles"/> + <label id="http://www.some-fictitious-zoo.com/reptiles/chameleon" class="indent" value="Chameleon which belongs to the class Reptiles"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" class="indent" value="African Elephant which belongs to the class Mammals"/> + </vbox> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="extended syntax - two rules recurse"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/humans"> +<template> +<rule> +<conditions> +<content uri="?uri"/> +<member container="?uri" child="?child"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</conditions> +<action> +<vbox uri="?child"> +<label value="The favorite animals of ?name"/> +</vbox> +</action> +</rule> +<rule> +<conditions> +<content uri="?uri"/> +<triple subject="?uri" predicate="http://www.some-fictitious-zoo.com/rdf#favoriteAnimal" object="?child"/> +<member container="?animalClass" child="?child"/> +<triple subject="?animalClass" predicate="http://www.w3.org/1999/02/22-rdf-syntax-ns#type" object="http://www.some-fictitious-zoo.com/rdf#Class"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +<triple subject="?animalClass" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?animalClassName"/> +</conditions> +<action> +<label uri="?child" class="indent" value="?name which belongs to the class ?animalClassName"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_extendedsyntaxusinganinterveningcontainer.xul b/dom/xul/templates/tests/chrome/test_tmpl_extendedsyntaxusinganinterveningcontainer.xul new file mode 100644 index 000000000..2e9ce8a2f --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_extendedsyntaxusinganinterveningcontainer.xul @@ -0,0 +1,60 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + extended syntax using an intervening container +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <groupbox> + <label id="http://www.some-fictitious-zoo.com/birds/emu" value="Emu"/> + <label id="http://www.some-fictitious-zoo.com/birds/barnowl" value="Barn Owl"/> + <label id="http://www.some-fictitious-zoo.com/birds/raven" value="Raven"/> + </groupbox> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="extended syntax using an intervening container"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds"> +<template id="template"> +<rule id="rule"> +<conditions id="conditions"> +<content uri="?start"/> +<member container="?start" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</conditions> +<action id="action"> +<groupbox> +<label uri="?animal" value="?name"/> +</groupbox> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_extendedvariablesubstitution.xul b/dom/xul/templates/tests/chrome/test_tmpl_extendedvariablesubstitution.xul new file mode 100644 index 000000000..deee822dd --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_extendedvariablesubstitution.xul @@ -0,0 +1,54 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + extended variable substitution +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/arachnids/tarantula" value="Tarantula? - TarantulaTarantula^ Test"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="extended variable substitution"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/arachnids"> +<template id="template"> +<rule id="rule"> +<conditions id="conditions"> +<content uri="?start"/> +<member container="?start" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name^?? ?name?name - ?name^?name^^ Test"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_gridelement.xul b/dom/xul/templates/tests/chrome/test_tmpl_gridelement.xul new file mode 100644 index 000000000..12575c6b8 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_gridelement.xul @@ -0,0 +1,131 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + grid element +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <columns> + <column/> + <column/> + </columns> + <rows> + <label step="3" id="http://www.some-fictitious-zoo.com/birds/wren" value="The coolest animal is: Wren"/> + <row id="http://www.some-fictitious-zoo.com/birds/emu"> + <label value="Emu"/> + <label value="Birds"/> + </row> + <label step="-4" id="http://www.some-fictitious-zoo.com/birds/barnowl" value="The coolest animal is: Barn Owl"/> + <row id="http://www.some-fictitious-zoo.com/birds/raven"> + <label value="Raven"/> + <label value="Birds"/> + </row> + <row step="2" id="http://www.some-fictitious-zoo.com/birds/archaeopteryx"> + <label value="Archaeopteryx"/> + <label value="Birds"/> + </row> + <label step="1" id="http://www.some-fictitious-zoo.com/birds/emperorpenguin" value="The coolest animal is: Emperor Penguin"/> + </rows> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="grid element"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/emperorpenguin'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Emperor Penguin'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Archaeopteryx'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/wren'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Wren'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + var removednode = container.RemoveElementAt('3', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Barn Owl'), true); + } +]; +]]> +</script> + +<grid xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds"> +<columns> +<column/> +<column/> +</columns> +<template> +<query> +<content uri="?uri"/> +<member container="?uri" child="?child"/> +<triple subject="?uri" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?classname"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions> +<where subject="?name" rel="equals" value="Raven" negate="true"/> +<where subject="?name" rel="contains" value="n"/> +</conditions> +<action> +<rows> +<label uri="?child" value="The coolest animal is: ?name"/> +</rows> +</action> +</rule> +<rule> +<action> +<rows> +<row uri="?child"> +<label value="?name"/> +<label value="?classname"/> +</row> +</rows> +</action> +</rule> +</template> +</grid> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_htmlelementextendedsyntaxwithbinding.xul b/dom/xul/templates/tests/chrome/test_tmpl_htmlelementextendedsyntaxwithbinding.xul new file mode 100644 index 000000000..a9a14e00a --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_htmlelementextendedsyntaxwithbinding.xul @@ -0,0 +1,114 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + html element - extended syntax with binding +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output" xmlns:html="http://www.w3.org/1999/xhtml"> + <html:div id="http://www.some-fictitious-zoo.com/mammals/lion" title="Lion"> + <html:em step="-2">4</html:em> + <html:em step="2">9</html:em> + </html:div> + <html:div id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" title="HIPPOPOTAMUS"> + <html:em>2</html:em> + </html:div> + <html:p id="http://www.some-fictitious-zoo.com/mammals/africanelephant"> + African Elephant + <html:span title="14"/> + </html:p> + <html:p step="4" id="http://www.some-fictitious-zoo.com/mammals/chimpanzee"> + Chimpanzee + <html:span step="-5"/> + <html:span step="5" title="3"/> + </html:p> + <html:div id="http://www.some-fictitious-zoo.com/mammals/llama" title="LLAMA"> + <html:em>5</html:em> + </html:div> + <html:div id="http://www.some-fictitious-zoo.com/mammals/polarbear" title="Polar Bear"> + <html:em step="-1">20</html:em> + <html:em step="1">5</html:em> + </html:div> + <html:div id="http://www.some-fictitious-zoo.com/mammals/aardvark" title="aardvark"> + <html:em>2</html:em> + </html:div> + <html:p step="-3" id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo"> + Nine-banded Armadillo + <html:span title="1"/> + </html:p> + <html:div id="http://www.some-fictitious-zoo.com/mammals/gorilla" title="Gorilla"> + <html:em>7</html:em> + </html:div> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="html element - extended syntax with binding"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var subject = RDF.GetResource(ZOO_NS + 'mammals/polarbear'); + var predicate = RDF.GetResource(ZOO_NS + 'rdf#specimens'); + var oldval = targetds.GetTarget(subject, predicate, true); + targetds.Change(RDF.GetResource(ZOO_NS + 'mammals/polarbear'), + RDF.GetResource(ZOO_NS + 'rdf#specimens'), + oldval, RDF.GetLiteral('5')); + }, + // step 2 + function(targetds, root) { + var subject = RDF.GetResource(ZOO_NS + 'mammals/lion'); + var predicate = RDF.GetResource(ZOO_NS + 'rdf#specimens'); + var oldval = targetds.GetTarget(subject, predicate, true); + targetds.Change(RDF.GetResource(ZOO_NS + 'mammals/lion'), + RDF.GetResource(ZOO_NS + 'rdf#specimens'), + oldval, RDF.GetLiteral('9')); + }, + // step 3 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + var removednode = container.RemoveElementAt('7', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Nine-banded Armadillo'), true); + }, + // step 4 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/chimpanzee'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Chimpanzee'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '4', true); + }, + // step 5 + function(targetds, root) { + targetds.Assert(RDF.GetResource(ZOO_NS + 'mammals/chimpanzee'), + RDF.GetResource(ZOO_NS + 'rdf#specimens'), + RDF.GetLiteral('3'), true); + } +]; +]]> +</script> + +<div xmlns="http://www.w3.org/1999/xhtml" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"><template xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="template"><rule><conditions><content uri="?uri"/><member container="?uri" child="?animal"/><triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/><where subject="?name" rel="contains" value="an"/></conditions><bindings><binding subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#specimens" object="?specimens"/></bindings><action><p xmlns="http://www.w3.org/1999/xhtml" uri="?animal"><textnode xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" value="?name"/><span title="?specimens"/></p></action></rule><rule><conditions><content uri="?uri"/><member container="?uri" child="?animal"/><triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/></conditions><bindings><binding subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#specimens" object="?specimens"/></bindings><action><div xmlns="http://www.w3.org/1999/xhtml" uri="?animal" title="?name"><em><textnode xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" value="?specimens"/></em></div></action></rule></template></div> +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_htmlelementquerysyntaxrecursive.xul b/dom/xul/templates/tests/chrome/test_tmpl_htmlelementquerysyntaxrecursive.xul new file mode 100644 index 000000000..b03f20ce2 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_htmlelementquerysyntaxrecursive.xul @@ -0,0 +1,81 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + html element - query syntax recursive +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output" xmlns:html="http://www.w3.org/1999/xhtml"> + <html:strong id="http://www.some-fictitious-zoo.com/arachnids" container="true" empty="false">Arachnids</html:strong> + <html:strong step="-3" id="http://www.some-fictitious-zoo.com/birds" container="true" empty="false"> + Birds + <html:span id="http://www.some-fictitious-zoo.com/birds/barnowl">Barn Owl</html:span> + </html:strong> + <html:strong step="1" id="http://www.some-fictitious-zoo.com/insects">Insects</html:strong> + <html:strong id="http://www.some-fictitious-zoo.com/mammals" container="true" empty="false"> + Mammals + <html:span id="http://www.some-fictitious-zoo.com/mammals/lion">Lion</html:span> + <html:span id="http://www.some-fictitious-zoo.com/mammals/hippopotamus">HIPPOPOTAMUS</html:span> + <html:span step="2" id="http://www.some-fictitious-zoo.com/mammals/koala">Koala</html:span> + <html:span id="http://www.some-fictitious-zoo.com/mammals/polarbear">Polar Bear</html:span> + <html:span id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo">Nine-banded Armadillo</html:span> + <html:span id="http://www.some-fictitious-zoo.com/mammals/gorilla">Gorilla</html:span> + </html:strong> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="html element - query syntax recursive"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'insects'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Insects'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'all-animals')); + container.InsertElementAt(newnode, '3', true); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/koala'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Koala'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'all-animals')); + var removednode = container.RemoveElementAt('2', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Birds'), true); + } +]; +]]> +</script> + +<div xmlns="http://www.w3.org/1999/xhtml" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/all-animals"><template xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="template"><query><content uri="?uri"/><member container="?uri" child="?child"/><triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/></query><rule><conditions><where subject="?name" rel="endswith" multiple="true" value="mals,ects,nids,irds"/></conditions><action><strong xmlns="http://www.w3.org/1999/xhtml" uri="?child"><textnode xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" value="?name"/></strong></action></rule><rule><conditions><where subject="?uri" rel="equals" negate="true" value="http://www.some-fictitious-zoo.com/all-animals"/><where subject="?name" rel="contains" ignorecase="true" value="o"/></conditions><action><span xmlns="http://www.w3.org/1999/xhtml" uri="?child"><textnode xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" value="?name"/></span></action></rule></template></div> +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_htmlelementquerysyntaxwithmultiplerules.xul b/dom/xul/templates/tests/chrome/test_tmpl_htmlelementquerysyntaxwithmultiplerules.xul new file mode 100644 index 000000000..e0ef6a732 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_htmlelementquerysyntaxwithmultiplerules.xul @@ -0,0 +1,102 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + html element - query syntax with multiple rules +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output" xmlns:html="http://www.w3.org/1999/xhtml"> + <html:span step="-2" id="http://www.some-fictitious-zoo.com/mammals/lion">Lion</html:span> + <html:span id="http://www.some-fictitious-zoo.com/mammals/hippopotamus">HIPPOPOTAMUS</html:span> + <vbox> + <html:p step="2" id="http://www.some-fictitious-zoo.com/mammals/lion"> + <html:span title="Lion"/> + </html:p> + <html:p id="http://www.some-fictitious-zoo.com/mammals/africanelephant"> + <html:span title="African Elephant"/> + </html:p> + <html:p step="-1" id="http://www.some-fictitious-zoo.com/mammals/polarbear"> + <html:span title="Polar Bear"/> + </html:p> + <html:p id="http://www.some-fictitious-zoo.com/mammals/gorilla"> + <html:span title="Gorilla"/> + </html:p> + </vbox> + <html:span step="5" id="http://www.some-fictitious-zoo.com/mammals/chimpanzee">Chimpanzee</html:span> + <html:span id="http://www.some-fictitious-zoo.com/mammals/llama">LLAMA</html:span> + <html:span step="1" id="http://www.some-fictitious-zoo.com/mammals/polarbear">Polar Bear</html:span> + <html:span id="http://www.some-fictitious-zoo.com/mammals/aardvark">aardvark</html:span> + <html:span step="-3" id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo">Nine-banded Armadillo</html:span> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="html element - query syntax with multiple rules"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = true; +var notWorkingYetDynamic = true; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var subject = RDF.GetResource(ZOO_NS + 'mammals/polarbear'); + var predicate = RDF.GetResource(ZOO_NS + 'rdf#specimens'); + var oldval = targetds.GetTarget(subject, predicate, true); + targetds.Change(RDF.GetResource(ZOO_NS + 'mammals/polarbear'), + RDF.GetResource(ZOO_NS + 'rdf#specimens'), + oldval, RDF.GetLiteral('5')); + }, + // step 2 + function(targetds, root) { + var subject = RDF.GetResource(ZOO_NS + 'mammals/lion'); + var predicate = RDF.GetResource(ZOO_NS + 'rdf#specimens'); + var oldval = targetds.GetTarget(subject, predicate, true); + targetds.Change(RDF.GetResource(ZOO_NS + 'mammals/lion'), + RDF.GetResource(ZOO_NS + 'rdf#specimens'), + oldval, RDF.GetLiteral('9')); + }, + // step 3 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + var removednode = container.RemoveElementAt('7', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Nine-banded Armadillo'), true); + }, + // step 4 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/chimpanzee'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Chimpanzee'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '4', true); + }, + // step 5 + function(targetds, root) { + targetds.Assert(RDF.GetResource(ZOO_NS + 'mammals/chimpanzee'), + RDF.GetResource(ZOO_NS + 'rdf#specimens'), + RDF.GetLiteral('3'), true); + } +]; +]]> +</script> + +<div xmlns="http://www.w3.org/1999/xhtml" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"><template xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="template"><query><content uri="?uri"/><member container="?uri" child="?animal"/><triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/><triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#specimens" object="?specimens"/></query><rule><conditions id="conditions"><where subject="?specimens" rel="greater" value="6"/></conditions><action><vbox><p xmlns="http://www.w3.org/1999/xhtml" uri="?animal"><span title="?name"/></p></vbox></action></rule><rule><action><span xmlns="http://www.w3.org/1999/xhtml" uri="?animal"><textnode xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" value="?name"/></span></action></rule></template></div> +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_htmlelementsimplesyntax.xul b/dom/xul/templates/tests/chrome/test_tmpl_htmlelementsimplesyntax.xul new file mode 100644 index 000000000..95c016fe8 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_htmlelementsimplesyntax.xul @@ -0,0 +1,81 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + html element - simple syntax +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output" xmlns:html="http://www.w3.org/1999/xhtml"> + <html:p step="3" id="http://www.some-fictitious-zoo.com/birds/wren" title="Wren"/> + <html:p id="http://www.some-fictitious-zoo.com/birds/emu" title="Emu"/> + <html:p step="-4" id="http://www.some-fictitious-zoo.com/birds/barnowl" title="Barn Owl"/> + <html:p id="http://www.some-fictitious-zoo.com/birds/raven" title="Raven"/> + <html:p step="2" id="http://www.some-fictitious-zoo.com/birds/archaeopteryx" title="Archaeopteryx"/> + <html:p step="1" id="http://www.some-fictitious-zoo.com/birds/emperorpenguin" title="Emperor Penguin"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="html element - simple syntax"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/emperorpenguin'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Emperor Penguin'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Archaeopteryx'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/wren'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Wren'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + var removednode = container.RemoveElementAt('3', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Barn Owl'), true); + } +]; +]]> +</script> + +<div xmlns="http://www.w3.org/1999/xhtml" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds"><template xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"><p xmlns="http://www.w3.org/1999/xhtml" uri="rdf:*" title="rdf:http://www.some-fictitious-zoo.com/rdf#name"/></template></div> +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_htmlelementsimplesyntaxusingatextnode.xul b/dom/xul/templates/tests/chrome/test_tmpl_htmlelementsimplesyntaxusingatextnode.xul new file mode 100644 index 000000000..14d8bab38 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_htmlelementsimplesyntaxusingatextnode.xul @@ -0,0 +1,81 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + html element - simple syntax using a textnode +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output" xmlns:html="http://www.w3.org/1999/xhtml"> + <html:p step="3" id="http://www.some-fictitious-zoo.com/birds/wren">Wren</html:p> + <html:p id="http://www.some-fictitious-zoo.com/birds/emu">Emu</html:p> + <html:p step="-4" id="http://www.some-fictitious-zoo.com/birds/barnowl">Barn Owl</html:p> + <html:p id="http://www.some-fictitious-zoo.com/birds/raven">Raven</html:p> + <html:p step="2" id="http://www.some-fictitious-zoo.com/birds/archaeopteryx">Archaeopteryx</html:p> + <html:p step="1" id="http://www.some-fictitious-zoo.com/birds/emperorpenguin">Emperor Penguin</html:p> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="html element - simple syntax using a textnode"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/emperorpenguin'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Emperor Penguin'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Archaeopteryx'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/wren'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Wren'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + var removednode = container.RemoveElementAt('3', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Barn Owl'), true); + } +]; +]]> +</script> + +<div xmlns="http://www.w3.org/1999/xhtml" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds"><template xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"><p xmlns="http://www.w3.org/1999/xhtml" uri="rdf:*"><textnode xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" value="rdf:http://www.some-fictitious-zoo.com/rdf#name"/></p></template></div> +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_invalidqp.xul b/dom/xul/templates/tests/chrome/test_tmpl_invalidqp.xul new file mode 100644 index 000000000..2231f2067 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_invalidqp.xul @@ -0,0 +1,48 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + invalid syntax - querytype="blah" +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"/> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="invalid syntax"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +Components.classes["@mozilla.org/consoleservice;1"]. + getService(Components.interfaces.nsIConsoleService).reset(); +expectedConsoleMessages.push("Error parsing template: querytype attribute doesn't specify a valid query processor"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" + ref="http://www.some-fictitious-zoo.com/birds" querytype="blah"> +<template zoo:name="Barn Owl" xmlns:zoo="http://www.some-fictitious-zoo.com/rdf#"> +<label uri="rdf:*" value="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_listboxelement.xul b/dom/xul/templates/tests/chrome/test_tmpl_listboxelement.xul new file mode 100644 index 000000000..e754b1542 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_listboxelement.xul @@ -0,0 +1,117 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + listbox element +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <listcols> + <listcol flex="1"/> + <listcol flex="1"/> + </listcols> + <listitem step="3" id="http://www.some-fictitious-zoo.com/birds/wren"> + <listcell label="Wren"/> + <listcell label=""/> + </listitem> + <listitem id="http://www.some-fictitious-zoo.com/birds/emu"> + <listcell label="Emu"/> + <listcell label="Dromaius novaehollandiae"/> + </listitem> + <listitem step="-4" id="http://www.some-fictitious-zoo.com/birds/barnowl"> + <listcell label="Barn Owl"/> + <listcell label="Tyto alba"/> + </listitem> + <listitem id="http://www.some-fictitious-zoo.com/birds/raven"> + <listcell label="Raven"/> + <listcell label="Corvus corax"/> + </listitem> + <listitem step="2" id="http://www.some-fictitious-zoo.com/birds/archaeopteryx"> + <listcell label="Archaeopteryx"/> + <listcell label=""/> + </listitem> + <listitem step="1" id="http://www.some-fictitious-zoo.com/birds/emperorpenguin"> + <listcell label="Emperor Penguin"/> + <listcell label=""/> + </listitem> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.expectAssertions(4); + +SimpleTest.waitForExplicitFinish(); + +var testid ="listbox element"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/emperorpenguin'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Emperor Penguin'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Archaeopteryx'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/wren'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Wren'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + var removednode = container.RemoveElementAt('3', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Barn Owl'), true); + } +]; +]]> +</script> + +<listbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds"> +<listcols> +<listcol flex="1"/> +<listcol flex="1"/> +</listcols> +<template> +<listitem uri="rdf:*"> +<listcell label="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +<listcell label="rdf:http://www.some-fictitious-zoo.com/rdf#species"/> +</listitem> +</template> +</listbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_literalasmember.xul b/dom/xul/templates/tests/chrome/test_tmpl_literalasmember.xul new file mode 100644 index 000000000..5e2ffe326 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_literalasmember.xul @@ -0,0 +1,52 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + literal as member +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"/> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="literal as member"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/arachnids"> +<template id="template"> +<rule id="rule"> +<conditions id="conditions"> +<content uri="?start"/> +<member container="?start" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</conditions> +<action id="action"> +<label uri="?name" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_membervariablechanged.xul b/dom/xul/templates/tests/chrome/test_tmpl_membervariablechanged.xul new file mode 100644 index 000000000..c231b8670 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_membervariablechanged.xul @@ -0,0 +1,49 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + member variable changed +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <button id="http://www.some-fictitious-zoo.com/birds/emu" label="http://www.some-fictitious-zoo.com/birds/emu Emu"/> + <button id="http://www.some-fictitious-zoo.com/birds/barnowl" label="http://www.some-fictitious-zoo.com/birds/barnowl Barn Owl"/> + <button id="http://www.some-fictitious-zoo.com/birds/raven" label="http://www.some-fictitious-zoo.com/birds/raven Raven"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="member variable changed"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds"> +<template member="?child"> +<rule> +<button uri="?child" label="?child rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_membervariablesubstitution.xul b/dom/xul/templates/tests/chrome/test_tmpl_membervariablesubstitution.xul new file mode 100644 index 000000000..512717934 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_membervariablesubstitution.xul @@ -0,0 +1,54 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + member variable substitution +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/arachnids/tarantula" value="http://www.some-fictitious-zoo.com/arachnids/tarantula"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="member variable substitution"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/arachnids"> +<template id="template"> +<rule id="rule"> +<conditions id="conditions"> +<content uri="?start"/> +<member container="?start" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?animal"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_menuelement.xul b/dom/xul/templates/tests/chrome/test_tmpl_menuelement.xul new file mode 100644 index 000000000..eecb6a7ed --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_menuelement.xul @@ -0,0 +1,90 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + menu element +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <menupopup> + <menuitem step="3" id="http://www.some-fictitious-zoo.com/birds/wren" label="Wren"/> + <menuitem id="http://www.some-fictitious-zoo.com/birds/emu" label="Emu"/> + <menuitem step="-4" id="http://www.some-fictitious-zoo.com/birds/barnowl" label="Barn Owl"/> + <menuitem id="http://www.some-fictitious-zoo.com/birds/raven" label="Raven"/> + <menuitem step="2" id="http://www.some-fictitious-zoo.com/birds/archaeopteryx" label="Archaeopteryx"/> + <menuitem step="1" id="http://www.some-fictitious-zoo.com/birds/emperorpenguin" label="Emperor Penguin"/> + </menupopup> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="menu element"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = true; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/emperorpenguin'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Emperor Penguin'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Archaeopteryx'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/wren'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Wren'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + var removednode = container.RemoveElementAt('3', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Barn Owl'), true); + } +]; +]]> +</script> + +<button xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" type="menu" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds"> +<template> +<menupopup> +<menuitem uri="rdf:*" label="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +</menupopup> +</template> +</button> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_menuelementrecursive.xul b/dom/xul/templates/tests/chrome/test_tmpl_menuelementrecursive.xul new file mode 100644 index 000000000..1597e3164 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_menuelementrecursive.xul @@ -0,0 +1,121 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + menu element recursive +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template()" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <menupopup> + <menuitem id="http://www.some-fictitious-zoo.com/arachnids" label="Arachnids" container="true" empty="false"/> + <menu step="-2" id="http://www.some-fictitious-zoo.com/birds" label="Birds" container="true" empty="false"/> + <menu step="2" id="http://www.some-fictitious-zoo.com/birds" label="Birds" container="true" empty="false" open="true"> + <menupopup> + <menuitem step="4" id="http://www.some-fictitious-zoo.com/birds/wren" label="Wren"/> + <menuitem id="http://www.some-fictitious-zoo.com/birds/emu" label="Emu"/> + <menuitem step="-5" id="http://www.some-fictitious-zoo.com/birds/barnowl" label="Barn Owl"/> + <menuitem id="http://www.some-fictitious-zoo.com/birds/raven" label="Raven"/> + <menuitem step="3" id="http://www.some-fictitious-zoo.com/birds/archaeopteryx" label="Archaeopteryx"/> + <menuitem id="http://www.some-fictitious-zoo.com/birds/emperorpenguin" label="Emperor Penguin"/> + </menupopup> + </menu> + <menuitem id="http://www.some-fictitious-zoo.com/crustaceans" label="Crustaceans" container="true" empty="true"/> + <menuitem id="http://www.some-fictitious-zoo.com/fish" label="Fish" container="true" empty="false"/> + <menuitem id="http://www.some-fictitious-zoo.com/mammals" label="Mammals" container="true" empty="false"/> + <menuitem id="http://www.some-fictitious-zoo.com/reptiles" label="Reptiles" container="true" empty="false"/> + </menupopup> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="menu element recursive"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = true; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + // nothing should change at this step as the submenu is not open + var newnode = RDF.GetResource(ZOO_NS + 'birds/emperorpenguin'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Emperor Penguin'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + // open the submenu + root.lastChild.firstChild.nextSibling.open = true; + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Archaeopteryx'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '4', true); + root.lastChild.firstChild.nextSibling.open = true; + }, + // step 4 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/wren'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Wren'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '1', true); + root.lastChild.firstChild.nextSibling.open = true; + }, + // step 5 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + var removednode = container.RemoveElementAt('3', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Barn Owl'), true); + root.lastChild.firstChild.nextSibling.open = true; + } +]; +]]> +</script> + +<button xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" type="menu" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/all-animals"> +<template> +<rule iscontainer="true" zoo:name="Birds" xmlns:zoo="http://www.some-fictitious-zoo.com/rdf#"> +<menupopup> +<menu uri="rdf:*" label="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +</menupopup> +</rule> +<rule parent="button"> +<menupopup> +<menuitem uri="rdf:*" label="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +</menupopup> +</rule> +<rule parent="menu"> +<menupopup> +<menuitem uri="rdf:*" label="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +</menupopup> +</rule> +</template> +</button> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_menulistelement.xul b/dom/xul/templates/tests/chrome/test_tmpl_menulistelement.xul new file mode 100644 index 000000000..bebc7b970 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_menulistelement.xul @@ -0,0 +1,90 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + menulist element +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="$('root').selectedItem = null; test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <menupopup> + <menuitem step="3" id="http://www.some-fictitious-zoo.com/birds/wren" label="Wren"/> + <menuitem id="http://www.some-fictitious-zoo.com/birds/emu" label="Emu"/> + <menuitem step="-4" id="http://www.some-fictitious-zoo.com/birds/barnowl" label="Barn Owl"/> + <menuitem id="http://www.some-fictitious-zoo.com/birds/raven" label="Raven"/> + <menuitem step="2" id="http://www.some-fictitious-zoo.com/birds/archaeopteryx" label="Archaeopteryx"/> + <menuitem step="1" id="http://www.some-fictitious-zoo.com/birds/emperorpenguin" label="Emperor Penguin"/> + </menupopup> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="menulist element"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/emperorpenguin'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Emperor Penguin'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Archaeopteryx'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/wren'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Wren'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + var removednode = container.RemoveElementAt('3', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Barn Owl'), true); + } +]; +]]> +</script> + +<menulist xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds"> +<template> +<menupopup> +<menuitem uri="rdf:*" label="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +</menupopup> +</template> +</menulist> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_mixedsyntaxiscontainer.xul b/dom/xul/templates/tests/chrome/test_tmpl_mixedsyntaxiscontainer.xul new file mode 100644 index 000000000..446fa77ec --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_mixedsyntaxiscontainer.xul @@ -0,0 +1,67 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + mixed syntax - iscontainer +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <checkbox id="http://www.some-fictitious-zoo.com/humans/sarah" label="Sarah"/> + <button id="http://www.some-fictitious-zoo.com/mammals" container="true" empty="false" label="Mammals"/> + <button id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="true" label="Crustaceans"/> + <checkbox id="http://www.some-fictitious-zoo.com/birds/emu" label="Emu"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="mixed syntax - iscontainer"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + targetds.Unassert(RDF.GetResource(ZOO_NS + 'humans/sarah'), + RDF.GetResource(ZOO_NS + 'rdf#pets'), + RDF.GetResource(ZOO_NS + 'sarahs-pets'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/marked" flags="dont-recurse"> +<template id="template"> +<rule id="rule1" iscontainer="true"> +<button uri="rdf:*" label="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +</rule> +<rule> +<conditions> +<content uri="?uri"/> +<member container="?uri" child="?child"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</conditions> +<action> +<checkbox uri="?child" label="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_mixedsyntaxiscontainerisempty.xul b/dom/xul/templates/tests/chrome/test_tmpl_mixedsyntaxiscontainerisempty.xul new file mode 100644 index 000000000..2c3a3e23c --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_mixedsyntaxiscontainerisempty.xul @@ -0,0 +1,60 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + mixed syntax - iscontainer isempty +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <checkbox id="http://www.some-fictitious-zoo.com/humans/sarah" label="Sarah"/> + <checkbox id="http://www.some-fictitious-zoo.com/mammals" container="true" empty="false" label="Mammals"/> + <label id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="true" value="Crustaceans"/> + <checkbox id="http://www.some-fictitious-zoo.com/birds/emu" label="Emu"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="mixed syntax - iscontainer isempty"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/marked" flags="dont-recurse"> +<template id="template"> +<rule id="rule1" iscontainer="true" isempty="true"> +<label uri="rdf:*" value="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +</rule> +<rule> +<conditions> +<content uri="?uri"/> +<member container="?uri" child="?child"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</conditions> +<action> +<checkbox uri="?child" label="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_mixedsyntaxisempty.xul b/dom/xul/templates/tests/chrome/test_tmpl_mixedsyntaxisempty.xul new file mode 100644 index 000000000..25b5d59a0 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_mixedsyntaxisempty.xul @@ -0,0 +1,60 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + mixed syntax - isempty +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/humans/sarah" value="Sarah"/> + <checkbox id="http://www.some-fictitious-zoo.com/mammals" container="true" empty="false" label="Mammals"/> + <label id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="true" value="Crustaceans"/> + <label id="http://www.some-fictitious-zoo.com/birds/emu" value="Emu"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="mixed syntax - isempty"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/marked" flags="dont-recurse"> +<template id="template"> +<rule id="rule1" isempty="true"> +<label uri="rdf:*" value="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +</rule> +<rule> +<conditions> +<content uri="?uri"/> +<member container="?uri" child="?child"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</conditions> +<action> +<checkbox uri="?child" label="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_noaction.xul b/dom/xul/templates/tests/chrome/test_tmpl_noaction.xul new file mode 100644 index 000000000..f03623e09 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_noaction.xul @@ -0,0 +1,48 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + no action +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"/> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="no action"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds"> +<template> +<query> +<content uri="?uri"/> +<member container="?uri" child="?child"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<button uri="?child" label="?name"/> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_noactionuriattribute.xul b/dom/xul/templates/tests/chrome/test_tmpl_noactionuriattribute.xul new file mode 100644 index 000000000..bd14bee1f --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_noactionuriattribute.xul @@ -0,0 +1,55 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + no action uri attribute +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label value="Tarantula"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="no action uri attribute"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var subject = RDF.GetResource(ZOO_NS + 'arachnids/tarantula'); + var predicate = RDF.GetResource(ZOO_NS + 'rdf#name'); + var oldval = targetds.GetTarget(subject, predicate, true); + targetds.Change(RDF.GetResource(ZOO_NS + 'arachnids/tarantula'), + RDF.GetResource(ZOO_NS + 'rdf#name'), + oldval, RDF.GetLiteral('Hairy Spider')); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/arachnids"> +<template id="template"> +<label value="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_parentconditions.xul b/dom/xul/templates/tests/chrome/test_tmpl_parentconditions.xul new file mode 100644 index 000000000..dae20a830 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_parentconditions.xul @@ -0,0 +1,113 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + parent - conditions +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <box step="-3" id="http://www.some-fictitious-zoo.com/birds" container="true" empty="false"> + <label value="Birds"/> + <button id="http://www.some-fictitious-zoo.com/birds/barnowl" label="Barn Owl"/> + <button id="http://www.some-fictitious-zoo.com/birds/raven" label="Raven"/> + </box> + <box step="1" id="http://www.some-fictitious-zoo.com/insects"> + <label value="Insects"/> + </box> + <box id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="true"> + <label value="Crustaceans"/> + </box> + <box id="http://www.some-fictitious-zoo.com/mammals" container="true" empty="false"> + <label value="Mammals"/> + <button id="http://www.some-fictitious-zoo.com/mammals/africanelephant" label="African Elephant"/> + <button step="2" id="http://www.some-fictitious-zoo.com/mammals/koala" label="Koala"/> + <button id="http://www.some-fictitious-zoo.com/mammals/polarbear" label="Polar Bear"/> + <button id="http://www.some-fictitious-zoo.com/mammals/aardvark" label="aardvark"/> + <button id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" label="Nine-banded Armadillo"/> + <button id="http://www.some-fictitious-zoo.com/mammals/gorilla" label="Gorilla"/> + </box> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="parent - conditions"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'insects'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Insects'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'all-animals')); + container.InsertElementAt(newnode, '3', true); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/koala'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Koala'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'all-animals')); + var removednode = container.RemoveElementAt('2', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Birds'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/all-animals"> +<template> +<query> +<content uri="?uri"/> +<member container="?uri" child="?parent"/> +<triple subject="?parent" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions parent="box"> +<where subject="?name" rel="contains" value="a"/> +</conditions> +<action> +<button uri="?parent" label="?name"/> +</action> +</rule> +<rule> +<conditions> +<where subject="?name" rel="equals" multiple="true" value="Mammals,Crustaceans,Birds,Insects"/> +</conditions> +<action> +<box uri="?parent"> +<label value="?name"/> +</box> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_parentcontenttag.xul b/dom/xul/templates/tests/chrome/test_tmpl_parentcontenttag.xul new file mode 100644 index 000000000..afcb1d34a --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_parentcontenttag.xul @@ -0,0 +1,114 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + parent - content tag +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <groupbox step="-3" id="http://www.some-fictitious-zoo.com/birds" container="true" empty="false"> + <caption value="Birds"/> + <checkbox id="http://www.some-fictitious-zoo.com/birds/barnowl" label="Barn Owl"/> + <checkbox id="http://www.some-fictitious-zoo.com/birds/raven" label="Raven"/> + </groupbox> + <groupbox step="1" id="http://www.some-fictitious-zoo.com/insects"> + <caption value="Insects"/> + </groupbox> + <groupbox id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="true"> + <caption value="Crustaceans"/> + </groupbox> + <groupbox id="http://www.some-fictitious-zoo.com/mammals" container="true" empty="false"> + <caption value="Mammals"/> + <checkbox id="http://www.some-fictitious-zoo.com/mammals/africanelephant" label="African Elephant"/> + <checkbox step="2" id="http://www.some-fictitious-zoo.com/mammals/koala" label="Koala"/> + <checkbox id="http://www.some-fictitious-zoo.com/mammals/polarbear" label="Polar Bear"/> + <checkbox id="http://www.some-fictitious-zoo.com/mammals/aardvark" label="aardvark"/> + <checkbox id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" label="Nine-banded Armadillo"/> + <checkbox id="http://www.some-fictitious-zoo.com/mammals/gorilla" label="Gorilla"/> + </groupbox> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="parent - content tag"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'insects'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Insects'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'all-animals')); + container.InsertElementAt(newnode, '3', true); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/koala'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Koala'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'all-animals')); + var removednode = container.RemoveElementAt('2', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Birds'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/all-animals"> +<template> +<rule> +<conditions> +<content uri="?uri" tag="groupbox"/> +<member container="?uri" child="?parent"/> +<triple subject="?parent" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +<where subject="?name" rel="contains" value="a"/> +</conditions> +<action> +<checkbox uri="?parent" label="?name"/> +</action> +</rule> +<rule> +<conditions> +<content uri="?uri"/> +<member container="?uri" child="?parent"/> +<triple subject="?parent" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +<where subject="?name" rel="equals" multiple="true" value="Mammals,Crustaceans,Birds,Insects"/> +</conditions> +<action> +<groupbox uri="?parent"> +<caption value="?name"/> +</groupbox> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_parentsimplesyntax.xul b/dom/xul/templates/tests/chrome/test_tmpl_parentsimplesyntax.xul new file mode 100644 index 000000000..c5d94e0e2 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_parentsimplesyntax.xul @@ -0,0 +1,110 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + parent - simple syntax +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <hbox step="-3" id="http://www.some-fictitious-zoo.com/birds" container="true" empty="false"> + <label value="Birds"/> + <button id="http://www.some-fictitious-zoo.com/birds/emu" label="Emu"/> + <button id="http://www.some-fictitious-zoo.com/birds/barnowl" label="Barn Owl"/> + <button id="http://www.some-fictitious-zoo.com/birds/raven" label="Raven"/> + </hbox> + <hbox step="1" id="http://www.some-fictitious-zoo.com/insects"> + <label value="Insects"/> + </hbox> + <hbox id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="true"> + <label value="Crustaceans"/> + </hbox> + <hbox id="http://www.some-fictitious-zoo.com/mammals" container="true" empty="false"> + <label value="Mammals"/> + <button id="http://www.some-fictitious-zoo.com/mammals/lion" label="Lion"/> + <button id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" label="HIPPOPOTAMUS"/> + <button id="http://www.some-fictitious-zoo.com/mammals/africanelephant" label="African Elephant"/> + <button step="2" id="http://www.some-fictitious-zoo.com/mammals/koala" label="Koala"/> + <button id="http://www.some-fictitious-zoo.com/mammals/llama" label="LLAMA"/> + <button id="http://www.some-fictitious-zoo.com/mammals/polarbear" label="Polar Bear"/> + <button id="http://www.some-fictitious-zoo.com/mammals/aardvark" label="aardvark"/> + <button id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" label="Nine-banded Armadillo"/> + <button id="http://www.some-fictitious-zoo.com/mammals/gorilla" label="Gorilla"/> + </hbox> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="parent - simple syntax"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'insects'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Insects'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'all-animals')); + container.InsertElementAt(newnode, '3', true); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/koala'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Koala'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'all-animals')); + var removednode = container.RemoveElementAt('2', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Birds'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/all-animals"> +<template member="?parent"> +<rule parent="hbox"> +<button uri="?parent" label="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +</rule> +<rule> +<conditions> +<content uri="?uri"/> +<member container="?uri" child="?parent"/> +<triple subject="?parent" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +<where subject="?name" rel="equals" multiple="true" value="Mammals,Crustaceans,Birds,Insects"/> +</conditions> +<action> +<hbox uri="?parent"> +<label value="?name"/> +</hbox> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_query3triples.xul b/dom/xul/templates/tests/chrome/test_tmpl_query3triples.xul new file mode 100644 index 000000000..c0114effe --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_query3triples.xul @@ -0,0 +1,59 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + query - 3 triples +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output" unordered="true"> + <label id="http://www.some-fictitious-zoo.com/arachnids/tarantula" value="Robert likes Tarantulas"/> + <label id="http://www.some-fictitious-zoo.com/reptiles/anaconda" value="Robert likes Anacondas"/> + <label id="http://www.some-fictitious-zoo.com/reptiles/chameleon" value="Robert likes Chameleons"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="Robert likes African Elephants"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="query - 3 triples"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/humans/robert"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<triple subject="?uri" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?humanname"/> +<triple subject="?uri" predicate="http://www.some-fictitious-zoo.com/rdf#favoriteAnimal" object="?favoriteAnimal"/> +<triple subject="?favoriteAnimal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?animalname"/> +</query> +<rule> +<conditions id="conditions"/> +<action> +<label uri="?favoriteAnimal" value="?humanname likes ?animalname^s"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_query3tripleswherecontains.xul b/dom/xul/templates/tests/chrome/test_tmpl_query3tripleswherecontains.xul new file mode 100644 index 000000000..98006fc0d --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_query3tripleswherecontains.xul @@ -0,0 +1,111 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + query - 3 triples - where contains +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/arachnids/tarantula" value="Robert likes Tarantulas"/> + <label step="-4" id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="Robert likes African Elephants"/> + <label step="5" id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="Robert likes African Elephants"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="query - 3 triples - where contains"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/arctichare'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Arctic Hare'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/koala'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Koala'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/zebra'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Zebra'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + var removednode = container.RemoveElementAt('4', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('African Elephant'), true); + }, + // step 5 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/africanelephant'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('African Elephant'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.AppendElement(newnode); + }, + // step 6 + function(targetds, root) { + targetds.Assert(RDF.GetResource(ZOO_NS + 'mammals/koala'), + RDF.GetResource(ZOO_NS + 'rdf#specimensAsString'), + RDF.GetLiteral('8'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/humans/robert"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<triple subject="?uri" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?humanname"/> +<triple subject="?uri" predicate="http://www.some-fictitious-zoo.com/rdf#favoriteAnimal" object="?favoriteAnimal"/> +<triple subject="?favoriteAnimal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?animalname"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?favoriteAnimal" rel="contains" value="ant"/> +</conditions> +<action> +<label uri="?favoriteAnimal" value="?humanname likes ?animalname^s"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_querymember3tripleswhereequals.xul b/dom/xul/templates/tests/chrome/test_tmpl_querymember3tripleswhereequals.xul new file mode 100644 index 000000000..8604a1321 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_querymember3tripleswhereequals.xul @@ -0,0 +1,60 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + query - member, 3 triples - where equals +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output" unordered="true"> + <label id="http://www.some-fictitious-zoo.com/birds/emu" value="Sarah likes Emus"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Sarah likes Polar Bears"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="query - member, 3 triples - where equals"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/humans"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?human"/> +<triple subject="?human" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?humanname"/> +<triple subject="?human" predicate="http://www.some-fictitious-zoo.com/rdf#favoriteAnimal" object="?favoriteAnimal"/> +<triple subject="?favoriteAnimal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?animalname"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?humanname" rel="equals" value="Sarah"/> +</conditions> +<action> +<label uri="?favoriteAnimal" value="?humanname likes ?animalname^s"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_querymemberandtwotriples.xul b/dom/xul/templates/tests/chrome/test_tmpl_querymemberandtwotriples.xul new file mode 100644 index 000000000..9f3f36288 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_querymemberandtwotriples.xul @@ -0,0 +1,58 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + query - member and two triples +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output" unordered="true"> + <label id="http://www.some-fictitious-zoo.com/arachnids/tarantula" value="Tarantula"/> + <label id="http://www.some-fictitious-zoo.com/reptiles/anaconda" value="Anaconda"/> + <label id="http://www.some-fictitious-zoo.com/reptiles/chameleon" value="Chameleon"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/birds/emu" value="Emu"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="query - member and two triples"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/humans"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?human"/> +<triple subject="?human" predicate="http://www.some-fictitious-zoo.com/rdf#favoriteAnimal" object="?child"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +,<action> +<label uri="?child" value="?name"/> +</action> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_querymembertriplemembertriple.xul b/dom/xul/templates/tests/chrome/test_tmpl_querymembertriplemembertriple.xul new file mode 100644 index 000000000..b5b3acf79 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_querymembertriplemembertriple.xul @@ -0,0 +1,64 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + query - member, triple, member, triple +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output" unordered="true"> + <label id="http://www.some-fictitious-zoo.com/arachnids/tarantula" value="Tarantula"/> + <label id="http://www.some-fictitious-zoo.com/reptiles/chameleon" value="Chameleon"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label step="-1" id="http://www.some-fictitious-zoo.com/birds/emu" value="Emu"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="query - member, triple, member, triple"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + targetds.Unassert(RDF.GetResource(ZOO_NS + 'humans/sarah'), + RDF.GetResource(ZOO_NS + 'rdf#pets'), + RDF.GetResource(ZOO_NS + 'sarahs-pets'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/humans"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?human"/> +<triple subject="?human" predicate="http://www.some-fictitious-zoo.com/rdf#pets" object="?pets"/> +<member container="?pets" child="?child"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +,<action> +<label uri="?child" value="?name"/> +</action> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_queryresourcematch.xul b/dom/xul/templates/tests/chrome/test_tmpl_queryresourcematch.xul new file mode 100644 index 000000000..b13ccdb7f --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_queryresourcematch.xul @@ -0,0 +1,58 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + query - resource match +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/arachnids" container="true" empty="false" value="Arachnids"/> + <label id="http://www.some-fictitious-zoo.com/birds" container="true" empty="false" value="Birds"/> + <label id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="true" value="Crustaceans"/> + <label id="http://www.some-fictitious-zoo.com/fish" container="true" empty="false" value="Fish"/> + <label id="http://www.some-fictitious-zoo.com/mammals" container="true" empty="false" value="Mammals"/> + <label id="http://www.some-fictitious-zoo.com/reptiles" container="true" empty="false" value="Reptiles"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="query - resource match"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/all-animals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?child"/> +<triple subject="?child" predicate="http://www.w3.org/1999/02/22-rdf-syntax-ns#type" object="http://www.some-fictitious-zoo.com/rdf#Class"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +,<action> +<label uri="?child" value="?name"/> +</action> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_queryreversetriple.xul b/dom/xul/templates/tests/chrome/test_tmpl_queryreversetriple.xul new file mode 100644 index 000000000..633df8834 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_queryreversetriple.xul @@ -0,0 +1,53 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + query - reverse triple +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/reptiles" container="true" empty="false" value="Reptiles"/> + <label id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="true" value="Crustaceans"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="query - reverse triple"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/humans/robert"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#keeper" object="?uri"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +,<action> +<label uri="?child" value="?name"/> +</action> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_queryselfwithtriple.xul b/dom/xul/templates/tests/chrome/test_tmpl_queryselfwithtriple.xul new file mode 100644 index 000000000..bd10d0d1f --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_queryselfwithtriple.xul @@ -0,0 +1,51 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + query - self with triple +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals" container="true" empty="false" value="Mammals"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="query - self with triple"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template> +<query id="query"> +<content uri="?uri"/> +<triple subject="?uri" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<action> +<label uri="?uri" value="?name"/> +</action> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_querysetone.xul b/dom/xul/templates/tests/chrome/test_tmpl_querysetone.xul new file mode 100644 index 000000000..ed9c93843 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_querysetone.xul @@ -0,0 +1,95 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + queryset - one +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <button step="3" id="http://www.some-fictitious-zoo.com/birds/wren" label="Wren"/> + <button id="http://www.some-fictitious-zoo.com/birds/emu" label="Emu"/> + <button step="-4" id="http://www.some-fictitious-zoo.com/birds/barnowl" label="Barn Owl"/> + <button id="http://www.some-fictitious-zoo.com/birds/raven" label="Raven"/> + <button step="2" id="http://www.some-fictitious-zoo.com/birds/archaeopteryx" label="Archaeopteryx"/> + <button step="1" id="http://www.some-fictitious-zoo.com/birds/emperorpenguin" label="Emperor Penguin"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="queryset - one"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/emperorpenguin'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Emperor Penguin'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Archaeopteryx'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/wren'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Wren'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + var removednode = container.RemoveElementAt('3', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Barn Owl'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds"> +<template id="template"> +<queryset> +<query> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<action> +<button uri="?animal" label="?name"/> +</action> +</queryset> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_querysettwo.xul b/dom/xul/templates/tests/chrome/test_tmpl_querysettwo.xul new file mode 100644 index 000000000..1f6627931 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_querysettwo.xul @@ -0,0 +1,117 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + queryset - two +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <button step="3" id="http://www.some-fictitious-zoo.com/birds/wren" label="Wren"/> + <button id="http://www.some-fictitious-zoo.com/birds/emu" label="Emu"/> + <checkbox step="-4" id="http://www.some-fictitious-zoo.com/birds/barnowl" label="Barn Owl"/> + <button id="http://www.some-fictitious-zoo.com/birds/raven" label="Raven"/> + <button step="2" id="http://www.some-fictitious-zoo.com/birds/archaeopteryx" label="Archaeopteryx"/> + <button step="1" id="http://www.some-fictitious-zoo.com/birds/emperorpenguin" label="Emperor Penguin"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="queryset - two"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/emperorpenguin'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Emperor Penguin'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.AppendElement(newnode); + expectConsoleMessage(ZOO_NS + 'birds', ZOO_NS + 'birds/emperorpenguin', true, true, + '2 matching rule 1'); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Archaeopteryx'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '4', true); + expectConsoleMessage(ZOO_NS + 'birds', ZOO_NS + 'birds/archaeopteryx', true, true, + '2 matching rule 1'); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/wren'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Wren'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '1', true); + expectConsoleMessage(ZOO_NS + 'birds', ZOO_NS + 'birds/wren', true, true, + '2 matching rule 1'); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + var removednode = container.RemoveElementAt('3', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Barn Owl'), true); + expectConsoleMessage(ZOO_NS + 'birds', ZOO_NS + 'birds/barnowl', false, false, + '2 (active query is 1)'); + expectConsoleMessage(ZOO_NS + 'birds', ZOO_NS + 'birds/barnowl', false, true, + '1 (no new active query)'); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" flags="logging" + datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds"> +<template> +<queryset> +<query> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="Barn Owl"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<action> +<checkbox uri="?animal" label="?name"/> +</action> +</queryset> +<queryset> +<query> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<action> +<button uri="?animal" label="?name"/> +</action> +</queryset> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_querysettwowithcondition.xul b/dom/xul/templates/tests/chrome/test_tmpl_querysettwowithcondition.xul new file mode 100644 index 000000000..29b19f984 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_querysettwowithcondition.xul @@ -0,0 +1,149 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + queryset - two with condition +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <hbox step="-6" id="http://www.some-fictitious-zoo.com/birds/emu"> + <label value="Emu"/> + <label value="Dromaius novaehollandiae"/> + </hbox> + <button step="6" id="http://www.some-fictitious-zoo.com/birds/emu" label="No Emus Currently"/> + <hbox step="3" id="http://www.some-fictitious-zoo.com/birds/archaeopteryx"> + <label value="Archaeopteryx"/> + <label value="Archaeopteryx lithographica"/> + </hbox> + <hbox step="-2" id="http://www.some-fictitious-zoo.com/birds/barnowl"> + <label value="Barn Owl"/> + <label value="Tyto alba"/> + </hbox> + <button step="-5" id="http://www.some-fictitious-zoo.com/birds/raven" label="No Ravens Currently"/> + <hbox step="5" id="http://www.some-fictitious-zoo.com/birds/raven"> + <label step="-7" value="Raven"/> + <label step="7" value="Crow"/> + <label value="Corvus corax"/> + </hbox> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="queryset - two with condition"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Archaeopteryx'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '2', true); + }, + // step 2 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + var removednode = container.RemoveElementAt('3', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Barn Owl'), true); + }, + // step 3 + function(targetds, root) { + targetds.Assert(RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'), + RDF.GetResource(ZOO_NS + 'rdf#species'), + RDF.GetLiteral('Archaeopteryx lithographica'), true); + }, + // step 4 + function(targetds, root) { + targetds.Assert(RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'), + RDF.GetResource(ZOO_NS + 'rdf#specimens'), + RDF.GetLiteral('5'), true); + }, + // step 5 + function(targetds, root) { + var subject = RDF.GetResource(ZOO_NS + 'birds/raven'); + var predicate = RDF.GetResource(ZOO_NS + 'rdf#specimens'); + var oldval = targetds.GetTarget(subject, predicate, true); + targetds.Change(RDF.GetResource(ZOO_NS + 'birds/raven'), + RDF.GetResource(ZOO_NS + 'rdf#specimens'), + oldval, RDF.GetLiteral('2')); + }, + // step 6 + function(targetds, root) { + var subject = RDF.GetResource(ZOO_NS + 'birds/emu'); + var predicate = RDF.GetResource(ZOO_NS + 'rdf#specimens'); + var oldval = targetds.GetTarget(subject, predicate, true); + targetds.Change(RDF.GetResource(ZOO_NS + 'birds/emu'), + RDF.GetResource(ZOO_NS + 'rdf#specimens'), + oldval, RDF.GetLiteral('0')); + }, + // step 7 + function(targetds, root) { + var subject = RDF.GetResource(ZOO_NS + 'birds/raven'); + var predicate = RDF.GetResource(ZOO_NS + 'rdf#name'); + var oldval = targetds.GetTarget(subject, predicate, true); + targetds.Change(RDF.GetResource(ZOO_NS + 'birds/raven'), + RDF.GetResource(ZOO_NS + 'rdf#name'), + oldval, RDF.GetLiteral('Crow')); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds"> +<template> +<queryset> +<query> +<content uri="?uri"/> +<member container="?uri" child="?child"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#specimens" object="?specimens"/> +</query> +<rule> +<conditions> +<where subject="?specimens" rel="equals" value="0"/> +</conditions> +<action> +<button uri="?child" label="No ?name^s Currently"/> +</action> +</rule> +</queryset> +<queryset> +<query> +<content uri="?uri"/> +<member container="?uri" child="?child"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#species" object="?species"/> +</query> +<action> +<hbox uri="?child"> +<label value="?name^?unknown"/> +<label value="?species^?specimens"/> +</hbox> +</action> +</queryset> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_querysyntax.xul b/dom/xul/templates/tests/chrome/test_tmpl_querysyntax.xul new file mode 100644 index 000000000..713cb371c --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_querysyntax.xul @@ -0,0 +1,63 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + query syntax +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label step="-1" id="http://www.some-fictitious-zoo.com/arachnids/tarantula" value="Tarantula"/> + <label step="1" id="http://www.some-fictitious-zoo.com/arachnids/tarantula" value="Hairy Spider"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="query syntax"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var subject = RDF.GetResource(ZOO_NS + 'arachnids/tarantula'); + var predicate = RDF.GetResource(ZOO_NS + 'rdf#name'); + var oldval = targetds.GetTarget(subject, predicate, true); + targetds.Change(RDF.GetResource(ZOO_NS + 'arachnids/tarantula'), + RDF.GetResource(ZOO_NS + 'rdf#name'), + oldval, RDF.GetLiteral('Hairy Spider')); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/arachnids"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_querysyntaxmultiplerules.xul b/dom/xul/templates/tests/chrome/test_tmpl_querysyntaxmultiplerules.xul new file mode 100644 index 000000000..e1ac87c72 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_querysyntaxmultiplerules.xul @@ -0,0 +1,115 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + query syntax - multiple rules +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label step="-2" id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <checkbox step="2" id="http://www.some-fictitious-zoo.com/mammals/lion" label="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <checkbox id="http://www.some-fictitious-zoo.com/mammals/africanelephant" label="African Elephant"/> + <label step="5" id="http://www.some-fictitious-zoo.com/mammals/chimpanzee" value="Chimpanzee"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <checkbox step="-1" id="http://www.some-fictitious-zoo.com/mammals/polarbear" label="Polar Bear"/> + <label step="1" id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + <label step="-3" id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <checkbox id="http://www.some-fictitious-zoo.com/mammals/gorilla" label="Gorilla"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="query syntax - multiple rules"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var subject = RDF.GetResource(ZOO_NS + 'mammals/polarbear'); + var predicate = RDF.GetResource(ZOO_NS + 'rdf#specimens'); + var oldval = targetds.GetTarget(subject, predicate, true); + targetds.Change(RDF.GetResource(ZOO_NS + 'mammals/polarbear'), + RDF.GetResource(ZOO_NS + 'rdf#specimens'), + oldval, RDF.GetLiteral('5')); + }, + // step 2 + function(targetds, root) { + var subject = RDF.GetResource(ZOO_NS + 'mammals/lion'); + var predicate = RDF.GetResource(ZOO_NS + 'rdf#specimens'); + var oldval = targetds.GetTarget(subject, predicate, true); + targetds.Change(RDF.GetResource(ZOO_NS + 'mammals/lion'), + RDF.GetResource(ZOO_NS + 'rdf#specimens'), + oldval, RDF.GetLiteral('9')); + }, + // step 3 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + var removednode = container.RemoveElementAt('7', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Nine-banded Armadillo'), true); + }, + // step 4 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/chimpanzee'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Chimpanzee'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '4', true); + }, + // step 5 + function(targetds, root) { + targetds.Assert(RDF.GetResource(ZOO_NS + 'mammals/chimpanzee'), + RDF.GetResource(ZOO_NS + 'rdf#specimens'), + RDF.GetLiteral('3'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#specimens" object="?specimens"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?specimens" rel="greater" value="6"/> +</conditions> +<action> +<checkbox uri="?animal" label="?name"/> +</action> +</rule> +<rule id="rule2"> +<action> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_querysyntaxmultiplerulesfirstconditionall.xul b/dom/xul/templates/tests/chrome/test_tmpl_querysyntaxmultiplerulesfirstconditionall.xul new file mode 100644 index 000000000..4509c1018 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_querysyntaxmultiplerulesfirstconditionall.xul @@ -0,0 +1,71 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + query syntax - multiple rules first condition all +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <checkbox id="http://www.some-fictitious-zoo.com/mammals/lion" label="Lion"/> + <checkbox id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" label="HIPPOPOTAMUS"/> + <checkbox id="http://www.some-fictitious-zoo.com/mammals/africanelephant" label="African Elephant"/> + <checkbox id="http://www.some-fictitious-zoo.com/mammals/llama" label="LLAMA"/> + <checkbox id="http://www.some-fictitious-zoo.com/mammals/polarbear" label="Polar Bear"/> + <checkbox id="http://www.some-fictitious-zoo.com/mammals/aardvark" label="aardvark"/> + <checkbox id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" label="Nine-banded Armadillo"/> + <checkbox id="http://www.some-fictitious-zoo.com/mammals/gorilla" label="Gorilla"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="query syntax - multiple rules first condition all"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#specimens" object="?specimens"/> +</query> +<rule> +<where subject="?animal" rel="contains" value="zoo"/> +<action> +<checkbox uri="?animal" label="?name"/> +</action> +</rule> +<rule id="rule2"> +<conditions> +<where subject="?name" rel="contains" value="an"/> +</conditions> +<action> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_querysyntaxmultiplerulestwoconditions.xul b/dom/xul/templates/tests/chrome/test_tmpl_querysyntaxmultiplerulestwoconditions.xul new file mode 100644 index 000000000..68060c6b4 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_querysyntaxmultiplerulestwoconditions.xul @@ -0,0 +1,113 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + query syntax - multiple rules two conditions +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <checkbox step="2" id="http://www.some-fictitious-zoo.com/mammals/lion" label="Lion"/> + <checkbox id="http://www.some-fictitious-zoo.com/mammals/africanelephant" label="African Elephant"/> + <label step="5" id="http://www.some-fictitious-zoo.com/mammals/chimpanzee" value="Chimpanzee"/> + <checkbox step="-1" id="http://www.some-fictitious-zoo.com/mammals/polarbear" label="Polar Bear"/> + <label step="-3" id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <checkbox id="http://www.some-fictitious-zoo.com/mammals/gorilla" label="Gorilla"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="query syntax - multiple rules two conditions"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var subject = RDF.GetResource(ZOO_NS + 'mammals/polarbear'); + var predicate = RDF.GetResource(ZOO_NS + 'rdf#specimens'); + var oldval = targetds.GetTarget(subject, predicate, true); + targetds.Change(RDF.GetResource(ZOO_NS + 'mammals/polarbear'), + RDF.GetResource(ZOO_NS + 'rdf#specimens'), + oldval, RDF.GetLiteral('5')); + }, + // step 2 + function(targetds, root) { + var subject = RDF.GetResource(ZOO_NS + 'mammals/lion'); + var predicate = RDF.GetResource(ZOO_NS + 'rdf#specimens'); + var oldval = targetds.GetTarget(subject, predicate, true); + targetds.Change(RDF.GetResource(ZOO_NS + 'mammals/lion'), + RDF.GetResource(ZOO_NS + 'rdf#specimens'), + oldval, RDF.GetLiteral('9')); + }, + // step 3 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + var removednode = container.RemoveElementAt('7', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Nine-banded Armadillo'), true); + }, + // step 4 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/chimpanzee'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Chimpanzee'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '4', true); + }, + // step 5 + function(targetds, root) { + targetds.Assert(RDF.GetResource(ZOO_NS + 'mammals/chimpanzee'), + RDF.GetResource(ZOO_NS + 'rdf#specimens'), + RDF.GetLiteral('3'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#specimens" object="?specimens"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?specimens" rel="greater" value="6"/> +</conditions> +<action> +<checkbox uri="?animal" label="?name"/> +</action> +</rule> +<rule id="rule2"> +<conditions> +<where subject="?name" rel="contains" value="an"/> +</conditions> +<action> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_querytripleandmembermerge.xul b/dom/xul/templates/tests/chrome/test_tmpl_querytripleandmembermerge.xul new file mode 100644 index 000000000..7abb67e1c --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_querytripleandmembermerge.xul @@ -0,0 +1,55 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + query - triple and member merge +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output" unordered="true"> + <label id="http://www.some-fictitious-zoo.com/reptiles/chameleon" value="Chameleon"/> + <label id="http://www.some-fictitious-zoo.com/arachnids/tarantula" value="Tarantula"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="query - triple and member merge"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/humans/robert"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<triple subject="?uri" predicate="http://www.some-fictitious-zoo.com/rdf#favoriteAnimal" object="?child"/> +<triple subject="?uri" predicate="http://www.some-fictitious-zoo.com/rdf#pets" object="?pets"/> +<member container="?pets" child="?child"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +,<action> +<label uri="?child" value="?name"/> +</action> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_querytripleobjecttosubject.xul b/dom/xul/templates/tests/chrome/test_tmpl_querytripleobjecttosubject.xul new file mode 100644 index 000000000..3998f1796 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_querytripleobjecttosubject.xul @@ -0,0 +1,67 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + query - triple object to subject +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label step="-1" id="http://www.some-fictitious-zoo.com/humans/robert" value="Robert"/> + <label step="2" id="http://www.some-fictitious-zoo.com/humans/robert" value="Robert"/> + <label id="http://www.some-fictitious-zoo.com/humans/sarah" value="Sarah"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="query - triple object to subject"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = true; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + targetds.Unassert(RDF.GetResource(ZOO_NS + 'humans/robert'), + RDF.GetResource(ZOO_NS + 'rdf#favoriteAnimal'), + RDF.GetResource(ZOO_NS + 'arachnids/tarantula'), true); + }, + // step 2 + function(targetds, root) { + targetds.Assert(RDF.GetResource(ZOO_NS + 'humans/robert'), + RDF.GetResource(ZOO_NS + 'rdf#favoriteAnimal'), + RDF.GetResource(ZOO_NS + 'mammals/lion'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/arachnids/tarantula"> +<template id="template"> +<query> +<content uri="?uri"/> +<triple subject="?human" predicate="http://www.some-fictitious-zoo.com/rdf#favoriteAnimal" object="?uri"/> +<triple subject="?human" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?humanname"/> +</query> +<action> +<label uri="?human" value="?humanname"/> +</action> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_querytwomembers.xul b/dom/xul/templates/tests/chrome/test_tmpl_querytwomembers.xul new file mode 100644 index 000000000..df487a26f --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_querytwomembers.xul @@ -0,0 +1,107 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + query - two members +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output" unordered="true"> + <label id="http://www.some-fictitious-zoo.com/arachnids/tarantula" value="Tarantula"/> + <label step="3" id="http://www.some-fictitious-zoo.com/birds/wren" value="Wren"/> + <label id="http://www.some-fictitious-zoo.com/birds/emu" value="Emu"/> + <label step="-4" id="http://www.some-fictitious-zoo.com/birds/barnowl" value="Barn Owl"/> + <label id="http://www.some-fictitious-zoo.com/birds/raven" value="Raven"/> + <label step="2" id="http://www.some-fictitious-zoo.com/birds/archaeopteryx" value="Archaeopteryx"/> + <label step="1" id="http://www.some-fictitious-zoo.com/birds/emperorpenguin" value="Emperor Penguin"/> + <label id="http://www.some-fictitious-zoo.com/fish/cod" value="Cod"/> + <label id="http://www.some-fictitious-zoo.com/fish/swordfish" value="Swordfish"/> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + <label id="http://www.some-fictitious-zoo.com/reptiles/anaconda" value="Anaconda"/> + <label id="http://www.some-fictitious-zoo.com/reptiles/chameleon" value="Chameleon"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="query - two members"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/emperorpenguin'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Emperor Penguin'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Archaeopteryx'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/wren'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Wren'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + var removednode = container.RemoveElementAt('3', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Barn Owl'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/all-animals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?childone"/> +<member container="?childone" child="?child"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +,<action> +<label uri="?child" value="?name"/> +</action> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_querytwomembersfiltered.xul b/dom/xul/templates/tests/chrome/test_tmpl_querytwomembersfiltered.xul new file mode 100644 index 000000000..fdd1efb20 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_querytwomembersfiltered.xul @@ -0,0 +1,54 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + query - two members filtered +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="Mammals"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="query - two members filtered"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/all-animals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?childone"/> +<triple subject="?childone" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +<member container="?childone" child="?child"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="LLAMA"/> +</query> +,<action> +<label uri="?child" value="?name"/> +</action> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_querytwotriples.xul b/dom/xul/templates/tests/chrome/test_tmpl_querytwotriples.xul new file mode 100644 index 000000000..83887358f --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_querytwotriples.xul @@ -0,0 +1,55 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + query - two triples +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output" unordered="true"> + <label id="http://www.some-fictitious-zoo.com/arachnids/tarantula" value="Tarantula"/> + <label id="http://www.some-fictitious-zoo.com/reptiles/anaconda" value="Anaconda"/> + <label id="http://www.some-fictitious-zoo.com/reptiles/chameleon" value="Chameleon"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="query - two triples"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/humans/robert"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<triple subject="?uri" predicate="http://www.some-fictitious-zoo.com/rdf#favoriteAnimal" object="?child"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +,<action> +<label uri="?child" value="?name"/> +</action> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_queryupwardsmember.xul b/dom/xul/templates/tests/chrome/test_tmpl_queryupwardsmember.xul new file mode 100644 index 000000000..5d3658535 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_queryupwardsmember.xul @@ -0,0 +1,54 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + query - upwards member +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output" unordered="true"> + <label id="http://www.some-fictitious-zoo.com/marked" container="true" empty="false" value="Marked"/> + <label id="http://www.some-fictitious-zoo.com/birds" container="true" empty="false" value="Birds"/> + <label id="http://www.some-fictitious-zoo.com/sarahs-pets" container="true" empty="false" value="Sarah's Pets"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="query - upwards member"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds/emu" flags="dont-recurse"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?child" child="?uri"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +,<action> +<label uri="?child" value="?name"/> +</action> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_queryupwardsmembertripleandfilteringtriple.xul b/dom/xul/templates/tests/chrome/test_tmpl_queryupwardsmembertripleandfilteringtriple.xul new file mode 100644 index 000000000..1cfb689b0 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_queryupwardsmembertripleandfilteringtriple.xul @@ -0,0 +1,55 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + query - upwards member, triple and filtering triple +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output" unordered="true"> + <label id="http://www.some-fictitious-zoo.com/reptiles/anaconda" value="Anaconda"/> + <label id="http://www.some-fictitious-zoo.com/reptiles/chameleon" value="Chameleon"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="query - upwards member, triple and filtering triple"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/humans/robert"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<triple subject="?uri" predicate="http://www.some-fictitious-zoo.com/rdf#favoriteAnimal" object="?child"/> +<member container="?reptiles" child="?child"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +<triple subject="?reptiles" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="Reptiles"/> +</query> +,<action> +<label uri="?child" value="?name"/> +</action> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_querywithemptyconditions.xul b/dom/xul/templates/tests/chrome/test_tmpl_querywithemptyconditions.xul new file mode 100644 index 000000000..265a5bb14 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_querywithemptyconditions.xul @@ -0,0 +1,117 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + query with empty conditions +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label step="3" id="http://www.some-fictitious-zoo.com/mammals/zebra" value="Zebra"/> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label step="-4" id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label step="2" id="http://www.some-fictitious-zoo.com/mammals/koala" value="Koala"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + <label step="1" id="http://www.some-fictitious-zoo.com/mammals/arctichare" value="Arctic Hare"/> + <label step="5" id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="query with empty conditions"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/arctichare'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Arctic Hare'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/koala'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Koala'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/zebra'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Zebra'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + var removednode = container.RemoveElementAt('4', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('African Elephant'), true); + }, + // step 5 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/africanelephant'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('African Elephant'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.AppendElement(newnode); + }, + // step 6 + function(targetds, root) { + targetds.Assert(RDF.GetResource(ZOO_NS + 'mammals/koala'), + RDF.GetResource(ZOO_NS + 'rdf#specimensAsString'), + RDF.GetLiteral('8'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"/> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_referenceasmember.xul b/dom/xul/templates/tests/chrome/test_tmpl_referenceasmember.xul new file mode 100644 index 000000000..bf67ebf9d --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_referenceasmember.xul @@ -0,0 +1,65 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + reference as member +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label step="-1" id="http://www.some-fictitious-zoo.com/arachnids" container="true" empty="false" value="Tarantula"/> + <label step="1" id="http://www.some-fictitious-zoo.com/arachnids" container="true" empty="false" value="Hairy Spider"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="reference as member"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var subject = RDF.GetResource(ZOO_NS + 'arachnids/tarantula'); + var predicate = RDF.GetResource(ZOO_NS + 'rdf#name'); + var oldval = targetds.GetTarget(subject, predicate, true); + targetds.Change(RDF.GetResource(ZOO_NS + 'arachnids/tarantula'), + RDF.GetResource(ZOO_NS + 'rdf#name'), + oldval, RDF.GetLiteral('Hairy Spider')); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/arachnids"> +<template id="template"> +<rule id="rule"> +<conditions id="conditions"> +<content uri="?start"/> +<member container="?start" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</conditions> +<action id="action"> +<label uri="?start" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_regenerate.xul b/dom/xul/templates/tests/chrome/test_tmpl_regenerate.xul new file mode 100644 index 000000000..2c9aedc7b --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_regenerate.xul @@ -0,0 +1,49 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + Regenerate template by removing and appending elements +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_regenerate()" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +function test_regenerate() +{ + var container = document.getElementById("container"); + var node = container.firstChild; + + if (node.childNodes.length != 2) { + setTimeout(test_regenerate, 50); + return; + } + + container.removeChild(node); + is(node.childNodes.length, 1, "childNodes after removeChild"); + container.appendChild(node); + is(node.childNodes.length, 2, "childNodes after appendChild"); + SimpleTest.finish(); +} + +]]> +</script> + +<vbox id="container"> +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="animals.rdf" ref="http://www.some-fictitious-zoo.com/birds"> +<template zoo:name="Barn Owl" xmlns:zoo="http://www.some-fictitious-zoo.com/rdf#"> +<label uri="rdf:*" value="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +</template> +</vbox> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_selfgenerationextendedsyntax.xul b/dom/xul/templates/tests/chrome/test_tmpl_selfgenerationextendedsyntax.xul new file mode 100644 index 000000000..712cd4a44 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_selfgenerationextendedsyntax.xul @@ -0,0 +1,66 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + self generation - extended syntax +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label step="-1" id="http://www.some-fictitious-zoo.com/arachnids/tarantula" value="Tarantula"/> + <label step="1" id="http://www.some-fictitious-zoo.com/arachnids/tarantula" value="Hairy Spider"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="self generation - extended syntax"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var subject = RDF.GetResource(ZOO_NS + 'arachnids/tarantula'); + var predicate = RDF.GetResource(ZOO_NS + 'rdf#name'); + var oldval = targetds.GetTarget(subject, predicate, true); + targetds.Change(RDF.GetResource(ZOO_NS + 'arachnids/tarantula'), + RDF.GetResource(ZOO_NS + 'rdf#name'), + oldval, RDF.GetLiteral('Hairy Spider')); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/arachnids/tarantula"> +<template id="template"> +<query> +<content uri="?uri"/> +</query> +<rule> +<bindings> +<binding subject="?uri" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</bindings> +<action> +<label uri="?uri" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_selfgenerationsimplesyntax.xul b/dom/xul/templates/tests/chrome/test_tmpl_selfgenerationsimplesyntax.xul new file mode 100644 index 000000000..e644d16ac --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_selfgenerationsimplesyntax.xul @@ -0,0 +1,53 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + self generation - simple syntax +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"/> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="self generation - simple syntax"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var subject = RDF.GetResource(ZOO_NS + 'arachnids/tarantula'); + var predicate = RDF.GetResource(ZOO_NS + 'rdf#name'); + var oldval = targetds.GetTarget(subject, predicate, true); + targetds.Change(RDF.GetResource(ZOO_NS + 'arachnids/tarantula'), + RDF.GetResource(ZOO_NS + 'rdf#name'), + oldval, RDF.GetLiteral('Hairy Spider')); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/arachnids/tarantula"> +<template id="template"> +<button uri="?" label="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxenclosedinacontainer.xul b/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxenclosedinacontainer.xul new file mode 100644 index 000000000..18e8b72a9 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxenclosedinacontainer.xul @@ -0,0 +1,90 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + simple syntax enclosed in a container +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <hbox> + <label step="3" id="http://www.some-fictitious-zoo.com/birds/wren" value="Wren"/> + <label id="http://www.some-fictitious-zoo.com/birds/emu" value="Emu"/> + <label step="-4" id="http://www.some-fictitious-zoo.com/birds/barnowl" value="Barn Owl"/> + <label id="http://www.some-fictitious-zoo.com/birds/raven" value="Raven"/> + <label step="2" id="http://www.some-fictitious-zoo.com/birds/archaeopteryx" value="Archaeopteryx"/> + <label step="1" id="http://www.some-fictitious-zoo.com/birds/emperorpenguin" value="Emperor Penguin"/> + </hbox> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="simple syntax enclosed in a container"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/emperorpenguin'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Emperor Penguin'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Archaeopteryx'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/wren'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Wren'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + var removednode = container.RemoveElementAt('3', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Barn Owl'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds"> +<template id="template"> +<hbox> +<label uri="rdf:*" value="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +</hbox> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxenclosedinacontainerwitharule.xul b/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxenclosedinacontainerwitharule.xul new file mode 100644 index 000000000..5582e4845 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxenclosedinacontainerwitharule.xul @@ -0,0 +1,58 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + simple syntax enclosed in a container with a rule +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <hbox id="http://www.some-fictitious-zoo.com/birds/emu"> + <label value="Emu"/> + </hbox> + <hbox id="http://www.some-fictitious-zoo.com/birds/barnowl"> + <label value="Barn Owl"/> + </hbox> + <hbox id="http://www.some-fictitious-zoo.com/birds/raven"> + <label value="Raven"/> + </hbox> + <label step="2" id="http://www.some-fictitious-zoo.com/birds/emperorpenguin" value="Emperor Penguin"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="simple syntax enclosed in a container with a rule"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds"> +<template id="template"> +<rule> +<hbox uri="rdf:*"> +<label value="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +</hbox> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxfilter.xul b/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxfilter.xul new file mode 100644 index 000000000..8715d3cc1 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxfilter.xul @@ -0,0 +1,45 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + simple syntax - filter +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/birds/barnowl" value="Barn Owl"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="simple syntax - filter"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds"> +<template zoo:name="Barn Owl" xmlns:zoo="http://www.some-fictitious-zoo.com/rdf#"> +<label uri="rdf:*" value="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxfilterwithmultiplerules.xul b/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxfilterwithmultiplerules.xul new file mode 100644 index 000000000..ba8591f60 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxfilterwithmultiplerules.xul @@ -0,0 +1,97 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + simple syntax - filter with multiple rules +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <button id="http://www.some-fictitious-zoo.com/birds/emu" label="Emu"/> + <label step="-1" id="http://www.some-fictitious-zoo.com/birds/barnowl" value="Barn Owl"/> + <button step="4,-5" id="http://www.some-fictitious-zoo.com/birds/barnowl" label="Spooky Bird"/> + <label step="5" id="http://www.some-fictitious-zoo.com/birds/barnowl" value="Barn Owl"/> + <button step="1,-3" id="http://www.some-fictitious-zoo.com/birds/barnowl" label="Spooky Bird"/> + <button id="http://www.some-fictitious-zoo.com/birds/raven" label="Raven"/> + <label step="2" id="http://www.some-fictitious-zoo.com/birds/syntheticbarnowl" value="Barn Owl"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="simple syntax - filter with multiple rules"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var subject = RDF.GetResource(ZOO_NS + 'birds/barnowl'); + var predicate = RDF.GetResource(ZOO_NS + 'rdf#name'); + var oldval = targetds.GetTarget(subject, predicate, true); + targetds.Change(RDF.GetResource(ZOO_NS + 'birds/barnowl'), + RDF.GetResource(ZOO_NS + 'rdf#name'), + oldval, RDF.GetLiteral('Spooky Bird')); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/syntheticbarnowl'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Barn Owl'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.AppendElement(newnode); + }, + // step 3 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + var removednode = container.RemoveElementAt('2', true); + }, + // step 4 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/barnowl'); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '2', true); + }, + // step 5 + function(targetds, root) { + var subject = RDF.GetResource(ZOO_NS + 'birds/barnowl'); + var predicate = RDF.GetResource(ZOO_NS + 'rdf#name'); + var oldval = targetds.GetTarget(subject, predicate, true); + targetds.Change(RDF.GetResource(ZOO_NS + 'birds/barnowl'), + RDF.GetResource(ZOO_NS + 'rdf#name'), + oldval, RDF.GetLiteral('Barn Owl')); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds"> +<template> +<rule zoo:name="Barn Owl" xmlns:zoo="http://www.some-fictitious-zoo.com/rdf#"> +<label uri="rdf:*" value="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +</rule> +<rule> +<button uri="rdf:*" label="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxfilterwithrule.xul b/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxfilterwithrule.xul new file mode 100644 index 000000000..a2a3eb55e --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxfilterwithrule.xul @@ -0,0 +1,98 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + simple syntax - filter with rule +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <hbox step="-1" id="http://www.some-fictitious-zoo.com/birds/barnowl"> + <label value="Barn Owl"/> + </hbox> + <hbox step="5" id="http://www.some-fictitious-zoo.com/birds/barnowl"> + <label value="Barn Owl"/> + </hbox> + <hbox step="2" id="http://www.some-fictitious-zoo.com/birds/syntheticbarnowl"> + <label value="Barn Owl"/> + </hbox> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="simple syntax - filter with rule"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var subject = RDF.GetResource(ZOO_NS + 'birds/barnowl'); + var predicate = RDF.GetResource(ZOO_NS + 'rdf#name'); + var oldval = targetds.GetTarget(subject, predicate, true); + targetds.Change(RDF.GetResource(ZOO_NS + 'birds/barnowl'), + RDF.GetResource(ZOO_NS + 'rdf#name'), + oldval, RDF.GetLiteral('Spooky Bird')); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/syntheticbarnowl'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Barn Owl'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.AppendElement(newnode); + }, + // step 3 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + var removednode = container.RemoveElementAt('2', true); + }, + // step 4 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/barnowl'); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '2', true); + }, + // step 5 + function(targetds, root) { + var subject = RDF.GetResource(ZOO_NS + 'birds/barnowl'); + var predicate = RDF.GetResource(ZOO_NS + 'rdf#name'); + var oldval = targetds.GetTarget(subject, predicate, true); + targetds.Change(RDF.GetResource(ZOO_NS + 'birds/barnowl'), + RDF.GetResource(ZOO_NS + 'rdf#name'), + oldval, RDF.GetLiteral('Barn Owl')); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds"> +<template> +<rule zoo:name="Barn Owl" xmlns:zoo="http://www.some-fictitious-zoo.com/rdf#"> +<hbox uri="rdf:*"> +<label value="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +</hbox> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxiteratingoverasinglevalue.xul b/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxiteratingoverasinglevalue.xul new file mode 100644 index 000000000..e8e05acf2 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxiteratingoverasinglevalue.xul @@ -0,0 +1,86 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + simple syntax iterating over a single value +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label step="3" id="http://www.some-fictitious-zoo.com/birds/wren" value="Wren"/> + <label id="http://www.some-fictitious-zoo.com/birds/emu" value="Emu"/> + <label step="-4" id="http://www.some-fictitious-zoo.com/birds/barnowl" value="Barn Owl"/> + <label id="http://www.some-fictitious-zoo.com/birds/raven" value="Raven"/> + <label step="2" id="http://www.some-fictitious-zoo.com/birds/archaeopteryx" value="Archaeopteryx"/> + <label step="1" id="http://www.some-fictitious-zoo.com/birds/emperorpenguin" value="Emperor Penguin"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="simple syntax iterating over a single value"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/emperorpenguin'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Emperor Penguin'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Archaeopteryx'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/wren'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Wren'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + var removednode = container.RemoveElementAt('3', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Barn Owl'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds"> +<template id="template"> +<label uri="rdf:*" value="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxusinganinterveningcontainer.xul b/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxusinganinterveningcontainer.xul new file mode 100644 index 000000000..5aa5172c9 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxusinganinterveningcontainer.xul @@ -0,0 +1,90 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + simple syntax using an intervening container +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <groupbox> + <label step="3" id="http://www.some-fictitious-zoo.com/birds/wren" value="Wren"/> + <label id="http://www.some-fictitious-zoo.com/birds/emu" value="Emu"/> + <label step="-4" id="http://www.some-fictitious-zoo.com/birds/barnowl" value="Barn Owl"/> + <label id="http://www.some-fictitious-zoo.com/birds/raven" value="Raven"/> + <label step="2" id="http://www.some-fictitious-zoo.com/birds/archaeopteryx" value="Archaeopteryx"/> + <label step="1" id="http://www.some-fictitious-zoo.com/birds/emperorpenguin" value="Emperor Penguin"/> + </groupbox> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="simple syntax using an intervening container"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/emperorpenguin'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Emperor Penguin'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Archaeopteryx'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/wren'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Wren'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + var removednode = container.RemoveElementAt('3', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Barn Owl'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds"> +<template id="template"> +<groupbox> +<label uri="rdf:*" value="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +</groupbox> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxusingatextnode.xul b/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxusingatextnode.xul new file mode 100644 index 000000000..3fb1fdfa0 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxusingatextnode.xul @@ -0,0 +1,50 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + simple syntax using a textnode +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <description step="3" id="http://www.some-fictitious-zoo.com/birds/wren">Wren</description> + <description id="http://www.some-fictitious-zoo.com/birds/emu">Emu</description> + <description step="-4" id="http://www.some-fictitious-zoo.com/birds/barnowl">Barn Owl</description> + <description id="http://www.some-fictitious-zoo.com/birds/raven">Raven</description> + <description step="2" id="http://www.some-fictitious-zoo.com/birds/archaeopteryx">Archaeopteryx</description> + <description step="1" id="http://www.some-fictitious-zoo.com/birds/emperorpenguin">Emperor Penguin</description> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="simple syntax using a textnode"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds"> +<template id="template"> +<description uri="rdf:*"><textnode value="rdf:http://www.some-fictitious-zoo.com/rdf#name"/></description> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxusingcontainerasthegenerationelement.xul b/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxusingcontainerasthegenerationelement.xul new file mode 100644 index 000000000..238540fb4 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxusingcontainerasthegenerationelement.xul @@ -0,0 +1,100 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + simple syntax using container as the generation element +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <hbox step="3" id="http://www.some-fictitious-zoo.com/birds/wren"> + <label value="Wren"/> + </hbox> + <hbox id="http://www.some-fictitious-zoo.com/birds/emu"> + <label value="Emu"/> + </hbox> + <hbox step="-4" id="http://www.some-fictitious-zoo.com/birds/barnowl"> + <label value="Barn Owl"/> + </hbox> + <hbox id="http://www.some-fictitious-zoo.com/birds/raven"> + <label value="Raven"/> + </hbox> + <hbox step="2" id="http://www.some-fictitious-zoo.com/birds/archaeopteryx"> + <label value="Archaeopteryx"/> + </hbox> + <hbox step="1" id="http://www.some-fictitious-zoo.com/birds/emperorpenguin"> + <label value="Emperor Penguin"/> + </hbox> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="simple syntax using container as the generation element"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/emperorpenguin'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Emperor Penguin'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Archaeopteryx'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/wren'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Wren'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + var removednode = container.RemoveElementAt('3', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Barn Owl'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds"> +<template id="template"> +<hbox uri="rdf:*"> +<label value="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +</hbox> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxusingdontrecurse.xul b/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxusingdontrecurse.xul new file mode 100644 index 000000000..31aa9c404 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxusingdontrecurse.xul @@ -0,0 +1,52 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + simple syntax using dont-recurse +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <hbox id="http://www.some-fictitious-zoo.com/arachnids" container="true" empty="false"> + <label value="Arachnids"/> + </hbox> + <hbox id="http://www.some-fictitious-zoo.com/birds" container="true" empty="false"> + <label value="Birds"/> + </hbox> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="simple syntax using dont-recurse"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/some-animals" flags="dont-recurse"> +<template id="template"> +<hbox uri="rdf:*"> +<label value="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +</hbox> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxusingrecursivegeneration.xul b/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxusingrecursivegeneration.xul new file mode 100644 index 000000000..3ef3b1d3d --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxusingrecursivegeneration.xul @@ -0,0 +1,109 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + simple syntax using recursive generation +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <hbox id="http://www.some-fictitious-zoo.com/arachnids" container="true" empty="false"> + <label value="Arachnids"/> + <hbox id="http://www.some-fictitious-zoo.com/arachnids/tarantula"> + <label value="Tarantula"/> + </hbox> + </hbox> + <hbox id="http://www.some-fictitious-zoo.com/birds" container="true" empty="false"> + <label value="Birds"/> + <hbox step="3" id="http://www.some-fictitious-zoo.com/birds/wren"> + <label value="Wren"/> + </hbox> + <hbox id="http://www.some-fictitious-zoo.com/birds/emu"> + <label value="Emu"/> + </hbox> + <hbox step="-4" id="http://www.some-fictitious-zoo.com/birds/barnowl"> + <label value="Barn Owl"/> + </hbox> + <hbox id="http://www.some-fictitious-zoo.com/birds/raven"> + <label value="Raven"/> + </hbox> + <hbox step="2" id="http://www.some-fictitious-zoo.com/birds/archaeopteryx"> + <label value="Archaeopteryx"/> + </hbox> + <hbox step="1" id="http://www.some-fictitious-zoo.com/birds/emperorpenguin"> + <label value="Emperor Penguin"/> + </hbox> + </hbox> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="simple syntax using recursive generation"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/emperorpenguin'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Emperor Penguin'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Archaeopteryx'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/wren'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Wren'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + var removednode = container.RemoveElementAt('3', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Barn Owl'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/some-animals"> +<template id="template"> +<hbox uri="rdf:*"> +<label value="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +</hbox> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxusingrecursivegenerationagain.xul b/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxusingrecursivegenerationagain.xul new file mode 100644 index 000000000..73d7f742e --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxusingrecursivegenerationagain.xul @@ -0,0 +1,64 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + simple syntax using recursive generation again +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <hbox id="http://www.some-fictitious-zoo.com/arachnids" container="true" empty="false"> + <label value="Arachnids"/> + <hbox id="http://www.some-fictitious-zoo.com/arachnids/tarantula"> + <label value="Tarantula"/> + </hbox> + </hbox> + <hbox id="http://www.some-fictitious-zoo.com/birds" container="true" empty="false"> + <label value="Birds"/> + <hbox id="http://www.some-fictitious-zoo.com/birds/emu"> + <label value="Emu"/> + </hbox> + <hbox id="http://www.some-fictitious-zoo.com/birds/barnowl"> + <label value="Barn Owl"/> + </hbox> + <hbox id="http://www.some-fictitious-zoo.com/birds/raven"> + <label value="Raven"/> + </hbox> + </hbox> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="simple syntax using recursive generation again"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/some-animals"> +<template id="template"> +<hbox uri="rdf:*"> +<label value="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +</hbox> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxwithtwovariablesused.xul b/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxwithtwovariablesused.xul new file mode 100644 index 000000000..fccee70ce --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_simplesyntaxwithtwovariablesused.xul @@ -0,0 +1,59 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + simple syntax with two variables used +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <hbox id="http://www.some-fictitious-zoo.com/birds/emu"> + <label value="Emu"/> + <label value="12"/> + </hbox> + <hbox id="http://www.some-fictitious-zoo.com/birds/barnowl"> + <label value="Barn Owl"/> + <label value="4"/> + </hbox> + <hbox id="http://www.some-fictitious-zoo.com/birds/raven"> + <label value="Raven"/> + <label value="0"/> + </hbox> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="simple syntax with two variables used"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds"> +<template id="template"> +<hbox uri="rdf:*"> +<label value="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +<label value="rdf:http://www.some-fictitious-zoo.com/rdf#specimens"/> +</hbox> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_simplevariablesubstitutioncaretsatbeginningandend.xul b/dom/xul/templates/tests/chrome/test_tmpl_simplevariablesubstitutioncaretsatbeginningandend.xul new file mode 100644 index 000000000..2ca63da19 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_simplevariablesubstitutioncaretsatbeginningandend.xul @@ -0,0 +1,45 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + simple variable substitution - carets at beginning and end +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/arachnids/tarantula" value="^^Before Tarantula^"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="simple variable substitution - carets at beginning and end"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/arachnids"> +<template id="template"> +<label uri="rdf:*" value="^^Before rdf:http://www.some-fictitious-zoo.com/rdf#name^^"/> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_simplevariablesubstitutioncaretsubstitution.xul b/dom/xul/templates/tests/chrome/test_tmpl_simplevariablesubstitutioncaretsubstitution.xul new file mode 100644 index 000000000..abc28ff0f --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_simplevariablesubstitutioncaretsubstitution.xul @@ -0,0 +1,45 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + simple variable substitution - caret substitution +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/arachnids/tarantula" value="Tarantula^ and^^ lots ^ more"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="simple variable substitution - caret substitution"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/arachnids"> +<template id="template"> +<label uri="rdf:*" value="rdf:http://www.some-fictitious-zoo.com/rdf#name^^ and^^ lots ^ more"/> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_simplevariablesubstitutionnovariable.xul b/dom/xul/templates/tests/chrome/test_tmpl_simplevariablesubstitutionnovariable.xul new file mode 100644 index 000000000..4d2404aca --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_simplevariablesubstitutionnovariable.xul @@ -0,0 +1,45 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + simple variable substitution - no variable +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/arachnids/tarantula" value="Name"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="simple variable substitution - no variable"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/arachnids"> +<template id="template"> +<label uri="rdf:*" value="Name"/> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_simplevariablesubstitutionquestionmarkaspartofvariable.xul b/dom/xul/templates/tests/chrome/test_tmpl_simplevariablesubstitutionquestionmarkaspartofvariable.xul new file mode 100644 index 000000000..0dd1a7ccf --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_simplevariablesubstitutionquestionmarkaspartofvariable.xul @@ -0,0 +1,45 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + simple variable substitution - question mark as part of variable +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/arachnids/tarantula" value="Name is "/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="simple variable substitution - question mark as part of variable"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/arachnids"> +<template id="template"> +<label uri="rdf:*" value="Name is rdf:http://www.some-fictitious-zoo.com/rdf#name?"/> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_simplevariablesubstitutionquestionmarksubstitution.xul b/dom/xul/templates/tests/chrome/test_tmpl_simplevariablesubstitutionquestionmarksubstitution.xul new file mode 100644 index 000000000..c31401dd4 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_simplevariablesubstitutionquestionmarksubstitution.xul @@ -0,0 +1,56 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + simple variable substitution - question mark substitution +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label step="-1" id="http://www.some-fictitious-zoo.com/arachnids/tarantula" value="Are you a Tarantula ?"/> + <label step="1" id="http://www.some-fictitious-zoo.com/arachnids/tarantula" value="Are you a Hairy Spider ?"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="simple variable substitution - question mark substitution"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var subject = RDF.GetResource(ZOO_NS + 'arachnids/tarantula'); + var predicate = RDF.GetResource(ZOO_NS + 'rdf#name'); + var oldval = targetds.GetTarget(subject, predicate, true); + targetds.Change(RDF.GetResource(ZOO_NS + 'arachnids/tarantula'), + RDF.GetResource(ZOO_NS + 'rdf#name'), + oldval, RDF.GetLiteral('Hairy Spider')); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/arachnids"> +<template id="template"> +<label uri="rdf:*" value="Are you a rdf:http://www.some-fictitious-zoo.com/rdf#name ?sample ??"/> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_simplevariablesubstitutiontextandvariable.xul b/dom/xul/templates/tests/chrome/test_tmpl_simplevariablesubstitutiontextandvariable.xul new file mode 100644 index 000000000..6d2bc806b --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_simplevariablesubstitutiontextandvariable.xul @@ -0,0 +1,56 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + simple variable substitution - text and variable +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label step="-1" id="http://www.some-fictitious-zoo.com/arachnids/tarantula" value="Name is Tarantula the Spider"/> + <label step="1" id="http://www.some-fictitious-zoo.com/arachnids/tarantula" value="Name is Hairy Spider the Spider"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="simple variable substitution - text and variable"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var subject = RDF.GetResource(ZOO_NS + 'arachnids/tarantula'); + var predicate = RDF.GetResource(ZOO_NS + 'rdf#name'); + var oldval = targetds.GetTarget(subject, predicate, true); + targetds.Change(RDF.GetResource(ZOO_NS + 'arachnids/tarantula'), + RDF.GetResource(ZOO_NS + 'rdf#name'), + oldval, RDF.GetLiteral('Hairy Spider')); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/arachnids"> +<template id="template"> +<label uri="rdf:*" value="Name is rdf:http://www.some-fictitious-zoo.com/rdf#name the Spider"/> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_simplevariablesubstitutionvariableandtextconcatenated.xul b/dom/xul/templates/tests/chrome/test_tmpl_simplevariablesubstitutionvariableandtextconcatenated.xul new file mode 100644 index 000000000..41df3d4d8 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_simplevariablesubstitutionvariableandtextconcatenated.xul @@ -0,0 +1,45 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + simple variable substitution - variable and text concatenated +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/arachnids/tarantula" value="The Tarantula's Nest"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="simple variable substitution - variable and text concatenated"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/arachnids"> +<template id="template"> +<label uri="rdf:*" value="The rdf:http://www.some-fictitious-zoo.com/rdf#name^'s Nest"/> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_simplevariablesubstitutionvariablesconcatenated.xul b/dom/xul/templates/tests/chrome/test_tmpl_simplevariablesubstitutionvariablesconcatenated.xul new file mode 100644 index 000000000..b8864c3b3 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_simplevariablesubstitutionvariablesconcatenated.xul @@ -0,0 +1,45 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + simple variable substitution - variables concatenated +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/arachnids/tarantula" value="Tarantula3"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="simple variable substitution - variables concatenated"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/arachnids"> +<template id="template"> +<label uri="rdf:*" value="rdf:http://www.some-fictitious-zoo.com/rdf#name^rdf:http://www.some-fictitious-zoo.com/rdf#specimens"/> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_sortascendinginteger.xul b/dom/xul/templates/tests/chrome/test_tmpl_sortascendinginteger.xul new file mode 100644 index 000000000..2d30ced27 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_sortascendinginteger.xul @@ -0,0 +1,70 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + sort ascending - integers +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label step="-1" id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + <label step="1" id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="sort ascending - integers"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var subject = RDF.GetResource(ZOO_NS + 'mammals/llama'); + var predicate = RDF.GetResource(ZOO_NS + 'rdf#specimensAsString'); + var oldval = targetds.GetTarget(subject, predicate, true); + targetds.Change(subject, predicate, oldval, RDF.GetLiteral('12'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" + ref="http://www.some-fictitious-zoo.com/mammals" sort="?specimens ?name" sortDirection="ascending" sorthints="integer"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#specimensAsString" object="?specimens"/> +</query> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_sortascendingquerysyntax.xul b/dom/xul/templates/tests/chrome/test_tmpl_sortascendingquerysyntax.xul new file mode 100644 index 000000000..5f90a8334 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_sortascendingquerysyntax.xul @@ -0,0 +1,114 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + sort ascending - query syntax +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + <label step="-4" id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label step="5" id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label step="1" id="http://www.some-fictitious-zoo.com/mammals/arctichare" value="Arctic Hare"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label step="2" id="http://www.some-fictitious-zoo.com/mammals/koala" value="Koala"/> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + <label step="3" id="http://www.some-fictitious-zoo.com/mammals/zebra" value="Zebra"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="sort ascending - query syntax"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/arctichare'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Arctic Hare'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/koala'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Koala'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/zebra'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Zebra'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + var removednode = container.RemoveElementAt('4', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('African Elephant'), true); + }, + // step 5 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/africanelephant'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('African Elephant'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.AppendElement(newnode); + }, + // step 6 + function(targetds, root) { + targetds.Assert(RDF.GetResource(ZOO_NS + 'mammals/koala'), + RDF.GetResource(ZOO_NS + 'rdf#specimensAsString'), + RDF.GetLiteral('8'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals" sort="?name" sortDirection="ascending"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_sortascendingtworulesquerysyntax.xul b/dom/xul/templates/tests/chrome/test_tmpl_sortascendingtworulesquerysyntax.xul new file mode 100644 index 000000000..612a50fc6 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_sortascendingtworulesquerysyntax.xul @@ -0,0 +1,70 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + sort ascending two rules - query syntax +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <button id="http://www.some-fictitious-zoo.com/mammals/aardvark" label="aardvark"/> + <button id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" label="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <button id="http://www.some-fictitious-zoo.com/mammals/llama" label="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + <button id="http://www.some-fictitious-zoo.com/mammals/africanelephant" label="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="sort ascending two rules - query syntax"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals" sortDirection="ascending" sort="?specimens ?name" sortResource="http://www.some-fictitious-zoo.com/rdf#specimens" sortResource2="http://www.some-fictitious-zoo.com/rdf#name"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#specimens" object="?specimens"/> +</query> +<rule> +<conditions> +<where subject="?name" rel="contains" value="o"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +<rule> +<action> +<button uri="?animal" label="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_sortascendingtworuleswithcontainerquerysyntax.xul b/dom/xul/templates/tests/chrome/test_tmpl_sortascendingtworuleswithcontainerquerysyntax.xul new file mode 100644 index 000000000..e3fb2e37b --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_sortascendingtworuleswithcontainerquerysyntax.xul @@ -0,0 +1,80 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + sort ascending two rules with container - query syntax +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <hbox id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo"> + <label value="Nine-banded Armadillo"/> + </hbox> + <button id="http://www.some-fictitious-zoo.com/mammals/aardvark" label="aardvark"/> + <button id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" label="HIPPOPOTAMUS"/> + <hbox id="http://www.some-fictitious-zoo.com/mammals/lion"> + <label value="Lion"/> + </hbox> + <button id="http://www.some-fictitious-zoo.com/mammals/llama" label="LLAMA"/> + <hbox id="http://www.some-fictitious-zoo.com/mammals/gorilla"> + <label value="Gorilla"/> + </hbox> + <button id="http://www.some-fictitious-zoo.com/mammals/africanelephant" label="African Elephant"/> + <hbox id="http://www.some-fictitious-zoo.com/mammals/polarbear"> + <label value="Polar Bear"/> + </hbox> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="sort ascending two rules with container - query syntax"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals" sortDirection="ascending" sort="?specimens ?name" sortResource="http://www.some-fictitious-zoo.com/rdf#specimens" sortResource2="http://www.some-fictitious-zoo.com/rdf#name"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#specimens" object="?specimens"/> +</query> +<rule> +<conditions> +<where subject="?name" rel="contains" value="o"/> +</conditions> +<action id="action"> +<hbox uri="?animal"> +<label value="?name"/> +</hbox> +</action> +</rule> +<rule> +<action> +<button uri="?animal" label="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_sortascendingtworuleswithdifferentcontainerquerysyntax.xul b/dom/xul/templates/tests/chrome/test_tmpl_sortascendingtworuleswithdifferentcontainerquerysyntax.xul new file mode 100644 index 000000000..fdab29054 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_sortascendingtworuleswithdifferentcontainerquerysyntax.xul @@ -0,0 +1,84 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + sort ascending two rules with different container - query syntax +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <vbox> + <hbox id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo"> + <label value="Nine-banded Armadillo"/> + </hbox> + <hbox id="http://www.some-fictitious-zoo.com/mammals/lion"> + <label value="Lion"/> + </hbox> + <hbox id="http://www.some-fictitious-zoo.com/mammals/gorilla"> + <label value="Gorilla"/> + </hbox> + <hbox id="http://www.some-fictitious-zoo.com/mammals/polarbear"> + <label value="Polar Bear"/> + </hbox> + </vbox> + <button id="http://www.some-fictitious-zoo.com/mammals/aardvark" label="aardvark"/> + <button id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" label="HIPPOPOTAMUS"/> + <button id="http://www.some-fictitious-zoo.com/mammals/llama" label="LLAMA"/> + <button id="http://www.some-fictitious-zoo.com/mammals/africanelephant" label="African Elephant"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="sort ascending two rules with different container - query syntax"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals" sortDirection="ascending" sort="?specimens ?name" sortResource="http://www.some-fictitious-zoo.com/rdf#specimens" sortResource2="http://www.some-fictitious-zoo.com/rdf#name"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#specimens" object="?specimens"/> +</query> +<rule> +<conditions> +<where subject="?name" rel="contains" value="o"/> +</conditions> +<action id="action"> +<vbox> +<hbox uri="?animal"> +<label value="?name"/> +</hbox> +</vbox> +</action> +</rule> +<rule> +<action> +<button uri="?animal" label="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_sortdescendingquerysyntax.xul b/dom/xul/templates/tests/chrome/test_tmpl_sortdescendingquerysyntax.xul new file mode 100644 index 000000000..1979fdb98 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_sortdescendingquerysyntax.xul @@ -0,0 +1,114 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + sort descending - query syntax +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label step="3" id="http://www.some-fictitious-zoo.com/mammals/zebra" value="Zebra"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label step="2" id="http://www.some-fictitious-zoo.com/mammals/koala" value="Koala"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + <label step="1" id="http://www.some-fictitious-zoo.com/mammals/arctichare" value="Arctic Hare"/> + <label step="5" id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label step="-4" id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="sort descending - query syntax"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/arctichare'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Arctic Hare'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/koala'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Koala'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/zebra'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Zebra'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + var removednode = container.RemoveElementAt('4', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('African Elephant'), true); + }, + // step 5 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/africanelephant'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('African Elephant'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.AppendElement(newnode); + }, + // step 6 + function(targetds, root) { + targetds.Assert(RDF.GetResource(ZOO_NS + 'mammals/koala'), + RDF.GetResource(ZOO_NS + 'rdf#specimensAsString'), + RDF.GetLiteral('8'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals" sort="?name" sortDirection="descending"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_sortquerymemberandtwotriples.xul b/dom/xul/templates/tests/chrome/test_tmpl_sortquerymemberandtwotriples.xul new file mode 100644 index 000000000..8176553e4 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_sortquerymemberandtwotriples.xul @@ -0,0 +1,58 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + sort query - member and two triples +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/reptiles/anaconda" value="Anaconda"/> + <label id="http://www.some-fictitious-zoo.com/reptiles/chameleon" value="Chameleon"/> + <label id="http://www.some-fictitious-zoo.com/birds/emu" value="Emu"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + <label id="http://www.some-fictitious-zoo.com/arachnids/tarantula" value="Tarantula"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="sort query - member and two triples"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/humans" sortDirection="ascending" sort="?name" sortResource="http://www.some-fictitious-zoo.com/rdf#specimens" sortResource2="http://www.some-fictitious-zoo.com/rdf#name"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?human"/> +<triple subject="?human" predicate="http://www.some-fictitious-zoo.com/rdf#favoriteAnimal" object="?child"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<action> +<label uri="?child" value="?name"/> +</action> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_sortresource2descendingsimplesyntax.xul b/dom/xul/templates/tests/chrome/test_tmpl_sortresource2descendingsimplesyntax.xul new file mode 100644 index 000000000..d328d81b2 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_sortresource2descendingsimplesyntax.xul @@ -0,0 +1,52 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + sortResource2 descending - simple syntax +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="sortResource2 descending - simple syntax"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals" sortDirection="descending" sort="http://www.some-fictitious-zoo.com/rdf#specimens http://www.some-fictitious-zoo.com/rdf#name" sortResource="http://www.some-fictitious-zoo.com/rdf#specimens" sortResource2="http://www.some-fictitious-zoo.com/rdf#name"> +<template id="template"> +<label uri="rdf:*" value="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_sortresource2settopredicateascendingquerysyntax.xul b/dom/xul/templates/tests/chrome/test_tmpl_sortresource2settopredicateascendingquerysyntax.xul new file mode 100644 index 000000000..653584c52 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_sortresource2settopredicateascendingquerysyntax.xul @@ -0,0 +1,59 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + sortResource2 set to predicate ascending - query syntax +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="sortResource2 set to predicate ascending - query syntax"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals" sortResource="http://www.some-fictitious-zoo.com/rdf#specimens" sortDirection="ascending" sortResource2="http://www.some-fictitious-zoo.com/rdf#name"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_sortresource2settopredicatedescendingquerysyntax.xul b/dom/xul/templates/tests/chrome/test_tmpl_sortresource2settopredicatedescendingquerysyntax.xul new file mode 100644 index 000000000..8fcb569a7 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_sortresource2settopredicatedescendingquerysyntax.xul @@ -0,0 +1,59 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + sortResource2 set to predicate descending - query syntax +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="sortResource2 set to predicate descending - query syntax"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals" sortResource="http://www.some-fictitious-zoo.com/rdf#specimens" sortDirection="descending" sortResource2="http://www.some-fictitious-zoo.com/rdf#name"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_sortresourceascendingquerysyntax.xul b/dom/xul/templates/tests/chrome/test_tmpl_sortresourceascendingquerysyntax.xul new file mode 100644 index 000000000..346a988a5 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_sortresourceascendingquerysyntax.xul @@ -0,0 +1,59 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + sortResource ascending - query syntax +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="sortResource ascending - query syntax"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals" sortResource="?name" sortDirection="ascending"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_sortresourcedescendingquerysyntax.xul b/dom/xul/templates/tests/chrome/test_tmpl_sortresourcedescendingquerysyntax.xul new file mode 100644 index 000000000..7281eb6ef --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_sortresourcedescendingquerysyntax.xul @@ -0,0 +1,59 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + sortResource descending - query syntax +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="sortResource descending - query syntax"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals" sortResource="?name" sortDirection="descending"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_sortresourcesettopredicateascendingquerysyntax.xul b/dom/xul/templates/tests/chrome/test_tmpl_sortresourcesettopredicateascendingquerysyntax.xul new file mode 100644 index 000000000..151c5d5ae --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_sortresourcesettopredicateascendingquerysyntax.xul @@ -0,0 +1,59 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + sortResource set to predicate ascending - query syntax +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="sortResource set to predicate ascending - query syntax"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals" sortResource="http://www.some-fictitious-zoo.com/rdf#specimens" sortDirection="ascending"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_sortresourcesettopredicatedescendingquerysyntax.xul b/dom/xul/templates/tests/chrome/test_tmpl_sortresourcesettopredicatedescendingquerysyntax.xul new file mode 100644 index 000000000..893f056b8 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_sortresourcesettopredicatedescendingquerysyntax.xul @@ -0,0 +1,59 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + sortResource set to predicate descending - query syntax +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="sortResource set to predicate descending - query syntax"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals" sortResource="http://www.some-fictitious-zoo.com/rdf#specimens" sortDirection="descending"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_sorttworesourcesasstringsettopredicatedescendingquerysyntax.xul b/dom/xul/templates/tests/chrome/test_tmpl_sorttworesourcesasstringsettopredicatedescendingquerysyntax.xul new file mode 100644 index 000000000..1daf2dab9 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_sorttworesourcesasstringsettopredicatedescendingquerysyntax.xul @@ -0,0 +1,59 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + sort two resources as string set to predicate descending - query syntax +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="sort two resources as string set to predicate descending - query syntax"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals" sortDirection="descending" sort="http://www.some-fictitious-zoo.com/rdf#specimensAsString http://www.some-fictitious-zoo.com/rdf#name"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_sorttworesourcessettopredicateascendingquerysyntax.xul b/dom/xul/templates/tests/chrome/test_tmpl_sorttworesourcessettopredicateascendingquerysyntax.xul new file mode 100644 index 000000000..8d9137ffa --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_sorttworesourcessettopredicateascendingquerysyntax.xul @@ -0,0 +1,59 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + sort two resources set to predicate ascending - query syntax +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="sort two resources set to predicate ascending - query syntax"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals" sortDirection="ascending" sort="http://www.some-fictitious-zoo.com/rdf#specimens http://www.some-fictitious-zoo.com/rdf#name"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_sorttwovariablesascendingquerysyntax.xul b/dom/xul/templates/tests/chrome/test_tmpl_sorttwovariablesascendingquerysyntax.xul new file mode 100644 index 000000000..6a396d3b9 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_sorttwovariablesascendingquerysyntax.xul @@ -0,0 +1,119 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + sort two variables ascending - query syntax +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + <label step="5" id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label step="-4" id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + <label step="1" id="http://www.some-fictitious-zoo.com/mammals/arctichare" value="Arctic Hare"/> + <label step="2" id="http://www.some-fictitious-zoo.com/mammals/koala" value="Koala"/> + <label step="3" id="http://www.some-fictitious-zoo.com/mammals/zebra" value="Zebra"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="sort two variables ascending - query syntax"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = true; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/arctichare'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Arctic Hare'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/koala'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Koala'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/zebra'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Zebra'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + var removednode = container.RemoveElementAt('4', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('African Elephant'), true); + }, + // step 5 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/africanelephant'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('African Elephant'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.AppendElement(newnode); + }, + // step 6 + function(targetds, root) { + targetds.Assert(RDF.GetResource(ZOO_NS + 'mammals/koala'), + RDF.GetResource(ZOO_NS + 'rdf#specimensAsString'), + RDF.GetLiteral('8'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals" sortDirection="ascending" sort="?specimens ?name"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<bindings> +<binding subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#specimens" object="?specimens"/> +</bindings> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_sorttwovariablesascendingsimplesyntax.xul b/dom/xul/templates/tests/chrome/test_tmpl_sorttwovariablesascendingsimplesyntax.xul new file mode 100644 index 000000000..fd8200a66 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_sorttwovariablesascendingsimplesyntax.xul @@ -0,0 +1,52 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + sort two variables ascending - simple syntax +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="sort two variables ascending - simple syntax"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals" sortDirection="ascending" sort="http://www.some-fictitious-zoo.com/rdf#specimens http://www.some-fictitious-zoo.com/rdf#name"> +<template id="template"> +<label uri="rdf:*" value="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_sorttwovariablesdescendingquerysyntax.xul b/dom/xul/templates/tests/chrome/test_tmpl_sorttwovariablesdescendingquerysyntax.xul new file mode 100644 index 000000000..f2fb6ca9d --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_sorttwovariablesdescendingquerysyntax.xul @@ -0,0 +1,64 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + sort two variables descending - query syntax +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="sort two variables descending - query syntax"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals" sortDirection="descending" sort="?specimens ?name"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<bindings> +<binding subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#specimens" object="?specimens"/> +</bindings> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_sortunknownascendingquerysyntax.xul b/dom/xul/templates/tests/chrome/test_tmpl_sortunknownascendingquerysyntax.xul new file mode 100644 index 000000000..d995c4c1c --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_sortunknownascendingquerysyntax.xul @@ -0,0 +1,59 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + sort unknown ascending - query syntax +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="sort unknown ascending - query syntax"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals" sort="?other"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_storage_bad_parameters.xul b/dom/xul/templates/tests/chrome/test_tmpl_storage_bad_parameters.xul new file mode 100644 index 000000000..b11367e28 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_storage_bad_parameters.xul @@ -0,0 +1,58 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + storage listbox with bad query parameters +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"/> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="storage listbox with bad query parameters"; +var queryType = "storage"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + + +Components.classes["@mozilla.org/consoleservice;1"] + .getService(Components.interfaces.nsIConsoleService) + .reset(); + +copyToProfile('animals.sqlite'); +expectedConsoleMessages.push("Error parsing template: the given named parameter is unknown in the SQL query"); + +var changes = []; +]]> +</script> + +<listbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" + flex="1" datasources="profile:animals.sqlite" ref="." querytype="storage"> + <template> + <query> + SELECT * FROM animals WHERE species_id = ? ORDER BY name + <param name="bar">2</param> + </query> + <action> + <listitem uri="?" label="?name"/> + </action> + </template> +</listbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_storage_bad_parameters_2.xul b/dom/xul/templates/tests/chrome/test_tmpl_storage_bad_parameters_2.xul new file mode 100644 index 000000000..81517d2df --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_storage_bad_parameters_2.xul @@ -0,0 +1,59 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + storage listbox with bad query parameters +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"/> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="storage listbox with bad query parameters"; +var queryType = "storage"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + + +Components.classes["@mozilla.org/consoleservice;1"] + .getService(Components.interfaces.nsIConsoleService) + .reset(); + +copyToProfile('animals.sqlite'); + +expectedConsoleMessages.push("Error parsing template: the type of a query parameter is wrong"); + +var changes = []; +]]> +</script> + +<listbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" + flex="1" datasources="profile:animals.sqlite" ref="." querytype="storage"> + <template> + <query> + SELECT * FROM animals WHERE species_id = ? ORDER BY name + <param type="mysupertype">2</param> + </query> + <action> + <listitem uri="?" label="?name"/> + </action> + </template> +</listbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_storage_bad_parameters_3.xul b/dom/xul/templates/tests/chrome/test_tmpl_storage_bad_parameters_3.xul new file mode 100644 index 000000000..22be02c28 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_storage_bad_parameters_3.xul @@ -0,0 +1,60 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + storage listbox with bad query parameters +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"/> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="storage listbox with bad query parameters"; +var queryType = "storage"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + + +Components.classes["@mozilla.org/consoleservice;1"] + .getService(Components.interfaces.nsIConsoleService) + .reset(); + +copyToProfile('animals.sqlite'); + +expectedConsoleMessages.push("Error parsing template: a query parameter cannot be bound to the SQL query"); + +var changes = []; +]]> +</script> + +<listbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" + flex="1" datasources="profile:animals.sqlite" ref="." querytype="storage"> + <template> + <query> + SELECT * FROM animals WHERE species_id = :spec ORDER BY name + <param name="spec" type="int32">5</param> + <param>L%</param> + </query> + <action> + <listitem uri="?" label="?name"/> + </action> + </template> +</listbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_storage_baddatasource.xul b/dom/xul/templates/tests/chrome/test_tmpl_storage_baddatasource.xul new file mode 100644 index 000000000..da9c83d0c --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_storage_baddatasource.xul @@ -0,0 +1,56 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + storage bad datasource +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"/> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="storage bad datasource"; +var queryType = "storage"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +Components.classes["@mozilla.org/consoleservice;1"] + .getService(Components.interfaces.nsIConsoleService) + .reset(); + +expectedConsoleMessages.push("Error parsing template: only profile: or file URI are allowed"); + + +var changes = []; +]]> +</script> + +<listbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" + flex="1" rows="8" + datasources="http://foo.local/animals.sqlite" ref="." querytype="storage"> + <template> + <query> + SELECT * FROM animals WHERE species_id = 2 ORDER BY name + </query> + <action> + <listitem uri="?" label="?name" /> + </action> + </template> +</listbox> +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_storage_badquery.xul b/dom/xul/templates/tests/chrome/test_tmpl_storage_badquery.xul new file mode 100644 index 000000000..39df0edac --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_storage_badquery.xul @@ -0,0 +1,58 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + storage bad query +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"/> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="storage bad query"; +var queryType = "storage"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +Components.classes["@mozilla.org/consoleservice;1"] + .getService(Components.interfaces.nsIConsoleService) + .reset(); + +copyToProfile('animals.sqlite'); + +expectedConsoleMessages.push("Error parsing template: syntax error in the SQL query"); + + +var changes = []; +]]> +</script> + +<listbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" + flex="1" rows="8" + datasources="profile:animals.sqlite" ref="." querytype="storage"> + <template> + <query> + SELECT * FROM animalssssssss WHERE species_id = 2 ORDER BY + </query> + <action> + <listitem uri="?" label="?name" /> + </action> + </template> +</listbox> +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_storage_dynamicparameters.xul b/dom/xul/templates/tests/chrome/test_tmpl_storage_dynamicparameters.xul new file mode 100644 index 000000000..c791c6acd --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_storage_dynamicparameters.xul @@ -0,0 +1,84 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + storage listbox with dynamic query parameters +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_storage_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"/> + + <data id="rebuilt-output"> + <listitem anyid="true" label="Barn Owl"/> + <listitem anyid="true" label="Emu"/> + <listitem anyid="true" label="Raven"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +copyToProfile('animals.sqlite'); + +function test_storage_template() +{ + var root = document.getElementById("root"); + expectedOutput = document.getElementById("output"); + checkResults(root, 0); + + document.getElementById("species-id").textContent = '2'; + root.builder.addListener(myBuilderListener); + root.builder.rebuild(); +} + +var myBuilderListener = { + + willRebuild: function(aBuilder) { + + }, + didRebuild: function (aBuilder) { + var root = document.getElementById("root"); + expectedOutput = document.getElementById("rebuilt-output"); + checkResults(root, 0); + SimpleTest.finish(); + } +} + +var testid ="storage listbox with dynamic query parameters"; +var queryType = "storage"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<listbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" + flex="1" datasources="profile:animals.sqlite" ref="." querytype="storage"> + <template> + <query> + SELECT * FROM animals WHERE species_id = :spec ORDER BY name + <param id="species-id" name="spec" type="int32" /> + </query> + <action> + <listitem uri="?" label="?name"/> + </action> + </template> +</listbox> + + + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_storage_listbox.xul b/dom/xul/templates/tests/chrome/test_tmpl_storage_listbox.xul new file mode 100644 index 000000000..43661c417 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_storage_listbox.xul @@ -0,0 +1,55 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + storage simple with listbox +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <listitem anyid="true" label="Barn Owl"/> + <listitem anyid="true" label="Emu"/> + <listitem anyid="true" label="Raven"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +copyToProfile('animals.sqlite'); + +var testid ="storage simple listbox"; +var queryType = "storage"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<listbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" + flex="1" rows="8" + datasources="profile:animals.sqlite" ref="." querytype="storage"> + <template> + <query> + SELECT * FROM animals WHERE species_id = 2 ORDER BY name + </query> + <action> + <listitem uri="?" label="?name" /> + </action> + </template> +</listbox> +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_storage_multiqueries.xul b/dom/xul/templates/tests/chrome/test_tmpl_storage_multiqueries.xul new file mode 100644 index 000000000..28dcaa926 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_storage_multiqueries.xul @@ -0,0 +1,86 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + storage listbox with multiqueries +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <listitem anyid="true" label="Mammal: African Elephant"/> + <listitem anyid="true" label="Mammal: Gorilla" style="font-weight:bold"/> + <listitem anyid="true" label="Mammal: HIPPOPOTAMUS"/> + <listitem anyid="true" label="Mammal: LAMA"/> + <listitem anyid="true" label="Mammal: Lion"/> + <listitem anyid="true" label="Mammal: Nine-banded Armadillo" style="font-weight:bold"/> + <listitem anyid="true" label="Mammal: Polar Bear"/> + <listitem anyid="true" label="Mammal: aardvark"/> + <listitem anyid="true" label="Bird: Barn Owl" style="font-style:italic"/> + <listitem anyid="true" label="Bird: Emu"/> + <listitem anyid="true" label="Bird: Raven"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +copyToProfile('animals.sqlite'); + +var testid ="storage listbox with multiqueries"; +var queryType = "storage"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<listbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" + flex="1" datasources="profile:animals.sqlite" ref="." querytype="storage"> + <template> + <queryset> + <query>SELECT * FROM animals WHERE species_id = 5 ORDER BY name</query> + <rule> + <where subject="?id" rel="greater" value="12"/> + <action> + <listitem uri="?" label="Mammal: ?name" style="font-weight:bold"/> + </action> + </rule> + <rule> + <action> + <listitem uri="?" label="Mammal: ?name"/> + </action> + </rule> + </queryset> + <queryset> + <!-- we use aliases on columns just to have different "column names" in the result set + to "similate" a result set from another table for example --> + <query>SELECT * FROM animals WHERE species_id = 2 ORDER BY name</query> + <rule> + <where subject="?id" rel="equals" value="3"/> + <action> + <listitem uri="?" label="Bird: ?name" style="font-style:italic"/> + </action> + </rule> + <rule> + <action> + <listitem uri="?" label="Bird: ?name"/> + </action> + </rule> + </queryset> + </template> +</listbox> +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_storage_parameters.xul b/dom/xul/templates/tests/chrome/test_tmpl_storage_parameters.xul new file mode 100644 index 000000000..0df561884 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_storage_parameters.xul @@ -0,0 +1,160 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + storage listbox with query parameters +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_storage_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output-birds"> + <listitem anyid="true" label="Barn Owl"/> + <listitem anyid="true" label="Emu"/> + <listitem anyid="true" label="Raven"/> + </data> + + <data id="output-L"> + <listitem anyid="true" label="LAMA"/> + <listitem anyid="true" label="Lion"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ + +copyToProfile('animals.sqlite'); +SimpleTest.waitForExplicitFinish(); + + +function test_storage_template() +{ + var root = document.getElementById("root1"); + expectedOutput = document.getElementById("output-birds"); + checkResults(root, 0); + +root = document.getElementById("root2"); +checkResults(root, 0); + +root = document.getElementById("root6"); +checkResults(root, 0); + +root = document.getElementById("root3"); +expectedOutput = document.getElementById("output-L"); +checkResults(root, 0); + +root = document.getElementById("root4"); +checkResults(root, 0); + +root = document.getElementById("root5"); +checkResults(root, 0); + +SimpleTest.finish(); +} + + +var testid ="storage listbox with query parameters"; +var queryType = "storage"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput; + +var changes = []; +]]> +</script> + +<listbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root1" + flex="1" datasources="profile:animals.sqlite" ref="." querytype="storage"> + <template> + <query> + SELECT * FROM animals WHERE species_id = ? ORDER BY name + <param>2</param> + </query> + <action> + <listitem uri="?" label="?name"/> + </action> + </template> +</listbox> + + +<listbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root2" + flex="1" datasources="profile:animals.sqlite" ref="." querytype="storage"> + <template> + <query> + SELECT * FROM animals WHERE species_id = ? ORDER BY name + <param type="int32">2</param> + </query> + <action> + <listitem uri="?" label="?name"/> + </action> + </template> +</listbox> + + +<listbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root3" + flex="1" datasources="profile:animals.sqlite" ref="." querytype="storage"> + <template> + <query> + SELECT * FROM animals WHERE species_id = :spec AND name like ? ORDER BY name + <param name="spec" type="int32">5</param> + <param>L%</param> + </query> + <action> + <listitem uri="?" label="?name"/> + </action> + </template> +</listbox> + +<listbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root4" + flex="1" datasources="profile:animals.sqlite" ref="." querytype="storage"> + <template> + <query> + SELECT * FROM animals WHERE species_id = ?3 AND name like ?1 ORDER BY name + <param index="3" type="int32">5</param> + <param index="1">L%</param> + </query> + <action> + <listitem uri="?" label="?name"/> + </action> + </template> +</listbox> + +<listbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root5" + flex="1" datasources="profile:animals.sqlite" ref="." querytype="storage"> + <template> + <query> + SELECT * FROM animals WHERE species_id = ?3 AND name like :pattern ORDER BY name + <param name="pattern">L%</param> + <param index="3" type="int32">5</param> + </query> + <action> + <listitem uri="?" label="?name"/> + </action> + </template> +</listbox> + +<listbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root6" + flex="1" datasources="profile:animals.sqlite" ref="." querytype="storage"> + <template> + <query> + SELECT * FROM animals WHERE species_id = ? ORDER BY name + <param type="int32">2qsdqsd</param> + </query> + <action> + <listitem uri="?" label="?name"/> + </action> + </template> +</listbox> + + + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_storage_rule.xul b/dom/xul/templates/tests/chrome/test_tmpl_storage_rule.xul new file mode 100644 index 000000000..003682750 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_storage_rule.xul @@ -0,0 +1,59 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + storage listbox with rule +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <listitem anyid="true" label="Gorilla"/> + <listitem anyid="true" label="Nine-banded Armadillo"/> + <listitem anyid="true" label="Polar Bear"/> + <listitem anyid="true" label="aardvark"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ + +SimpleTest.waitForExplicitFinish(); +copyToProfile('animals.sqlite'); + +var testid ="storage listbox with rule"; +var queryType = "storage"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<listbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" + flex="1" rows="8" + datasources="profile:animals.sqlite" ref="." querytype="storage"> + <template> + <query> + SELECT * FROM animals WHERE species_id = 5 ORDER BY name + </query> + <rule> + <where subject="?id" rel="greater" value="10"/> + <action> + <listitem uri="?" label="?name" /> + </action> + </rule> + </template> +</listbox> +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_storage_simple.xul b/dom/xul/templates/tests/chrome/test_tmpl_storage_simple.xul new file mode 100644 index 000000000..0ac6d3857 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_storage_simple.xul @@ -0,0 +1,52 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + storage simple +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <button anyid="true" label="Barn Owl / Tyto alba"/> + <button anyid="true" label="Emu / Dromaius novaehollandiae"/> + <button anyid="true" label="Raven / Corvus corax"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +copyToProfile('animals.sqlite'); +SimpleTest.waitForExplicitFinish(); + +var testid ="storage simple"; +var queryType = "storage"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" + datasources="profile:animals.sqlite" querytype="storage" ref="."> +<template> + <query>SELECT * FROM animals WHERE species_id = 2 ORDER BY name</query> + <action> + <button uri="?" label="?name / ?specimen"/> + </action> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_storage_sortintegerasc.xul b/dom/xul/templates/tests/chrome/test_tmpl_storage_sortintegerasc.xul new file mode 100644 index 000000000..376cee25c --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_storage_sortintegerasc.xul @@ -0,0 +1,75 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + storage sort integer asc +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <row anyid="true"> <label value="1"/> <label value="Tarantula"/> </row> + <row anyid="true"> <label value="2"/> <label value="Emu"/> </row> + <row anyid="true"> <label value="3"/> <label value="Barn Owl"/> </row> + <row anyid="true"> <label value="4"/> <label value="Raven"/> </row> + <row anyid="true"> <label value="5"/> <label value="Cod"/> </row> + <row anyid="true"> <label value="6"/> <label value="Swordfish"/> </row> + <row anyid="true"> <label value="7"/> <label value="Lion"/> </row> + <row anyid="true"> <label value="8"/> <label value="HIPPOPOTAMUS"/> </row> + <row anyid="true"> <label value="9"/> <label value="African Elephant"/> </row> + <row anyid="true"> <label value="10"/> <label value="LAMA"/> </row> + <row anyid="true"> <label value="11"/> <label value="Polar Bear"/> </row> + <row anyid="true"> <label value="12"/> <label value="aardvark"/> </row> + <row anyid="true"> <label value="13"/> <label value="Nine-banded Armadillo"/> </row> + <row anyid="true"> <label value="14"/> <label value="Gorilla"/> </row> + <row anyid="true"> <label value="15"/> <label value="Anaconda"/> </row> + <row anyid="true"> <label value="16"/> <label value="Chameleon"/> </row> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +copyToProfile('animals.sqlite'); + +var testid ="storage sort integer asc"; +var queryType = "storage"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<grid xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" > + <columns> + <column flex="1"/> + <column flex="3"/> + </columns> + <rows id="root" datasources="profile:animals.sqlite" querytype="storage" ref="." + sort="?id" sortDirection="ascending"> + <template> + <query>SELECT id, name FROM animals</query> + <action> + <row uri="?"> + <label value="?id"/> + <label value="?name"/> + </row> + </action> + </template> + </rows> +</grid> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_storage_sortintegerdesc.xul b/dom/xul/templates/tests/chrome/test_tmpl_storage_sortintegerdesc.xul new file mode 100644 index 000000000..3ab75fcf4 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_storage_sortintegerdesc.xul @@ -0,0 +1,78 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + storage sort integer desc +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <row anyid="true"> <label value="16"/> <label value="Chameleon"/> </row> + <row anyid="true"> <label value="15"/> <label value="Anaconda"/> </row> + <row anyid="true"> <label value="14"/> <label value="Gorilla"/> </row> + <row anyid="true"> <label value="13"/> <label value="Nine-banded Armadillo"/> </row> + <row anyid="true"> <label value="12"/> <label value="aardvark"/> </row> + <row anyid="true"> <label value="11"/> <label value="Polar Bear"/> </row> + <row anyid="true"> <label value="10"/> <label value="LAMA"/> </row> + <row anyid="true"> <label value="9"/> <label value="African Elephant"/> </row> + <row anyid="true"> <label value="8"/> <label value="HIPPOPOTAMUS"/> </row> + <row anyid="true"> <label value="7"/> <label value="Lion"/> </row> + <row anyid="true"> <label value="6"/> <label value="Swordfish"/> </row> + <row anyid="true"> <label value="5"/> <label value="Cod"/> </row> + <row anyid="true"> <label value="4"/> <label value="Raven"/> </row> + <row anyid="true"> <label value="3"/> <label value="Barn Owl"/> </row> + <row anyid="true"> <label value="2"/> <label value="Emu"/> </row> + <row anyid="true"> <label value="1"/> <label value="Tarantula"/> </row> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +copyToProfile('animals.sqlite'); + +var testid ="storage sort integer desc"; +var queryType = "storage"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<grid xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" > + <columns> + <column flex="1"/> + <column flex="3"/> + </columns> + <rows id="root" datasources="profile:animals.sqlite" querytype="storage" ref="." + sort="?id" sortDirection="descending"> + <template> + <query>SELECT id, name FROM animals</query> + <action> + <row uri="?"> + <label value="?id"/> + <label value="?name"/> + </row> + </action> + </template> + </rows> +</grid> + + + + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_storage_sortstringasc.xul b/dom/xul/templates/tests/chrome/test_tmpl_storage_sortstringasc.xul new file mode 100644 index 000000000..600db67c4 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_storage_sortstringasc.xul @@ -0,0 +1,75 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + storage sort string asc +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <row anyid="true"> <label value="12"/> <label value="aardvark"/> </row> + <row anyid="true"> <label value="9"/> <label value="African Elephant"/> </row> + <row anyid="true"> <label value="15"/> <label value="Anaconda"/> </row> + <row anyid="true"> <label value="3"/> <label value="Barn Owl"/> </row> + <row anyid="true"> <label value="16"/> <label value="Chameleon"/> </row> + <row anyid="true"> <label value="5"/> <label value="Cod"/> </row> + <row anyid="true"> <label value="2"/> <label value="Emu"/> </row> + <row anyid="true"> <label value="14"/> <label value="Gorilla"/> </row> + <row anyid="true"> <label value="8"/> <label value="HIPPOPOTAMUS"/> </row> + <row anyid="true"> <label value="10"/> <label value="LAMA"/> </row> + <row anyid="true"> <label value="7"/> <label value="Lion"/> </row> + <row anyid="true"> <label value="13"/> <label value="Nine-banded Armadillo"/> </row> + <row anyid="true"> <label value="11"/> <label value="Polar Bear"/> </row> + <row anyid="true"> <label value="4"/> <label value="Raven"/> </row> + <row anyid="true"> <label value="6"/> <label value="Swordfish"/> </row> + <row anyid="true"> <label value="1"/> <label value="Tarantula"/> </row> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +copyToProfile('animals.sqlite'); + +var testid ="storage sort string asc"; +var queryType = "storage"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<grid xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" > + <columns> + <column flex="1"/> + <column flex="3"/> + </columns> + <rows id="root" datasources="profile:animals.sqlite" querytype="storage" ref="." + sort="?name" sortDirection="ascending"> + <template> + <query>SELECT id, name FROM animals</query> + <action> + <row uri="?"> + <label value="?id"/> + <label value="?name"/> + </row> + </action> + </template> + </rows> +</grid> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_storage_sortstringdesc.xul b/dom/xul/templates/tests/chrome/test_tmpl_storage_sortstringdesc.xul new file mode 100644 index 000000000..b99c849e7 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_storage_sortstringdesc.xul @@ -0,0 +1,75 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + storage sort string desc +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <row anyid="true"> <label value="1"/> <label value="Tarantula"/> </row> + <row anyid="true"> <label value="6"/> <label value="Swordfish"/> </row> + <row anyid="true"> <label value="4"/> <label value="Raven"/> </row> + <row anyid="true"> <label value="11"/> <label value="Polar Bear"/> </row> + <row anyid="true"> <label value="13"/> <label value="Nine-banded Armadillo"/> </row> + <row anyid="true"> <label value="7"/> <label value="Lion"/> </row> + <row anyid="true"> <label value="10"/> <label value="LAMA"/> </row> + <row anyid="true"> <label value="8"/> <label value="HIPPOPOTAMUS"/> </row> + <row anyid="true"> <label value="14"/> <label value="Gorilla"/> </row> + <row anyid="true"> <label value="2"/> <label value="Emu"/> </row> + <row anyid="true"> <label value="5"/> <label value="Cod"/> </row> + <row anyid="true"> <label value="16"/> <label value="Chameleon"/> </row> + <row anyid="true"> <label value="3"/> <label value="Barn Owl"/> </row> + <row anyid="true"> <label value="15"/> <label value="Anaconda"/> </row> + <row anyid="true"> <label value="9"/> <label value="African Elephant"/> </row> + <row anyid="true"> <label value="12"/> <label value="aardvark"/> </row> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +copyToProfile('animals.sqlite'); + +var testid ="storage sort string desc"; +var queryType = "storage"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<grid xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" > + <columns> + <column flex="1"/> + <column flex="3"/> + </columns> + <rows id="root" datasources="profile:animals.sqlite" querytype="storage" ref="." + sort="?name" sortDirection="descending"> + <template> + <query>SELECT id, name FROM animals</query> + <action> + <row uri="?"> + <label value="?id"/> + <label value="?name"/> + </row> + </action> + </template> + </rows> +</grid> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_storage_tree.xul b/dom/xul/templates/tests/chrome/test_tmpl_storage_tree.xul new file mode 100644 index 000000000..9ca042713 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_storage_tree.xul @@ -0,0 +1,122 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + storage tree +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <treecols orient="horizontal"> + <treecol id="species" primary="true" label="Species" flex="2" ordinal="1"/> + <treecol id="name" label="Common name" flex="2" ordinal="3"/> + <treecol id="specimen" label="Specimen" flex="3" ordinal="5"/> + <treecol id="id" label="id" flex="1" ordinal="7"/> + </treecols> + <treechildren> + <treeitem anyid="true"> <treerow> + <treecell label="arachnids" /> <treecell label="Tarantula"/> <treecell label="Avicularia avicularia" /> <treecell label="1"/> + </treerow> </treeitem> + <treeitem anyid="true"> <treerow> + <treecell label="birds" /> <treecell label="Barn Owl"/> <treecell label="Tyto alba" /> <treecell label="3"/> + </treerow> </treeitem> + <treeitem anyid="true"> <treerow> + <treecell label="birds" /> <treecell label="Emu"/> <treecell label="Dromaius novaehollandiae" /> <treecell label="2"/> + </treerow> </treeitem> + <treeitem anyid="true"> <treerow> + <treecell label="birds" /> <treecell label="Raven"/> <treecell label="Corvus corax" /> <treecell label="4"/> + </treerow> </treeitem> + <treeitem anyid="true"> <treerow> + <treecell label="fish" /> <treecell label="Cod"/> <treecell label="Gadus morhua" /> <treecell label="5"/> + </treerow> </treeitem> + <treeitem anyid="true"> <treerow> + <treecell label="fish" /> <treecell label="Swordfish"/> <treecell label="Xiphias gladius" /> <treecell label="6"/> + </treerow> </treeitem> + <treeitem anyid="true"> <treerow> + <treecell label="mammals" /> <treecell label="African Elephant"/> <treecell label="Loxodonta africana" /> <treecell label="9"/> + </treerow> </treeitem> + <treeitem anyid="true"> <treerow> + <treecell label="mammals" /> <treecell label="Gorilla"/> <treecell label="Gorilla gorilla" /> <treecell label="14"/> + </treerow> </treeitem> + <treeitem anyid="true"> <treerow> + <treecell label="mammals" /> <treecell label="HIPPOPOTAMUS"/> <treecell label="Hippopotamus amphibius" /> <treecell label="8"/> + </treerow> </treeitem> + <treeitem anyid="true"> <treerow> + <treecell label="mammals" /> <treecell label="LAMA"/> <treecell label="Lama glama" /> <treecell label="10"/> + </treerow> </treeitem> + <treeitem anyid="true"> <treerow> + <treecell label="mammals" /> <treecell label="Lion"/> <treecell label="Panthera leo" /> <treecell label="7"/> + </treerow> </treeitem> + <treeitem anyid="true"> <treerow> + <treecell label="mammals" /> <treecell label="Nine-banded Armadillo"/> <treecell label="Dasypus novemcinctus" /> <treecell label="13"/> + </treerow> </treeitem> + <treeitem anyid="true"> <treerow> + <treecell label="mammals" /> <treecell label="Polar Bear"/> <treecell label="Thalarctos maritimus" /> <treecell label="11"/> + </treerow> </treeitem> + <treeitem anyid="true"> <treerow> + <treecell label="mammals" /> <treecell label="aardvark"/> <treecell label="Orycteropus afer" /> <treecell label="12"/> + </treerow> </treeitem> + <treeitem anyid="true"> <treerow> + <treecell label="reptiles" /> <treecell label="Anaconda"/> <treecell label="Eunectes murinus" /> <treecell label="15"/> + </treerow> </treeitem> + <treeitem anyid="true"> <treerow> + <treecell label="reptiles" /> <treecell label="Chameleon"/> <treecell label="Chamaeleo chamaelon" /> <treecell label="16"/> + </treerow> </treeitem> + </treechildren> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +copyToProfile('animals.sqlite'); + +var testid ="storage tree"; +var queryType = "storage"; +var isTreeBuilder = true; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<tree xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" + flex="1" datasources="profile:animals.sqlite" ref="*" querytype="storage" flags="dont-build-content"> + <treecols> + <treecol id="species" primary="true" label="Species" flex="2"/> + <treecol id="name" label="Common name" flex="2"/> + <treecol id="specimen" label="Specimen" flex="3"/> + <treecol id="id" label="id" flex="1"/> + </treecols> + <template> + <query> + SELECT a.id, a.name, a.specimen, s.name as species FROM animals a, species s + WHERE a.species_id = s.id ORDER BY species, a.name</query> + <action> + <treechildren> + <treeitem uri="?"> + <treerow> + <treecell label="?species"/> + <treecell label="?name"/> + <treecell label="?specimen"/> + <treecell label="?id"/> + </treerow> + </treeitem> + </treechildren> + </action> + </template> + </tree> +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_treeelementquerysyntax.xul b/dom/xul/templates/tests/chrome/test_tmpl_treeelementquerysyntax.xul new file mode 100644 index 000000000..bfb494c79 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_treeelementquerysyntax.xul @@ -0,0 +1,158 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + tree element - query syntax +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <treecols id="treecols" orient="horizontal"> + <treecol id="treecol" primary="true" label="Name" ordinal="1"/> + <treecol label="Species" ordinal="3"/> + </treecols> + <treechildren> + <treeitem id="http://www.some-fictitious-zoo.com/birds/emu"> + <treerow> + <treecell label="Emu"/> + <treecell step="-6" label="12"/> + <treecell step="6" label="0"/> + </treerow> + </treeitem> + <treeitem step="1" id="http://www.some-fictitious-zoo.com/birds/archaeopteryx"> + <treerow> + <treecell label="Archaeopteryx"/> + <treecell step="-4"/> + <treecell step="4" label="5"/> + </treerow> + </treeitem> + <treeitem step="-2" id="http://www.some-fictitious-zoo.com/birds/barnowl"> + <treerow> + <treecell label="Barn Owl"/> + <treecell label="4"/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/birds/raven"> + <treerow> + <treecell step="-7" label="Raven"/> + <treecell step="7" label="Crow"/> + <treecell step="-5" label="0"/> + <treecell step="5" label="2"/> + </treerow> + </treeitem> + </treechildren> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="tree element - query syntax"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Archaeopteryx'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '2', true); + }, + // step 2 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + var removednode = container.RemoveElementAt('3', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Barn Owl'), true); + }, + // step 3 + function(targetds, root) { + targetds.Assert(RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'), + RDF.GetResource(ZOO_NS + 'rdf#species'), + RDF.GetLiteral('Archaeopteryx lithographica'), true); + }, + // step 4 + function(targetds, root) { + targetds.Assert(RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'), + RDF.GetResource(ZOO_NS + 'rdf#specimens'), + RDF.GetLiteral('5'), true); + }, + // step 5 + function(targetds, root) { + var subject = RDF.GetResource(ZOO_NS + 'birds/raven'); + var predicate = RDF.GetResource(ZOO_NS + 'rdf#specimens'); + var oldval = targetds.GetTarget(subject, predicate, true); + targetds.Change(RDF.GetResource(ZOO_NS + 'birds/raven'), + RDF.GetResource(ZOO_NS + 'rdf#specimens'), + oldval, RDF.GetLiteral('2')); + }, + // step 6 + function(targetds, root) { + var subject = RDF.GetResource(ZOO_NS + 'birds/emu'); + var predicate = RDF.GetResource(ZOO_NS + 'rdf#specimens'); + var oldval = targetds.GetTarget(subject, predicate, true); + targetds.Change(RDF.GetResource(ZOO_NS + 'birds/emu'), + RDF.GetResource(ZOO_NS + 'rdf#specimens'), + oldval, RDF.GetLiteral('0')); + }, + // step 7 + function(targetds, root) { + var subject = RDF.GetResource(ZOO_NS + 'birds/raven'); + var predicate = RDF.GetResource(ZOO_NS + 'rdf#name'); + var oldval = targetds.GetTarget(subject, predicate, true); + targetds.Change(RDF.GetResource(ZOO_NS + 'birds/raven'), + RDF.GetResource(ZOO_NS + 'rdf#name'), + oldval, RDF.GetLiteral('Crow')); + } +]; +]]> +</script> + +<tree xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" hidevscroll="true" hidehscroll="true" datasources="rdf:null" flags="" id="root" ref="http://www.some-fictitious-zoo.com/birds"> +<treecols orient="horizontal" id="treecols"> +<treecol id="treecol" primary="true" label="Name"/> +<treecol label="Species"/> +</treecols> +<template id="template"> +<query> +<content uri="?uri"/> +<member container="?uri" child="?child"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<bindings> +<binding subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#specimens" object="?specimens"/> +</bindings> +<action> +<treechildren> +<treeitem uri="?child"> +<treerow> +<treecell label="?name"/> +<treecell label="?specimens"/> +</treerow> +</treeitem> +</treechildren> +</action> +</rule> +</template> +</tree> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_treeelementquerysyntaxnotrecursive.xul b/dom/xul/templates/tests/chrome/test_tmpl_treeelementquerysyntaxnotrecursive.xul new file mode 100644 index 000000000..9a07372fe --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_treeelementquerysyntaxnotrecursive.xul @@ -0,0 +1,145 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + tree element - query syntax not recursive +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <treecols id="treecols" orient="horizontal"> + <treecol id="treecol" primary="true" label="Name" ordinal="1"/> + <treecol label="Species" ordinal="3"/> + </treecols> + <treechildren> + <treeitem id="http://www.some-fictitious-zoo.com/humans/sarah"> + <treerow> + <treecell label="Sarah"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="-1" id="http://www.some-fictitious-zoo.com/mammals" container="true" empty="false"> + <treerow> + <treecell label="Mammals"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="1" id="http://www.some-fictitious-zoo.com/mammals" container="true" empty="false" open="true"> + <treerow> + <treecell label="Mammals"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="-4" id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="true"> + <treerow> + <treecell label="Crustaceans"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="4" id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="true"> + <treerow> + <treecell label="Crustaceans"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/birds/emu"> + <treerow> + <treecell label="Emu"/> + <treecell/> + </treerow> + </treeitem> + </treechildren> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="tree element - query syntax not recursive"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = true; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + if (root.view && 1 < root.view.rowCount && root.view.isContainer(1)) + root.view.toggleOpenState(1); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/koala'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Koala'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'crustaceans/lobster'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Lobster'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'crustaceans')); + container.AppendElement(newnode); + }, + // step 4 + function(targetds, root) { + if (root.view && 11 < root.view.rowCount && root.view.isContainer(11)) + root.view.toggleOpenState(11); + }, + // step 5 + function(targetds, root) { + if (root.view && 11 < root.view.rowCount && root.view.isContainer(11)) + root.view.toggleOpenState(11); + }, + // step 6 + function(targetds, root) { + if (root.view && 1 < root.view.rowCount && root.view.isContainer(1)) + root.view.toggleOpenState(1); + } +]; +]]> +</script> + +<tree xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" hidevscroll="true" hidehscroll="true" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/marked" flags="dont-recurse" id="root"> +<treecols orient="horizontal" id="treecols"> +<treecol id="treecol" primary="true" label="Name"/> +<treecol label="Species"/> +</treecols> +<template id="template"> +<query> +<content uri="?uri"/> +<member container="?uri" child="?child"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<action> +<treechildren> +<treeitem uri="?child"> +<treerow> +<treecell label="?name"/> +<treecell/> +</treerow> +</treeitem> +</treechildren> +</action> +</rule> +</template> +</tree> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_treeelementquerysyntaxnotrecursivetreebuilder.xul b/dom/xul/templates/tests/chrome/test_tmpl_treeelementquerysyntaxnotrecursivetreebuilder.xul new file mode 100644 index 000000000..106d4d7cd --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_treeelementquerysyntaxnotrecursivetreebuilder.xul @@ -0,0 +1,145 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + tree element - query syntax not recursive tree builder +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <treecols id="treecols" orient="horizontal"> + <treecol id="treecol" primary="true" label="Name" ordinal="1"/> + <treecol label="Species" ordinal="3"/> + </treecols> + <treechildren> + <treeitem id="http://www.some-fictitious-zoo.com/humans/sarah"> + <treerow> + <treecell label="Sarah"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="-1" id="http://www.some-fictitious-zoo.com/mammals" container="true" empty="true"> + <treerow> + <treecell label="Mammals"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="1" id="http://www.some-fictitious-zoo.com/mammals" container="true" empty="true" open=""> + <treerow> + <treecell label="Mammals"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="-4" id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="true"> + <treerow> + <treecell label="Crustaceans"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="4" id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="true"> + <treerow> + <treecell label="Crustaceans"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/birds/emu"> + <treerow> + <treecell label="Emu"/> + <treecell/> + </treerow> + </treeitem> + </treechildren> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="tree element - query syntax not recursive tree builder"; +var queryType = "rdf"; +var isTreeBuilder = true; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + if (root.view && 1 < root.view.rowCount && root.view.isContainer(1)) + root.view.toggleOpenState(1); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/koala'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Koala'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'crustaceans/lobster'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Lobster'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'crustaceans')); + container.AppendElement(newnode); + }, + // step 4 + function(targetds, root) { + if (root.view && 11 < root.view.rowCount && root.view.isContainer(11)) + root.view.toggleOpenState(11); + }, + // step 5 + function(targetds, root) { + if (root.view && 11 < root.view.rowCount && root.view.isContainer(11)) + root.view.toggleOpenState(11); + }, + // step 6 + function(targetds, root) { + if (root.view && 1 < root.view.rowCount && root.view.isContainer(1)) + root.view.toggleOpenState(1); + } +]; +]]> +</script> + +<tree xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" hidevscroll="true" hidehscroll="true" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/marked" flags="dont-recurse dont-build-content" id="root"> +<treecols orient="horizontal" id="treecols"> +<treecol id="treecol" primary="true" label="Name"/> +<treecol label="Species"/> +</treecols> +<template id="template"> +<query> +<content uri="?uri"/> +<member container="?uri" child="?child"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<action> +<treechildren> +<treeitem uri="?child"> +<treerow> +<treecell label="?name"/> +<treecell/> +</treerow> +</treeitem> +</treechildren> +</action> +</rule> +</template> +</tree> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_treeelementquerysyntaxrecursive.xul b/dom/xul/templates/tests/chrome/test_tmpl_treeelementquerysyntaxrecursive.xul new file mode 100644 index 000000000..1002cd00c --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_treeelementquerysyntaxrecursive.xul @@ -0,0 +1,215 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + tree element - query syntax recursive +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <treecols id="treecols" orient="horizontal"> + <treecol id="treecol" primary="true" label="Name" ordinal="1"/> + <treecol label="Species" ordinal="3"/> + </treecols> + <treechildren> + <treeitem id="http://www.some-fictitious-zoo.com/humans/sarah"> + <treerow> + <treecell label="Sarah"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="-1" id="http://www.some-fictitious-zoo.com/mammals" container="true" empty="false"> + <treerow> + <treecell label="Mammals"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="1" id="http://www.some-fictitious-zoo.com/mammals" container="true" empty="false" open="true"> + <treerow> + <treecell label="Mammals"/> + <treecell/> + </treerow> + <treechildren> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/lion"> + <treerow> + <treecell label="Lion"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/hippopotamus"> + <treerow> + <treecell label="HIPPOPOTAMUS"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/africanelephant"> + <treerow> + <treecell label="African Elephant"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="2" id="http://www.some-fictitious-zoo.com/mammals/koala"> + <treerow> + <treecell label="Koala"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/llama"> + <treerow> + <treecell label="LLAMA"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/polarbear"> + <treerow> + <treecell label="Polar Bear"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/aardvark"> + <treerow> + <treecell label="aardvark"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo"> + <treerow> + <treecell label="Nine-banded Armadillo"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/gorilla"> + <treerow> + <treecell label="Gorilla"/> + <treecell/> + </treerow> + </treeitem> + </treechildren> + </treeitem> + <treeitem step="-3" id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="true"> + <treerow> + <treecell label="Crustaceans"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="3,-4" id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="false"> + <treerow> + <treecell label="Crustaceans"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="4" id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="false" open="true"> + <treerow> + <treecell label="Crustaceans"/> + <treecell/> + </treerow> + <treechildren> + <treeitem id="http://www.some-fictitious-zoo.com/crustaceans/lobster"> + <treerow> + <treecell label="Lobster"/> + <treecell/> + </treerow> + </treeitem> + </treechildren> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/birds/emu"> + <treerow> + <treecell label="Emu"/> + <treecell/> + </treerow> + </treeitem> + </treechildren> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="tree element - query syntax recursive"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = true; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + if (root.view && 1 < root.view.rowCount && root.view.isContainer(1)) + root.view.toggleOpenState(1); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/koala'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Koala'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'crustaceans/lobster'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Lobster'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'crustaceans')); + container.AppendElement(newnode); + }, + // step 4 + function(targetds, root) { + if (root.view && 11 < root.view.rowCount && root.view.isContainer(11)) + root.view.toggleOpenState(11); + }, + // step 5 + function(targetds, root) { + if (root.view && 11 < root.view.rowCount && root.view.isContainer(11)) + root.view.toggleOpenState(11); + }, + // step 6 + function(targetds, root) { + if (root.view && 1 < root.view.rowCount && root.view.isContainer(1)) + root.view.toggleOpenState(1); + } +]; +]]> +</script> + +<tree xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" hidevscroll="true" hidehscroll="true" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/marked" id="root"> +<treecols orient="horizontal" id="treecols"> +<treecol id="treecol" primary="true" label="Name"/> +<treecol label="Species"/> +</treecols> +<template id="template"> +<query> +<content uri="?uri"/> +<member container="?uri" child="?child"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<action> +<treechildren> +<treeitem uri="?child"> +<treerow> +<treecell label="?name"/> +<treecell/> +</treerow> +</treeitem> +</treechildren> +</action> +</rule> +</template> +</tree> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_treeelementquerysyntaxrecursivemultiplerules.xul b/dom/xul/templates/tests/chrome/test_tmpl_treeelementquerysyntaxrecursivemultiplerules.xul new file mode 100644 index 000000000..282beb69d --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_treeelementquerysyntaxrecursivemultiplerules.xul @@ -0,0 +1,268 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + tree element - query syntax recursive multiple rules +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <treecols id="treecols" orient="horizontal"> + <treecol id="treecol" primary="true" label="Name" ordinal="1"/> + <treecol label="Species" ordinal="3"/> + </treecols> + <treechildren> + <treeitem id="http://www.some-fictitious-zoo.com/humans/sarah"> + <treerow> + <treecell label="Sarah"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="-1" id="http://www.some-fictitious-zoo.com/mammals" container="true" empty="false"> + <treerow> + <treecell/> + <treecell label="Is this cool: Mammals?"/> + </treerow> + </treeitem> + <treeitem step="1" id="http://www.some-fictitious-zoo.com/mammals" container="true" empty="false" open="true"> + <treerow> + <treecell/> + <treecell label="Is this cool: Mammals?"/> + </treerow> + <treechildren> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/lion"> + <treerow> + <treecell/> + <treecell label="Is this cool: Lion?"/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/hippopotamus"> + <treerow> + <treecell label="HIPPOPOTAMUS"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/africanelephant"> + <treerow> + <treecell/> + <treecell label="Is this cool: African Elephant?"/> + </treerow> + </treeitem> + <treeitem step="2" id="http://www.some-fictitious-zoo.com/mammals/koala"> + <treerow> + <treecell/> + <treecell label="Is this cool: Koala?"/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/llama"> + <treerow> + <treecell/> + <treecell label="Is this cool: LLAMA?"/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/polarbear"> + <treerow> + <treecell/> + <treecell label="Is this cool: Polar Bear?"/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/aardvark"> + <treerow> + <treecell label="aardvark"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo"> + <treerow> + <treecell/> + <treecell label="Is this cool: Nine-banded Armadillo?"/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/gorilla"> + <treerow> + <treecell/> + <treecell label="Is this cool: Gorilla?"/> + </treerow> + </treeitem> + </treechildren> + </treeitem> + <treeitem step="-3" id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="true"> + <treerow> + <treecell label="Crustaceans"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="3,-4" id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="false"> + <treerow> + <treecell label="Crustaceans"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="5" id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="false" open="true"> + <treerow> + <treecell label="Crustaceans"/> + <treecell/> + </treerow> + <treechildren> + <treeitem id="http://www.some-fictitious-zoo.com/crustaceans/lobster"> + <treerow> + <treecell/> + <treecell label="Is this cool: Lobster?"/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/crustaceans/crayfish"> + <treerow> + <treecell label="Crayfish"/> + <treecell/> + </treerow> + </treeitem> + </treechildren> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/birds/emu"> + <treerow> + <treecell label="Emu"/> + <treecell/> + </treerow> + </treeitem> + </treechildren> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="tree element - query syntax recursive multiple rules"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = true; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + if (root.view && 1 < root.view.rowCount && root.view.isContainer(1)) + root.view.toggleOpenState(1); + expectConsoleMessage(ZOO_NS + 'mammals', ZOO_NS + 'mammals/lion', true, true, + '1 matching rule 1'); + expectConsoleMessage(ZOO_NS + 'mammals', ZOO_NS + 'mammals/hippopotamus', true, true, + '1 matching rule 2'); + expectConsoleMessage(ZOO_NS + 'mammals', ZOO_NS + 'mammals/africanelephant', true, true, + '1 matching rule 1'); + expectConsoleMessage(ZOO_NS + 'mammals', ZOO_NS + 'mammals/llama', true, true, + '1 matching rule 1'); + expectConsoleMessage(ZOO_NS + 'mammals', ZOO_NS + 'mammals/polarbear', true, true, + '1 matching rule 1'); + expectConsoleMessage(ZOO_NS + 'mammals', ZOO_NS + 'mammals/aardvark', true, true, + '1 matching rule 2'); + expectConsoleMessage(ZOO_NS + 'mammals', ZOO_NS + 'mammals/ninebandedarmadillo', true, true, + '1 matching rule 1'); + expectConsoleMessage(ZOO_NS + 'mammals', ZOO_NS + 'mammals/gorilla', true, true, + '1 matching rule 1'); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/koala'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Koala'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '4', true); + expectConsoleMessage(ZOO_NS + 'mammals', ZOO_NS + 'mammals/koala', true, true, + '1 matching rule 1'); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'crustaceans/lobster'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Lobster'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'crustaceans')); + container.AppendElement(newnode); + }, + // step 4 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'crustaceans/crayfish'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Crayfish'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'crustaceans')); + container.AppendElement(newnode); + }, + // step 5 + function(targetds, root) { + if (root.view && 11 < root.view.rowCount && root.view.isContainer(11)) + root.view.toggleOpenState(11); + expectConsoleMessage(ZOO_NS + 'crustaceans', ZOO_NS + 'crustaceans/lobster', true, true, + '1 matching rule 1'); + expectConsoleMessage(ZOO_NS + 'crustaceans', ZOO_NS + 'crustaceans/crayfish', true, true, + '1 matching rule 2'); + }, + // step 6 + function(targetds, root) { + if (root.view && 11 < root.view.rowCount && root.view.isContainer(11)) + root.view.toggleOpenState(11); + }, + // step 7 + function(targetds, root) { + if (root.view && 1 < root.view.rowCount && root.view.isContainer(1)) + root.view.toggleOpenState(1); + } +]; +]]> +</script> + +<tree xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" hidevscroll="true" hidehscroll="true" + flags="logging" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/marked" id="root"> +<treecols orient="horizontal" id="treecols"> +<treecol id="treecol" primary="true" label="Name"/> +<treecol label="Species"/> +</treecols> +<template id="template"> +<query> +<content uri="?uri"/> +<member container="?uri" child="?child"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions> +<where subject="?name" rel="contains" value="l" ignorecase="true"/> +</conditions> +<action> +<treechildren> +<treeitem uri="?child"> +<treerow> +<treecell/> +<treecell label="Is this cool: ?name^??"/> +</treerow> +</treeitem> +</treechildren> +</action> +</rule> +<rule> +<action> +<treechildren> +<treeitem uri="?child"> +<treerow> +<treecell label="?name"/> +<treecell/> +</treerow> +</treeitem> +</treechildren> +</action> +</rule> +</template> +</tree> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_treeelementquerysyntaxrecursivemultiplerulestreebuilder.xul b/dom/xul/templates/tests/chrome/test_tmpl_treeelementquerysyntaxrecursivemultiplerulestreebuilder.xul new file mode 100644 index 000000000..8c9e5c012 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_treeelementquerysyntaxrecursivemultiplerulestreebuilder.xul @@ -0,0 +1,230 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + tree element - query syntax recursive multiple rules tree builder +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <treecols id="treecols" orient="horizontal"> + <treecol id="treecol" primary="true" label="Name" ordinal="1"/> + <treecol label="Species" ordinal="3"/> + </treecols> + <treechildren> + <treeitem id="http://www.some-fictitious-zoo.com/humans/sarah"> + <treerow> + <treecell label="Sarah"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="-1" id="http://www.some-fictitious-zoo.com/mammals" container="true" empty="false"> + <treerow> + <treecell/> + <treecell label="Is this cool: Mammals?"/> + </treerow> + </treeitem> + <treeitem step="1" id="http://www.some-fictitious-zoo.com/mammals" container="true" empty="false" open="true"> + <treerow> + <treecell/> + <treecell label="Is this cool: Mammals?"/> + </treerow> + <treechildren> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/lion"> + <treerow> + <treecell/> + <treecell label="Is this cool: Lion?"/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/hippopotamus"> + <treerow> + <treecell label="HIPPOPOTAMUS"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/africanelephant"> + <treerow> + <treecell/> + <treecell label="Is this cool: African Elephant?"/> + </treerow> + </treeitem> + <treeitem step="2" id="http://www.some-fictitious-zoo.com/mammals/koala"> + <treerow> + <treecell/> + <treecell label="Is this cool: Koala?"/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/llama"> + <treerow> + <treecell/> + <treecell label="Is this cool: LLAMA?"/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/polarbear"> + <treerow> + <treecell/> + <treecell label="Is this cool: Polar Bear?"/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/aardvark"> + <treerow> + <treecell label="aardvark"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo"> + <treerow> + <treecell/> + <treecell label="Is this cool: Nine-banded Armadillo?"/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/gorilla"> + <treerow> + <treecell/> + <treecell label="Is this cool: Gorilla?"/> + </treerow> + </treeitem> + </treechildren> + </treeitem> + <treeitem step="-3" id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="true"> + <treerow> + <treecell label="Crustaceans"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="3,-4" id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="false"> + <treerow> + <treecell label="Crustaceans"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="4" id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="false" open="true"> + <treerow> + <treecell label="Crustaceans"/> + <treecell/> + </treerow> + <treechildren> + <treeitem id="http://www.some-fictitious-zoo.com/crustaceans/lobster"> + <treerow> + <treecell/> + <treecell label="Is this cool: Lobster?"/> + </treerow> + </treeitem> + </treechildren> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/birds/emu"> + <treerow> + <treecell label="Emu"/> + <treecell/> + </treerow> + </treeitem> + </treechildren> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="tree element - query syntax recursive multiple rules tree builder"; +var queryType = "rdf"; +var isTreeBuilder = true; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = true; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + if (root.view && 1 < root.view.rowCount && root.view.isContainer(1)) + root.view.toggleOpenState(1); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/koala'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Koala'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'crustaceans/lobster'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Lobster'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'crustaceans')); + container.AppendElement(newnode); + }, + // step 4 + function(targetds, root) { + if (root.view && 11 < root.view.rowCount && root.view.isContainer(11)) + root.view.toggleOpenState(11); + }, + // step 5 + function(targetds, root) { + if (root.view && 11 < root.view.rowCount && root.view.isContainer(11)) + root.view.toggleOpenState(11); + }, + // step 6 + function(targetds, root) { + if (root.view && 1 < root.view.rowCount && root.view.isContainer(1)) + root.view.toggleOpenState(1); + } +]; +]]> +</script> + +<tree xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" hidevscroll="true" hidehscroll="true" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/marked" flags=" dont-build-content" id="root"> +<treecols orient="horizontal" id="treecols"> +<treecol id="treecol" primary="true" label="Name"/> +<treecol label="Species"/> +</treecols> +<template id="template"> +<query> +<content uri="?uri"/> +<member container="?uri" child="?child"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions> +<where subject="?name" rel="contains" value="l" ignorecase="true"/> +</conditions> +<action> +<treechildren> +<treeitem uri="?child"> +<treerow> +<treecell/> +<treecell label="Is this cool: ?name^??"/> +</treerow> +</treeitem> +</treechildren> +</action> +</rule> +<rule> +<action> +<treechildren> +<treeitem uri="?child"> +<treerow> +<treecell label="?name"/> +<treecell/> +</treerow> +</treeitem> +</treechildren> +</action> +</rule> +</template> +</tree> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_treeelementquerysyntaxrecursivetreebuilder.xul b/dom/xul/templates/tests/chrome/test_tmpl_treeelementquerysyntaxrecursivetreebuilder.xul new file mode 100644 index 000000000..387279240 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_treeelementquerysyntaxrecursivetreebuilder.xul @@ -0,0 +1,215 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + tree element - query syntax recursive tree builder +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <treecols id="treecols" orient="horizontal"> + <treecol id="treecol" primary="true" label="Name" ordinal="1"/> + <treecol label="Species" ordinal="3"/> + </treecols> + <treechildren> + <treeitem id="http://www.some-fictitious-zoo.com/humans/sarah"> + <treerow> + <treecell label="Sarah"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="-1" id="http://www.some-fictitious-zoo.com/mammals" container="true" empty="false"> + <treerow> + <treecell label="Mammals"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="1" id="http://www.some-fictitious-zoo.com/mammals" container="true" empty="false" open="true"> + <treerow> + <treecell label="Mammals"/> + <treecell/> + </treerow> + <treechildren> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/lion"> + <treerow> + <treecell label="Lion"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/hippopotamus"> + <treerow> + <treecell label="HIPPOPOTAMUS"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/africanelephant"> + <treerow> + <treecell label="African Elephant"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="2" id="http://www.some-fictitious-zoo.com/mammals/koala"> + <treerow> + <treecell label="Koala"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/llama"> + <treerow> + <treecell label="LLAMA"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/polarbear"> + <treerow> + <treecell label="Polar Bear"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/aardvark"> + <treerow> + <treecell label="aardvark"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo"> + <treerow> + <treecell label="Nine-banded Armadillo"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/gorilla"> + <treerow> + <treecell label="Gorilla"/> + <treecell/> + </treerow> + </treeitem> + </treechildren> + </treeitem> + <treeitem step="-3" id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="true"> + <treerow> + <treecell label="Crustaceans"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="3,-4" id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="false"> + <treerow> + <treecell label="Crustaceans"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="4" id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="false" open="true"> + <treerow> + <treecell label="Crustaceans"/> + <treecell/> + </treerow> + <treechildren> + <treeitem id="http://www.some-fictitious-zoo.com/crustaceans/lobster"> + <treerow> + <treecell label="Lobster"/> + <treecell/> + </treerow> + </treeitem> + </treechildren> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/birds/emu"> + <treerow> + <treecell label="Emu"/> + <treecell/> + </treerow> + </treeitem> + </treechildren> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="tree element - query syntax recursive tree builder"; +var queryType = "rdf"; +var isTreeBuilder = true; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = true; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + if (root.view && 1 < root.view.rowCount && root.view.isContainer(1)) + root.view.toggleOpenState(1); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/koala'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Koala'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'crustaceans/lobster'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Lobster'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'crustaceans')); + container.AppendElement(newnode); + }, + // step 4 + function(targetds, root) { + if (root.view && 11 < root.view.rowCount && root.view.isContainer(11)) + root.view.toggleOpenState(11); + }, + // step 5 + function(targetds, root) { + if (root.view && 11 < root.view.rowCount && root.view.isContainer(11)) + root.view.toggleOpenState(11); + }, + // step 6 + function(targetds, root) { + if (root.view && 1 < root.view.rowCount && root.view.isContainer(1)) + root.view.toggleOpenState(1); + } +]; +]]> +</script> + +<tree xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" hidevscroll="true" hidehscroll="true" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/marked" flags=" dont-build-content" id="root"> +<treecols orient="horizontal" id="treecols"> +<treecol id="treecol" primary="true" label="Name"/> +<treecol label="Species"/> +</treecols> +<template id="template"> +<query> +<content uri="?uri"/> +<member container="?uri" child="?child"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<action> +<treechildren> +<treeitem uri="?child"> +<treerow> +<treecell label="?name"/> +<treecell/> +</treerow> +</treeitem> +</treechildren> +</action> +</rule> +</template> +</tree> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_treeelementquerysyntaxtreebuilder.xul b/dom/xul/templates/tests/chrome/test_tmpl_treeelementquerysyntaxtreebuilder.xul new file mode 100644 index 000000000..e857c8e0e --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_treeelementquerysyntaxtreebuilder.xul @@ -0,0 +1,158 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + tree element - query syntax tree builder +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <treecols id="treecols" orient="horizontal"> + <treecol id="treecol" primary="true" label="Name" ordinal="1"/> + <treecol label="Species" ordinal="3"/> + </treecols> + <treechildren> + <treeitem id="http://www.some-fictitious-zoo.com/birds/emu"> + <treerow> + <treecell label="Emu"/> + <treecell step="-6" label="12"/> + <treecell step="6" label="0"/> + </treerow> + </treeitem> + <treeitem step="1" id="http://www.some-fictitious-zoo.com/birds/archaeopteryx"> + <treerow> + <treecell label="Archaeopteryx"/> + <treecell step="-4"/> + <treecell step="4" label="5"/> + </treerow> + </treeitem> + <treeitem step="-2" id="http://www.some-fictitious-zoo.com/birds/barnowl"> + <treerow> + <treecell label="Barn Owl"/> + <treecell label="4"/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/birds/raven"> + <treerow> + <treecell step="-7" label="Raven"/> + <treecell step="7" label="Crow"/> + <treecell step="-5" label="0"/> + <treecell step="5" label="2"/> + </treerow> + </treeitem> + </treechildren> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="tree element - query syntax tree builder"; +var queryType = "rdf"; +var isTreeBuilder = true; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = true; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Archaeopteryx'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '2', true); + }, + // step 2 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + var removednode = container.RemoveElementAt('3', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Barn Owl'), true); + }, + // step 3 + function(targetds, root) { + targetds.Assert(RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'), + RDF.GetResource(ZOO_NS + 'rdf#species'), + RDF.GetLiteral('Archaeopteryx lithographica'), true); + }, + // step 4 + function(targetds, root) { + targetds.Assert(RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'), + RDF.GetResource(ZOO_NS + 'rdf#specimens'), + RDF.GetLiteral('5'), true); + }, + // step 5 + function(targetds, root) { + var subject = RDF.GetResource(ZOO_NS + 'birds/raven'); + var predicate = RDF.GetResource(ZOO_NS + 'rdf#specimens'); + var oldval = targetds.GetTarget(subject, predicate, true); + targetds.Change(RDF.GetResource(ZOO_NS + 'birds/raven'), + RDF.GetResource(ZOO_NS + 'rdf#specimens'), + oldval, RDF.GetLiteral('2')); + }, + // step 6 + function(targetds, root) { + var subject = RDF.GetResource(ZOO_NS + 'birds/emu'); + var predicate = RDF.GetResource(ZOO_NS + 'rdf#specimens'); + var oldval = targetds.GetTarget(subject, predicate, true); + targetds.Change(RDF.GetResource(ZOO_NS + 'birds/emu'), + RDF.GetResource(ZOO_NS + 'rdf#specimens'), + oldval, RDF.GetLiteral('0')); + }, + // step 7 + function(targetds, root) { + var subject = RDF.GetResource(ZOO_NS + 'birds/raven'); + var predicate = RDF.GetResource(ZOO_NS + 'rdf#name'); + var oldval = targetds.GetTarget(subject, predicate, true); + targetds.Change(RDF.GetResource(ZOO_NS + 'birds/raven'), + RDF.GetResource(ZOO_NS + 'rdf#name'), + oldval, RDF.GetLiteral('Crow')); + } +]; +]]> +</script> + +<tree xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" hidevscroll="true" hidehscroll="true" datasources="rdf:null" flags="dont-build-content" id="root" ref="http://www.some-fictitious-zoo.com/birds"> +<treecols orient="horizontal" id="treecols"> +<treecol id="treecol" primary="true" label="Name"/> +<treecol label="Species"/> +</treecols> +<template id="template"> +<query> +<content uri="?uri"/> +<member container="?uri" child="?child"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<bindings> +<binding subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#specimens" object="?specimens"/> +</bindings> +<action> +<treechildren> +<treeitem uri="?child"> +<treerow> +<treecell label="?name"/> +<treecell label="?specimens"/> +</treerow> +</treeitem> +</treechildren> +</action> +</rule> +</template> +</tree> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_treeelementsimplesyntaxnotrecursive.xul b/dom/xul/templates/tests/chrome/test_tmpl_treeelementsimplesyntaxnotrecursive.xul new file mode 100644 index 000000000..ff447916e --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_treeelementsimplesyntaxnotrecursive.xul @@ -0,0 +1,136 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + tree element - simple syntax not recursive +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <treecols id="treecols" orient="horizontal"> + <treecol id="treecol" primary="true" label="Name" ordinal="1"/> + <treecol label="Species" ordinal="3"/> + </treecols> + <treechildren> + <treeitem id="http://www.some-fictitious-zoo.com/humans/sarah"> + <treerow> + <treecell label="Sarah"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="-1" id="http://www.some-fictitious-zoo.com/mammals" container="true" empty="false"> + <treerow> + <treecell label="Mammals"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="1" id="http://www.some-fictitious-zoo.com/mammals" container="true" empty="false" open="true"> + <treerow> + <treecell label="Mammals"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="-4" id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="true"> + <treerow> + <treecell label="Crustaceans"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="4" id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="true"> + <treerow> + <treecell label="Crustaceans"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/birds/emu"> + <treerow> + <treecell label="Emu"/> + <treecell/> + </treerow> + </treeitem> + </treechildren> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="tree element - simple syntax not recursive"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = true; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + if (root.view && 1 < root.view.rowCount && root.view.isContainer(1)) + root.view.toggleOpenState(1); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/koala'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Koala'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'crustaceans/lobster'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Lobster'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'crustaceans')); + container.AppendElement(newnode); + }, + // step 4 + function(targetds, root) { + if (root.view && 11 < root.view.rowCount && root.view.isContainer(11)) + root.view.toggleOpenState(11); + }, + // step 5 + function(targetds, root) { + if (root.view && 11 < root.view.rowCount && root.view.isContainer(11)) + root.view.toggleOpenState(11); + }, + // step 6 + function(targetds, root) { + if (root.view && 1 < root.view.rowCount && root.view.isContainer(1)) + root.view.toggleOpenState(1); + } +]; +]]> +</script> + +<tree xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" hidevscroll="true" hidehscroll="true" datasources="rdf:null" flags="dont-recurse" ref="http://www.some-fictitious-zoo.com/marked" id="root"> +<treecols orient="horizontal" id="treecols"> +<treecol id="treecol" primary="true" label="Name"/> +<treecol label="Species"/> +</treecols> +<template id="template"> +<treechildren> +<treeitem uri="rdf:*"> +<treerow> +<treecell label="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +<treecell/> +</treerow> +</treeitem> +</treechildren> +</template> +</tree> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_treeelementsimplesyntaxnotrecursivetreebuilder.xul b/dom/xul/templates/tests/chrome/test_tmpl_treeelementsimplesyntaxnotrecursivetreebuilder.xul new file mode 100644 index 000000000..793506143 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_treeelementsimplesyntaxnotrecursivetreebuilder.xul @@ -0,0 +1,136 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + tree element - simple syntax not recursive tree builder +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <treecols id="treecols" orient="horizontal"> + <treecol id="treecol" primary="true" label="Name" ordinal="1"/> + <treecol label="Species" ordinal="3"/> + </treecols> + <treechildren> + <treeitem id="http://www.some-fictitious-zoo.com/humans/sarah"> + <treerow> + <treecell label="Sarah"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="-1" id="http://www.some-fictitious-zoo.com/mammals" container="true" empty="true"> + <treerow> + <treecell label="Mammals"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="1" id="http://www.some-fictitious-zoo.com/mammals" container="true" empty="true" open=""> + <treerow> + <treecell label="Mammals"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="-4" id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="true"> + <treerow> + <treecell label="Crustaceans"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="4" id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="true"> + <treerow> + <treecell label="Crustaceans"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/birds/emu"> + <treerow> + <treecell label="Emu"/> + <treecell/> + </treerow> + </treeitem> + </treechildren> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="tree element - simple syntax not recursive tree builder"; +var queryType = "rdf"; +var isTreeBuilder = true; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + if (root.view && 1 < root.view.rowCount && root.view.isContainer(1)) + root.view.toggleOpenState(1); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/koala'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Koala'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'crustaceans/lobster'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Lobster'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'crustaceans')); + container.AppendElement(newnode); + }, + // step 4 + function(targetds, root) { + if (root.view && 11 < root.view.rowCount && root.view.isContainer(11)) + root.view.toggleOpenState(11); + }, + // step 5 + function(targetds, root) { + if (root.view && 11 < root.view.rowCount && root.view.isContainer(11)) + root.view.toggleOpenState(11); + }, + // step 6 + function(targetds, root) { + if (root.view && 1 < root.view.rowCount && root.view.isContainer(1)) + root.view.toggleOpenState(1); + } +]; +]]> +</script> + +<tree xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" hidevscroll="true" hidehscroll="true" datasources="rdf:null" flags="dont-recurse dont-build-content" ref="http://www.some-fictitious-zoo.com/marked" id="root"> +<treecols orient="horizontal" id="treecols"> +<treecol id="treecol" primary="true" label="Name"/> +<treecol label="Species"/> +</treecols> +<template id="template"> +<treechildren> +<treeitem uri="rdf:*"> +<treerow> +<treecell label="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +<treecell/> +</treerow> +</treeitem> +</treechildren> +</template> +</tree> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_treeelementsimplesyntaxrecursive.xul b/dom/xul/templates/tests/chrome/test_tmpl_treeelementsimplesyntaxrecursive.xul new file mode 100644 index 000000000..009dd1367 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_treeelementsimplesyntaxrecursive.xul @@ -0,0 +1,206 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + tree element - simple syntax recursive +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <treecols id="treecols" orient="horizontal"> + <treecol id="treecol" primary="true" label="Name" ordinal="1"/> + <treecol label="Species" ordinal="3"/> + </treecols> + <treechildren> + <treeitem id="http://www.some-fictitious-zoo.com/humans/sarah"> + <treerow> + <treecell label="Sarah"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="-1" id="http://www.some-fictitious-zoo.com/mammals" container="true" empty="false"> + <treerow> + <treecell label="Mammals"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="1" id="http://www.some-fictitious-zoo.com/mammals" container="true" empty="false" open="true"> + <treerow> + <treecell label="Mammals"/> + <treecell/> + </treerow> + <treechildren> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/lion"> + <treerow> + <treecell label="Lion"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/hippopotamus"> + <treerow> + <treecell label="HIPPOPOTAMUS"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/africanelephant"> + <treerow> + <treecell label="African Elephant"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="2" id="http://www.some-fictitious-zoo.com/mammals/koala"> + <treerow> + <treecell label="Koala"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/llama"> + <treerow> + <treecell label="LLAMA"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/polarbear"> + <treerow> + <treecell label="Polar Bear"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/aardvark"> + <treerow> + <treecell label="aardvark"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo"> + <treerow> + <treecell label="Nine-banded Armadillo"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/gorilla"> + <treerow> + <treecell label="Gorilla"/> + <treecell/> + </treerow> + </treeitem> + </treechildren> + </treeitem> + <treeitem step="-3" id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="true"> + <treerow> + <treecell label="Crustaceans"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="3,-4" id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="false"> + <treerow> + <treecell label="Crustaceans"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="4" id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="false" open="true"> + <treerow> + <treecell label="Crustaceans"/> + <treecell/> + </treerow> + <treechildren> + <treeitem id="http://www.some-fictitious-zoo.com/crustaceans/lobster"> + <treerow> + <treecell label="Lobster"/> + <treecell/> + </treerow> + </treeitem> + </treechildren> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/birds/emu"> + <treerow> + <treecell label="Emu"/> + <treecell/> + </treerow> + </treeitem> + </treechildren> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="tree element - simple syntax recursive"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = true; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + if (root.view && 1 < root.view.rowCount && root.view.isContainer(1)) + root.view.toggleOpenState(1); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/koala'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Koala'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'crustaceans/lobster'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Lobster'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'crustaceans')); + container.AppendElement(newnode); + }, + // step 4 + function(targetds, root) { + if (root.view && 11 < root.view.rowCount && root.view.isContainer(11)) + root.view.toggleOpenState(11); + }, + // step 5 + function(targetds, root) { + if (root.view && 11 < root.view.rowCount && root.view.isContainer(11)) + root.view.toggleOpenState(11); + }, + // step 6 + function(targetds, root) { + if (root.view && 1 < root.view.rowCount && root.view.isContainer(1)) + root.view.toggleOpenState(1); + } +]; +]]> +</script> + +<tree xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" hidevscroll="true" hidehscroll="true" datasources="rdf:null" flags="" ref="http://www.some-fictitious-zoo.com/marked" id="root"> +<treecols orient="horizontal" id="treecols"> +<treecol id="treecol" primary="true" label="Name"/> +<treecol label="Species"/> +</treecols> +<template id="template"> +<treechildren> +<treeitem uri="rdf:*"> +<treerow> +<treecell label="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +<treecell/> +</treerow> +</treeitem> +</treechildren> +</template> +</tree> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_treeelementsimplesyntaxrecursivetreebuilder.xul b/dom/xul/templates/tests/chrome/test_tmpl_treeelementsimplesyntaxrecursivetreebuilder.xul new file mode 100644 index 000000000..3cd7a8f07 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_treeelementsimplesyntaxrecursivetreebuilder.xul @@ -0,0 +1,206 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + tree element - simple syntax recursive tree builder +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <treecols id="treecols" orient="horizontal"> + <treecol id="treecol" primary="true" label="Name" ordinal="1"/> + <treecol label="Species" ordinal="3"/> + </treecols> + <treechildren> + <treeitem id="http://www.some-fictitious-zoo.com/humans/sarah"> + <treerow> + <treecell label="Sarah"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="-1" id="http://www.some-fictitious-zoo.com/mammals" container="true" empty="false"> + <treerow> + <treecell label="Mammals"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="1" id="http://www.some-fictitious-zoo.com/mammals" container="true" empty="false" open="true"> + <treerow> + <treecell label="Mammals"/> + <treecell/> + </treerow> + <treechildren> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/lion"> + <treerow> + <treecell label="Lion"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/hippopotamus"> + <treerow> + <treecell label="HIPPOPOTAMUS"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/africanelephant"> + <treerow> + <treecell label="African Elephant"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="2" id="http://www.some-fictitious-zoo.com/mammals/koala"> + <treerow> + <treecell label="Koala"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/llama"> + <treerow> + <treecell label="LLAMA"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/polarbear"> + <treerow> + <treecell label="Polar Bear"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/aardvark"> + <treerow> + <treecell label="aardvark"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo"> + <treerow> + <treecell label="Nine-banded Armadillo"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/mammals/gorilla"> + <treerow> + <treecell label="Gorilla"/> + <treecell/> + </treerow> + </treeitem> + </treechildren> + </treeitem> + <treeitem step="-3" id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="true"> + <treerow> + <treecell label="Crustaceans"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="3,-4" id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="false"> + <treerow> + <treecell label="Crustaceans"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="4" id="http://www.some-fictitious-zoo.com/crustaceans" container="true" empty="false" open="true"> + <treerow> + <treecell label="Crustaceans"/> + <treecell/> + </treerow> + <treechildren> + <treeitem id="http://www.some-fictitious-zoo.com/crustaceans/lobster"> + <treerow> + <treecell label="Lobster"/> + <treecell/> + </treerow> + </treeitem> + </treechildren> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/birds/emu"> + <treerow> + <treecell label="Emu"/> + <treecell/> + </treerow> + </treeitem> + </treechildren> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="tree element - simple syntax recursive tree builder"; +var queryType = "rdf"; +var isTreeBuilder = true; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = true; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + if (root.view && 1 < root.view.rowCount && root.view.isContainer(1)) + root.view.toggleOpenState(1); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/koala'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Koala'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'crustaceans/lobster'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Lobster'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'crustaceans')); + container.AppendElement(newnode); + }, + // step 4 + function(targetds, root) { + if (root.view && 11 < root.view.rowCount && root.view.isContainer(11)) + root.view.toggleOpenState(11); + }, + // step 5 + function(targetds, root) { + if (root.view && 11 < root.view.rowCount && root.view.isContainer(11)) + root.view.toggleOpenState(11); + }, + // step 6 + function(targetds, root) { + if (root.view && 1 < root.view.rowCount && root.view.isContainer(1)) + root.view.toggleOpenState(1); + } +]; +]]> +</script> + +<tree xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" hidevscroll="true" hidehscroll="true" datasources="rdf:null" flags="dont-build-content" ref="http://www.some-fictitious-zoo.com/marked" id="root"> +<treecols orient="horizontal" id="treecols"> +<treecol id="treecol" primary="true" label="Name"/> +<treecol label="Species"/> +</treecols> +<template id="template"> +<treechildren> +<treeitem uri="rdf:*"> +<treerow> +<treecell label="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +<treecell/> +</treerow> +</treeitem> +</treechildren> +</template> +</tree> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_treeelementtreecell.xul b/dom/xul/templates/tests/chrome/test_tmpl_treeelementtreecell.xul new file mode 100644 index 000000000..43c926ec8 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_treeelementtreecell.xul @@ -0,0 +1,133 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + tree element - treecell +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <treecols id="treecols" orient="horizontal"> + <treecol id="treecol" primary="true" label="Name" ordinal="1"/> + <treecol label="Species" ordinal="3"/> + </treecols> + <treechildren> + <treeitem step="3" id="http://www.some-fictitious-zoo.com/birds/wren"> + <treerow> + <treecell label="Wren"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/birds/emu"> + <treerow> + <treecell label="Emu"/> + <treecell label="Dromaius novaehollandiae"/> + </treerow> + </treeitem> + <treeitem step="-4" id="http://www.some-fictitious-zoo.com/birds/barnowl"> + <treerow> + <treecell label="Barn Owl"/> + <treecell label="Tyto alba"/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/birds/raven"> + <treerow> + <treecell label="Raven"/> + <treecell label="Corvus corax"/> + </treerow> + </treeitem> + <treeitem step="2" id="http://www.some-fictitious-zoo.com/birds/archaeopteryx"> + <treerow> + <treecell label="Archaeopteryx"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="1" id="http://www.some-fictitious-zoo.com/birds/emperorpenguin"> + <treerow> + <treecell label="Emperor Penguin"/> + <treecell/> + </treerow> + </treeitem> + </treechildren> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="tree element - treecell"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/emperorpenguin'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Emperor Penguin'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Archaeopteryx'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/wren'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Wren'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + var removednode = container.RemoveElementAt('3', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Barn Owl'), true); + } +]; +]]> +</script> + +<tree xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds" hidevscroll="true" hidehscroll="true"> +<treecols orient="horizontal" id="treecols"> +<treecol id="treecol" primary="true" label="Name" ordinal="1"/> +<treecol label="Species" ordinal="3"/> +</treecols> +<template id="template"> +<treechildren id="treechildren"> +<treeitem uri="rdf:*"> +<treerow> +<treecell label="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +<treecell label="rdf:http://www.some-fictitious-zoo.com/rdf#species"/> +</treerow> +</treeitem> +</treechildren> +</template> +</tree> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_treeelementtreecellsortascending.xul b/dom/xul/templates/tests/chrome/test_tmpl_treeelementtreecellsortascending.xul new file mode 100644 index 000000000..da6c52507 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_treeelementtreecellsortascending.xul @@ -0,0 +1,133 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + tree element - treecell sort ascending +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <treecols id="treecols" orient="horizontal"> + <treecol id="treecol" primary="true" label="Name" ordinal="1" sort="rdf:http://www.some-fictitious-zoo.com/rdf#name" sortDirection="ascending" sortActive="true"/> + <treecol label="Species" ordinal="3"/> + </treecols> + <treechildren> + <treeitem step="2" id="http://www.some-fictitious-zoo.com/birds/archaeopteryx"> + <treerow> + <treecell label="Archaeopteryx"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="-4" id="http://www.some-fictitious-zoo.com/birds/barnowl"> + <treerow> + <treecell label="Barn Owl"/> + <treecell label="Tyto alba"/> + </treerow> + </treeitem> + <treeitem step="1" id="http://www.some-fictitious-zoo.com/birds/emperorpenguin"> + <treerow> + <treecell label="Emperor Penguin"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/birds/emu"> + <treerow> + <treecell label="Emu"/> + <treecell label="Dromaius novaehollandiae"/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/birds/raven"> + <treerow> + <treecell label="Raven"/> + <treecell label="Corvus corax"/> + </treerow> + </treeitem> + <treeitem step="3" id="http://www.some-fictitious-zoo.com/birds/wren"> + <treerow> + <treecell label="Wren"/> + <treecell/> + </treerow> + </treeitem> + </treechildren> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="tree element - treecell sort ascending"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/emperorpenguin'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Emperor Penguin'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Archaeopteryx'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/wren'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Wren'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + var removednode = container.RemoveElementAt('3', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Barn Owl'), true); + } +]; +]]> +</script> + +<tree xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" hidevscroll="true" hidehscroll="true" datasources="rdf:null" flags="" sort="rdf:http://www.some-fictitious-zoo.com/rdf#name" sortDirection="ascending" id="root" ref="http://www.some-fictitious-zoo.com/birds"> +<treecols orient="horizontal" id="treecols"> +<treecol id="treecol" primary="true" label="Name" ordinal="1" sort="rdf:http://www.some-fictitious-zoo.com/rdf#name" sortDirection="ascending" sortActive="true"/> +<treecol label="Species" ordinal="3"/> +</treecols> +<template id="template"> +<treechildren id="treechildren"> +<treeitem uri="rdf:*"> +<treerow> +<treecell label="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +<treecell label="rdf:http://www.some-fictitious-zoo.com/rdf#species"/> +</treerow> +</treeitem> +</treechildren> +</template> +</tree> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_treeelementtreecellsortascendingtreebuilder.xul b/dom/xul/templates/tests/chrome/test_tmpl_treeelementtreecellsortascendingtreebuilder.xul new file mode 100644 index 000000000..a4f0e2417 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_treeelementtreecellsortascendingtreebuilder.xul @@ -0,0 +1,133 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + tree element - treecell sort ascending tree builder +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <treecols id="treecols" orient="horizontal"> + <treecol id="treecol" primary="true" label="Name" ordinal="1" sort="rdf:http://www.some-fictitious-zoo.com/rdf#name" sortDirection="ascending" sortActive="true"/> + <treecol label="Species" ordinal="3"/> + </treecols> + <treechildren> + <treeitem step="2" id="http://www.some-fictitious-zoo.com/birds/archaeopteryx"> + <treerow> + <treecell label="Archaeopteryx"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="-4" id="http://www.some-fictitious-zoo.com/birds/barnowl"> + <treerow> + <treecell label="Barn Owl"/> + <treecell label="Tyto alba"/> + </treerow> + </treeitem> + <treeitem step="1" id="http://www.some-fictitious-zoo.com/birds/emperorpenguin"> + <treerow> + <treecell label="Emperor Penguin"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/birds/emu"> + <treerow> + <treecell label="Emu"/> + <treecell label="Dromaius novaehollandiae"/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/birds/raven"> + <treerow> + <treecell label="Raven"/> + <treecell label="Corvus corax"/> + </treerow> + </treeitem> + <treeitem step="3" id="http://www.some-fictitious-zoo.com/birds/wren"> + <treerow> + <treecell label="Wren"/> + <treecell/> + </treerow> + </treeitem> + </treechildren> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="tree element - treecell sort ascending tree builder"; +var queryType = "rdf"; +var isTreeBuilder = true; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/emperorpenguin'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Emperor Penguin'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Archaeopteryx'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/wren'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Wren'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + var removednode = container.RemoveElementAt('3', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Barn Owl'), true); + } +]; +]]> +</script> + +<tree xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" hidevscroll="true" hidehscroll="true" datasources="rdf:null" flags="dont-build-content" sort="rdf:http://www.some-fictitious-zoo.com/rdf#name" sortDirection="ascending" id="root" ref="http://www.some-fictitious-zoo.com/birds"> +<treecols orient="horizontal" id="treecols"> +<treecol id="treecol" primary="true" label="Name" ordinal="1" sort="rdf:http://www.some-fictitious-zoo.com/rdf#name" sortDirection="ascending" sortActive="true"/> +<treecol label="Species" ordinal="3"/> +</treecols> +<template id="template"> +<treechildren id="treechildren"> +<treeitem uri="rdf:*"> +<treerow> +<treecell label="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +<treecell label="rdf:http://www.some-fictitious-zoo.com/rdf#species"/> +</treerow> +</treeitem> +</treechildren> +</template> +</tree> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_treeelementtreecelltreebuilder.xul b/dom/xul/templates/tests/chrome/test_tmpl_treeelementtreecelltreebuilder.xul new file mode 100644 index 000000000..dcc14e0bb --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_treeelementtreecelltreebuilder.xul @@ -0,0 +1,133 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + tree element - treecell tree builder +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <treecols id="treecols" orient="horizontal"> + <treecol id="treecol" primary="true" label="Name" ordinal="1"/> + <treecol label="Species" ordinal="3"/> + </treecols> + <treechildren> + <treeitem step="3" id="http://www.some-fictitious-zoo.com/birds/wren"> + <treerow> + <treecell label="Wren"/> + <treecell/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/birds/emu"> + <treerow> + <treecell label="Emu"/> + <treecell label="Dromaius novaehollandiae"/> + </treerow> + </treeitem> + <treeitem step="-4" id="http://www.some-fictitious-zoo.com/birds/barnowl"> + <treerow> + <treecell label="Barn Owl"/> + <treecell label="Tyto alba"/> + </treerow> + </treeitem> + <treeitem id="http://www.some-fictitious-zoo.com/birds/raven"> + <treerow> + <treecell label="Raven"/> + <treecell label="Corvus corax"/> + </treerow> + </treeitem> + <treeitem step="2" id="http://www.some-fictitious-zoo.com/birds/archaeopteryx"> + <treerow> + <treecell label="Archaeopteryx"/> + <treecell/> + </treerow> + </treeitem> + <treeitem step="1" id="http://www.some-fictitious-zoo.com/birds/emperorpenguin"> + <treerow> + <treecell label="Emperor Penguin"/> + <treecell/> + </treerow> + </treeitem> + </treechildren> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="tree element - treecell tree builder"; +var queryType = "rdf"; +var isTreeBuilder = true; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = true; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/emperorpenguin'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Emperor Penguin'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Archaeopteryx'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/wren'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Wren'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + var removednode = container.RemoveElementAt('3', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Barn Owl'), true); + } +]; +]]> +</script> + +<tree xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" hidevscroll="true" hidehscroll="true" datasources="rdf:null" flags="dont-build-content" id="root" ref="http://www.some-fictitious-zoo.com/birds"> +<treecols orient="horizontal" id="treecols"> +<treecol id="treecol" primary="true" label="Name" ordinal="1"/> +<treecol label="Species" ordinal="3"/> +</treecols> +<template id="template"> +<treechildren id="treechildren"> +<treeitem uri="rdf:*"> +<treerow> +<treecell label="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +<treecell label="rdf:http://www.some-fictitious-zoo.com/rdf#species"/> +</treerow> +</treeitem> +</treechildren> +</template> +</tree> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_treeelementtreeitemonly.xul b/dom/xul/templates/tests/chrome/test_tmpl_treeelementtreeitemonly.xul new file mode 100644 index 000000000..fb94cbdad --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_treeelementtreeitemonly.xul @@ -0,0 +1,96 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + tree element - treeitem only +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <treecols id="treecols" orient="horizontal"> + <treecol id="treecol" primary="true" label="Name" ordinal="1"/> + </treecols> + <treechildren> + <treeitem step="3" id="http://www.some-fictitious-zoo.com/birds/wren" label="Wren"/> + <treeitem id="http://www.some-fictitious-zoo.com/birds/emu" label="Emu"/> + <treeitem step="-4" id="http://www.some-fictitious-zoo.com/birds/barnowl" label="Barn Owl"/> + <treeitem id="http://www.some-fictitious-zoo.com/birds/raven" label="Raven"/> + <treeitem step="2" id="http://www.some-fictitious-zoo.com/birds/archaeopteryx" label="Archaeopteryx"/> + <treeitem step="1" id="http://www.some-fictitious-zoo.com/birds/emperorpenguin" label="Emperor Penguin"/> + </treechildren> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="tree element - treeitem only"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/emperorpenguin'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Emperor Penguin'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Archaeopteryx'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/wren'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Wren'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + var removednode = container.RemoveElementAt('3', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Barn Owl'), true); + } +]; +]]> +</script> + +<tree xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds" hidevscroll="true" hidehscroll="true"> +<treecols orient="horizontal" id="treecols"> +<treecol id="treecol" primary="true" label="Name"/> +</treecols> +<template id="template"> +<treechildren id="treechildren"> +<treeitem uri="rdf:*" label="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +</treechildren> +</template> +</tree> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_treeelementtreeitemsortascending.xul b/dom/xul/templates/tests/chrome/test_tmpl_treeelementtreeitemsortascending.xul new file mode 100644 index 000000000..654e79e1c --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_treeelementtreeitemsortascending.xul @@ -0,0 +1,96 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + tree element - treeitem sort ascending +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <treecols id="treecols" orient="horizontal"> + <treecol id="treecol" primary="true" label="Name" ordinal="1" sort="rdf:http://www.some-fictitious-zoo.com/rdf#name" sortDirection="ascending" sortActive="true"/> + </treecols> + <treechildren> + <treeitem step="2" id="http://www.some-fictitious-zoo.com/birds/archaeopteryx" label="Archaeopteryx"/> + <treeitem step="-4" id="http://www.some-fictitious-zoo.com/birds/barnowl" label="Barn Owl"/> + <treeitem step="1" id="http://www.some-fictitious-zoo.com/birds/emperorpenguin" label="Emperor Penguin"/> + <treeitem id="http://www.some-fictitious-zoo.com/birds/emu" label="Emu"/> + <treeitem id="http://www.some-fictitious-zoo.com/birds/raven" label="Raven"/> + <treeitem step="3" id="http://www.some-fictitious-zoo.com/birds/wren" label="Wren"/> + </treechildren> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="tree element - treeitem sort ascending"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/emperorpenguin'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Emperor Penguin'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Archaeopteryx'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/wren'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Wren'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + var removednode = container.RemoveElementAt('3', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Barn Owl'), true); + } +]; +]]> +</script> + +<tree xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" hidevscroll="true" hidehscroll="true" datasources="rdf:null" sort="rdf:http://www.some-fictitious-zoo.com/rdf#name" sortDirection="ascending" id="root" ref="http://www.some-fictitious-zoo.com/birds"> +<treecols orient="horizontal" id="treecols"> +<treecol id="treecol" primary="true" label="Name" ordinal="1" sort="rdf:http://www.some-fictitious-zoo.com/rdf#name" sortDirection="ascending" sortActive="true"/> +</treecols> +<template id="template"> +<treechildren id="treechildren"> +<treeitem uri="rdf:*" label="rdf:http://www.some-fictitious-zoo.com/rdf#name"/> +</treechildren> +</template> +</tree> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_twogenerationnodes.xul b/dom/xul/templates/tests/chrome/test_tmpl_twogenerationnodes.xul new file mode 100644 index 000000000..17528d34b --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_twogenerationnodes.xul @@ -0,0 +1,100 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + two generation nodes +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <button step="3" id="http://www.some-fictitious-zoo.com/birds/wren" label="Wren"/> + <button step="3" id="http://www.some-fictitious-zoo.com/birds/wren" label="Wren"/> + <button id="http://www.some-fictitious-zoo.com/birds/emu" label="Emu"/> + <button id="http://www.some-fictitious-zoo.com/birds/emu" label="Emu"/> + <button step="-4" id="http://www.some-fictitious-zoo.com/birds/barnowl" label="Barn Owl"/> + <button step="-4" id="http://www.some-fictitious-zoo.com/birds/barnowl" label="Barn Owl"/> + <button id="http://www.some-fictitious-zoo.com/birds/raven" label="Raven"/> + <button id="http://www.some-fictitious-zoo.com/birds/raven" label="Raven"/> + <button step="2" id="http://www.some-fictitious-zoo.com/birds/archaeopteryx" label="Archaeopteryx"/> + <button step="2" id="http://www.some-fictitious-zoo.com/birds/archaeopteryx" label="Archaeopteryx"/> + <button step="1" id="http://www.some-fictitious-zoo.com/birds/emperorpenguin" label="Emperor Penguin"/> + <button step="1" id="http://www.some-fictitious-zoo.com/birds/emperorpenguin" label="Emperor Penguin"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="two generation nodes"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/emperorpenguin'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Emperor Penguin'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/archaeopteryx'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Archaeopteryx'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'birds/wren'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Wren'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'birds')); + var removednode = container.RemoveElementAt('3', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Barn Owl'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/birds"> +<template> +<query> +<content uri="?uri"/> +<member container="?uri" child="?child"/> +<triple subject="?child" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<action> +<button uri="?child" label="?name"/> +<button uri="?child" label="?name"/> +</action> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_whereafterignorecase.xul b/dom/xul/templates/tests/chrome/test_tmpl_whereafterignorecase.xul new file mode 100644 index 000000000..1fda7ec75 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_whereafterignorecase.xul @@ -0,0 +1,60 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - after ignore case +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - after ignore case"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="after" ignorecase="true" value="l"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_whereafterlowercase.xul b/dom/xul/templates/tests/chrome/test_tmpl_whereafterlowercase.xul new file mode 100644 index 000000000..987c9a468 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_whereafterlowercase.xul @@ -0,0 +1,63 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - after lowercase +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - after lowercase"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = true; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="after" value="l"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_whereafternegation.xul b/dom/xul/templates/tests/chrome/test_tmpl_whereafternegation.xul new file mode 100644 index 000000000..4ae159991 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_whereafternegation.xul @@ -0,0 +1,59 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - after negation +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - after negation"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="after" negate="true" value="H"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_whereafteruppercase.xul b/dom/xul/templates/tests/chrome/test_tmpl_whereafteruppercase.xul new file mode 100644 index 000000000..4f11a3de3 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_whereafteruppercase.xul @@ -0,0 +1,60 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - after uppercase +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - after uppercase"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="after" value="L"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_wherebeforeignorecase.xul b/dom/xul/templates/tests/chrome/test_tmpl_wherebeforeignorecase.xul new file mode 100644 index 000000000..130f3ee76 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_wherebeforeignorecase.xul @@ -0,0 +1,60 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - before ignore case +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - before ignore case"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = true; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="before" ignorecase="true" value="h"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_wherebeforelowercase.xul b/dom/xul/templates/tests/chrome/test_tmpl_wherebeforelowercase.xul new file mode 100644 index 000000000..b6a6a1de3 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_wherebeforelowercase.xul @@ -0,0 +1,64 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - before lowercase +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - before lowercase"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = true; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="before" value="l"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_wherebeforenegation.xul b/dom/xul/templates/tests/chrome/test_tmpl_wherebeforenegation.xul new file mode 100644 index 000000000..9885dbfce --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_wherebeforenegation.xul @@ -0,0 +1,58 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - before negation +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - before negation"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="before" negate="true" value="N"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_wherebeforeuppercase.xul b/dom/xul/templates/tests/chrome/test_tmpl_wherebeforeuppercase.xul new file mode 100644 index 000000000..40cd73854 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_wherebeforeuppercase.xul @@ -0,0 +1,59 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - before uppercase +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - before uppercase"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="before" value="H"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_wherecontains.xul b/dom/xul/templates/tests/chrome/test_tmpl_wherecontains.xul new file mode 100644 index 000000000..09818b1e1 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_wherecontains.xul @@ -0,0 +1,113 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - contains +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label step="3" id="http://www.some-fictitious-zoo.com/mammals/zebra" value="Zebra"/> + <label step="-4" id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <label step="1" id="http://www.some-fictitious-zoo.com/mammals/arctichare" value="Arctic Hare"/> + <label step="5" id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - contains"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/arctichare'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Arctic Hare'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/koala'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Koala'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/zebra'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Zebra'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + var removednode = container.RemoveElementAt('4', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('African Elephant'), true); + }, + // step 5 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/africanelephant'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('African Elephant'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.AppendElement(newnode); + }, + // step 6 + function(targetds, root) { + targetds.Assert(RDF.GetResource(ZOO_NS + 'mammals/koala'), + RDF.GetResource(ZOO_NS + 'rdf#specimensAsString'), + RDF.GetLiteral('8'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="contains" value="e"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_wherecontainsignorecase.xul b/dom/xul/templates/tests/chrome/test_tmpl_wherecontainsignorecase.xul new file mode 100644 index 000000000..ecec06997 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_wherecontainsignorecase.xul @@ -0,0 +1,59 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - contains ignore case +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - contains ignore case"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="contains" ignorecase="true" value="P"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_wherecontainsnegation.xul b/dom/xul/templates/tests/chrome/test_tmpl_wherecontainsnegation.xul new file mode 100644 index 000000000..07a421ef0 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_wherecontainsnegation.xul @@ -0,0 +1,59 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - contains negation +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - contains negation"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="contains" negate="true" value="a"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_wherecontainsnumber.xul b/dom/xul/templates/tests/chrome/test_tmpl_wherecontainsnumber.xul new file mode 100644 index 000000000..b1934edca --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_wherecontainsnumber.xul @@ -0,0 +1,59 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - contains number +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="2"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="20"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="2"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - contains number"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#specimens" object="?specimens"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?specimens" rel="contains" value="2"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?specimens"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_wherecontainsnumberstring.xul b/dom/xul/templates/tests/chrome/test_tmpl_wherecontainsnumberstring.xul new file mode 100644 index 000000000..033c87506 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_wherecontainsnumberstring.xul @@ -0,0 +1,59 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - contains number string +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="2"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="20"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="2"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - contains number string"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#specimensAsString" object="?specimens"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?specimens" rel="contains" value="2"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?specimens"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_wherecontainsresource.xul b/dom/xul/templates/tests/chrome/test_tmpl_wherecontainsresource.xul new file mode 100644 index 000000000..45569d41e --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_wherecontainsresource.xul @@ -0,0 +1,57 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - contains resource +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - contains resource"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?animal" rel="contains" value="/llama"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_wherecontainstwo.xul b/dom/xul/templates/tests/chrome/test_tmpl_wherecontainstwo.xul new file mode 100644 index 000000000..1519862fa --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_wherecontainstwo.xul @@ -0,0 +1,113 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - contains two +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label step="-4" id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + <label step="1" id="http://www.some-fictitious-zoo.com/mammals/arctichare" value="Arctic Hare"/> + <label step="5" id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - contains two"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/arctichare'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Arctic Hare'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/koala'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Koala'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/zebra'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Zebra'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + var removednode = container.RemoveElementAt('4', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('African Elephant'), true); + }, + // step 5 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/africanelephant'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('African Elephant'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.AppendElement(newnode); + }, + // step 6 + function(targetds, root) { + targetds.Assert(RDF.GetResource(ZOO_NS + 'mammals/koala'), + RDF.GetResource(ZOO_NS + 'rdf#specimensAsString'), + RDF.GetLiteral('8'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="contains" value="i"/> +<where subject="?name" rel="contains" value="r"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_whereendswith.xul b/dom/xul/templates/tests/chrome/test_tmpl_whereendswith.xul new file mode 100644 index 000000000..69a6d68fc --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_whereendswith.xul @@ -0,0 +1,57 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - endswith +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - endswith"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="endswith" value="a"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_whereendswithignorecase.xul b/dom/xul/templates/tests/chrome/test_tmpl_whereendswithignorecase.xul new file mode 100644 index 000000000..34794c51a --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_whereendswithignorecase.xul @@ -0,0 +1,58 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - endswith ignore case +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - endswith ignore case"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="endswith" ignorecase="true" value="A"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_whereendswithnegation.xul b/dom/xul/templates/tests/chrome/test_tmpl_whereendswithnegation.xul new file mode 100644 index 000000000..8d3d0c575 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_whereendswithnegation.xul @@ -0,0 +1,63 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - endswith negation +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - endswith negation"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="endswith" negate="true" value="k"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_whereequals.xul b/dom/xul/templates/tests/chrome/test_tmpl_whereequals.xul new file mode 100644 index 000000000..2679c607e --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_whereequals.xul @@ -0,0 +1,109 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - equals +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label step="-4" id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label step="5" id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - equals"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/arctichare'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Arctic Hare'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/koala'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Koala'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/zebra'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Zebra'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + var removednode = container.RemoveElementAt('4', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('African Elephant'), true); + }, + // step 5 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/africanelephant'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('African Elephant'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.AppendElement(newnode); + }, + // step 6 + function(targetds, root) { + targetds.Assert(RDF.GetResource(ZOO_NS + 'mammals/koala'), + RDF.GetResource(ZOO_NS + 'rdf#specimensAsString'), + RDF.GetLiteral('8'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="equals" value="African Elephant"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_whereequalsignorecase.xul b/dom/xul/templates/tests/chrome/test_tmpl_whereequalsignorecase.xul new file mode 100644 index 000000000..e26be898b --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_whereequalsignorecase.xul @@ -0,0 +1,57 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - equals ignore case +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - equals ignore case"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="equals" ignorecase="true" value="GoriLLA"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_whereequalsmultiple.xul b/dom/xul/templates/tests/chrome/test_tmpl_whereequalsmultiple.xul new file mode 100644 index 000000000..00558388d --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_whereequalsmultiple.xul @@ -0,0 +1,58 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - equals multiple +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - equals multiple"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="equals" multiple="true" value="Lion,Gorilla"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_whereequalsmultiplenegation.xul b/dom/xul/templates/tests/chrome/test_tmpl_whereequalsmultiplenegation.xul new file mode 100644 index 000000000..a66792dd7 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_whereequalsmultiplenegation.xul @@ -0,0 +1,125 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - equals multiple negation +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label step="3" id="http://www.some-fictitious-zoo.com/mammals/zebra" value="Zebra"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label step="-4" id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <label step="1" id="http://www.some-fictitious-zoo.com/mammals/arctichare" value="Arctic Hare"/> + <label step="5" id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - equals multiple negation"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/arctichare'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Arctic Hare'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.AppendElement(newnode); + expectConsoleMessage(ZOO_NS + 'mammals', ZOO_NS + 'mammals/arctichare', true, true, + '1 matching rule 1'); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/koala'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Koala'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '4', true); + expectConsoleMessage(ZOO_NS + 'mammals', ZOO_NS + 'mammals/koala', true, false, + '1 (didn\'t match a rule)'); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/zebra'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Zebra'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '1', true); + expectConsoleMessage(ZOO_NS + 'mammals', ZOO_NS + 'mammals/zebra', true, true, + '1 matching rule 1'); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + var removednode = container.RemoveElementAt('4', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('African Elephant'), true); + expectConsoleMessage(ZOO_NS + 'mammals', ZOO_NS + 'mammals/africanelephant', false, true, + '1 (no new active query)'); + }, + // step 5 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/africanelephant'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('African Elephant'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.AppendElement(newnode); + expectConsoleMessage(ZOO_NS + 'mammals', ZOO_NS + 'mammals/africanelephant', true, true, + '1 matching rule 1'); + }, + // step 6 + function(targetds, root) { + targetds.Assert(RDF.GetResource(ZOO_NS + 'mammals/koala'), + RDF.GetResource(ZOO_NS + 'rdf#specimensAsString'), + RDF.GetLiteral('8'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" flags="logging" + datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="equals" multiple="true" negate="true" value="Lion,aardvark,LLAMA,Gorilla,Koala"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_whereequalsmultiplenegationignorecase.xul b/dom/xul/templates/tests/chrome/test_tmpl_whereequalsmultiplenegationignorecase.xul new file mode 100644 index 000000000..67b3f2fcf --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_whereequalsmultiplenegationignorecase.xul @@ -0,0 +1,60 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - equals multiple negation ignore case +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - equals multiple negation ignore case"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="equals" multiple="true" ignorecase="true" negate="true" value="Lion,Aardvark,llama,Polar BEAR"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_whereequalsnegation.xul b/dom/xul/templates/tests/chrome/test_tmpl_whereequalsnegation.xul new file mode 100644 index 000000000..edcac5542 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_whereequalsnegation.xul @@ -0,0 +1,63 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - equals negation +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - equals negation"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="equals" negate="true" value="Polar Bear"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_whereequalsnegationignorecase.xul b/dom/xul/templates/tests/chrome/test_tmpl_whereequalsnegationignorecase.xul new file mode 100644 index 000000000..cd877f1ce --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_whereequalsnegationignorecase.xul @@ -0,0 +1,63 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - equals negation ignore case +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - equals negation ignore case"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="equals" negate="true" ignorecase="true" value="AFRICAN Elephant"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_whereequalsnegationwrongcase.xul b/dom/xul/templates/tests/chrome/test_tmpl_whereequalsnegationwrongcase.xul new file mode 100644 index 000000000..16eca331e --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_whereequalsnegationwrongcase.xul @@ -0,0 +1,64 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - equals negation wrong case +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - equals negation wrong case"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="equals" negate="true" value="Llama"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_whereequalsnumber.xul b/dom/xul/templates/tests/chrome/test_tmpl_whereequalsnumber.xul new file mode 100644 index 000000000..c036a3c6a --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_whereequalsnumber.xul @@ -0,0 +1,58 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - equals number +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="2"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="2"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - equals number"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#specimens" object="?specimens"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?specimens" rel="equals" value="2"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?specimens"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_whereequalsothervariable.xul b/dom/xul/templates/tests/chrome/test_tmpl_whereequalsothervariable.xul new file mode 100644 index 000000000..a6ae12135 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_whereequalsothervariable.xul @@ -0,0 +1,65 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - equals other variable +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - equals other variable"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?othername"/> +</query> +<rule id="rule"> +<conditions id="conditions"> +<where subject="?name" rel="equals" value="?othername"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_whereequalsresource.xul b/dom/xul/templates/tests/chrome/test_tmpl_whereequalsresource.xul new file mode 100644 index 000000000..d2779a20f --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_whereequalsresource.xul @@ -0,0 +1,57 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - equals resource +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - equals resource"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?animal" rel="equals" value="http://www.some-fictitious-zoo.com/mammals/polarbear"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_whereequalssamevariable.xul b/dom/xul/templates/tests/chrome/test_tmpl_whereequalssamevariable.xul new file mode 100644 index 000000000..455fd147f --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_whereequalssamevariable.xul @@ -0,0 +1,64 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - equals same variable +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - equals same variable"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="equals" value="?name"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_whereequalswrongcase.xul b/dom/xul/templates/tests/chrome/test_tmpl_whereequalswrongcase.xul new file mode 100644 index 000000000..b67200b57 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_whereequalswrongcase.xul @@ -0,0 +1,55 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - equals wrong case +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"/> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - equals wrong case"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="equals" value="Llama"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_wheregreater.xul b/dom/xul/templates/tests/chrome/test_tmpl_wheregreater.xul new file mode 100644 index 000000000..5276fb598 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_wheregreater.xul @@ -0,0 +1,60 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - greater +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="14"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="5"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="20"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="7"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - greater"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#specimens" object="?specimens"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?specimens" rel="greater" value="4"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?specimens"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_wheregreaternegation.xul b/dom/xul/templates/tests/chrome/test_tmpl_wheregreaternegation.xul new file mode 100644 index 000000000..7daa0a7cc --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_wheregreaternegation.xul @@ -0,0 +1,60 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - greater negation +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="4"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="2"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="2"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="1"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - greater negation"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#specimens" object="?specimens"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?specimens" rel="greater" negate="true" value="4"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?specimens"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_wheregreaternegationstring.xul b/dom/xul/templates/tests/chrome/test_tmpl_wheregreaternegationstring.xul new file mode 100644 index 000000000..22f351547 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_wheregreaternegationstring.xul @@ -0,0 +1,60 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - greater negation string +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="4"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="2"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="2"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="1"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - greater negation string"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#specimensAsString" object="?specimens"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?specimens" rel="greater" negate="true" value="4"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?specimens"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_wheregreaterstring.xul b/dom/xul/templates/tests/chrome/test_tmpl_wheregreaterstring.xul new file mode 100644 index 000000000..f7c1e4b11 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_wheregreaterstring.xul @@ -0,0 +1,113 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - greater string +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label step="-4" id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="14"/> + <label step="6" id="http://www.some-fictitious-zoo.com/mammals/koala" value="8"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="5"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="20"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="7"/> + <label step="5" id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="14"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - greater string"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/arctichare'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Arctic Hare'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/koala'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Koala'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/zebra'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Zebra'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + var removednode = container.RemoveElementAt('4', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('African Elephant'), true); + }, + // step 5 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/africanelephant'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('African Elephant'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.AppendElement(newnode); + }, + // step 6 + function(targetds, root) { + targetds.Assert(RDF.GetResource(ZOO_NS + 'mammals/koala'), + RDF.GetResource(ZOO_NS + 'rdf#specimensAsString'), + RDF.GetLiteral('8'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#specimensAsString" object="?specimens"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?specimens" rel="greater" value="4"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?specimens"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_whereless.xul b/dom/xul/templates/tests/chrome/test_tmpl_whereless.xul new file mode 100644 index 000000000..4c42f54e2 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_whereless.xul @@ -0,0 +1,62 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - less +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="4"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="2"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="5"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="2"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="1"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="7"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - less"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#specimens" object="?specimens"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?specimens" rel="less" value="14"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?specimens"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_wherelessnegation.xul b/dom/xul/templates/tests/chrome/test_tmpl_wherelessnegation.xul new file mode 100644 index 000000000..46e9d7819 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_wherelessnegation.xul @@ -0,0 +1,61 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - less negation +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="4"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="14"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="5"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="20"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="7"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - less negation"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#specimens" object="?specimens"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?specimens" rel="less" negate="true" value="4"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?specimens"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_wherelessnegationstring.xul b/dom/xul/templates/tests/chrome/test_tmpl_wherelessnegationstring.xul new file mode 100644 index 000000000..728e7ae3d --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_wherelessnegationstring.xul @@ -0,0 +1,61 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - less negation string +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="4"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="14"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="5"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="20"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="7"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - less negation string"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#specimensAsString" object="?specimens"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?specimens" rel="less" negate="true" value="4"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?specimens"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_wherelessstring.xul b/dom/xul/templates/tests/chrome/test_tmpl_wherelessstring.xul new file mode 100644 index 000000000..445f5efa1 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_wherelessstring.xul @@ -0,0 +1,62 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - less string +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="4"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="2"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="5"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="2"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="1"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="7"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - less string"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#specimensAsString" object="?specimens"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?specimens" rel="less" value="14"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?specimens"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_wherenorel.xul b/dom/xul/templates/tests/chrome/test_tmpl_wherenorel.xul new file mode 100644 index 000000000..b7e79ba18 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_wherenorel.xul @@ -0,0 +1,69 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - no rel +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +expectLoggedMessages = function() +{ + expectedConsoleMessages.push("Error parsing template: <where> element is missing a rel attribute"); +} + +var testid ="where - no rel"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" value="Lion"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_wherenosubject.xul b/dom/xul/templates/tests/chrome/test_tmpl_wherenosubject.xul new file mode 100644 index 000000000..c6b566fbe --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_wherenosubject.xul @@ -0,0 +1,69 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - no subject +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +expectLoggedMessages = function() +{ + expectedConsoleMessages.push("Error parsing template: <where> element is missing a subject attribute"); +} + +var testid ="where - no subject"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where rel="startswith" value="d"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_wherenovalue.xul b/dom/xul/templates/tests/chrome/test_tmpl_wherenovalue.xul new file mode 100644 index 000000000..53507b0a3 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_wherenovalue.xul @@ -0,0 +1,69 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - no value +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +expectLoggedMessages = function() +{ + expectedConsoleMessages.push("Error parsing template: <where> element is missing a value attribute"); +} + +var testid ="where - no value"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="startswith"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_wherestartswith.xul b/dom/xul/templates/tests/chrome/test_tmpl_wherestartswith.xul new file mode 100644 index 000000000..6e86c981d --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_wherestartswith.xul @@ -0,0 +1,108 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - startswith +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - startswith"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = [ + // step 1 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/arctichare'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Arctic Hare'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.AppendElement(newnode); + }, + // step 2 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/koala'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Koala'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '4', true); + }, + // step 3 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/zebra'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('Zebra'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.InsertElementAt(newnode, '1', true); + }, + // step 4 + function(targetds, root) { + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + var removednode = container.RemoveElementAt('4', true); + targetds.Unassert(removednode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('African Elephant'), true); + }, + // step 5 + function(targetds, root) { + var newnode = RDF.GetResource(ZOO_NS + 'mammals/africanelephant'); + targetds.Assert(newnode, RDF.GetResource(ZOO_NS + 'rdf#name'), + RDF.GetLiteral('African Elephant'), true); + var container = ContainerUtils.MakeSeq(targetds, + RDF.GetResource(ZOO_NS + 'mammals')); + container.AppendElement(newnode); + }, + // step 6 + function(targetds, root) { + targetds.Assert(RDF.GetResource(ZOO_NS + 'mammals/koala'), + RDF.GetResource(ZOO_NS + 'rdf#specimensAsString'), + RDF.GetLiteral('8'), true); + } +]; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="startswith" value="a"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_wherestartswithignorecase.xul b/dom/xul/templates/tests/chrome/test_tmpl_wherestartswithignorecase.xul new file mode 100644 index 000000000..434ba40c4 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_wherestartswithignorecase.xul @@ -0,0 +1,58 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - startswith ignore case +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - startswith ignore case"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="startswith" ignorecase="true" value="a"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_wherestartswithmultiple.xul b/dom/xul/templates/tests/chrome/test_tmpl_wherestartswithmultiple.xul new file mode 100644 index 000000000..4876ed383 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_wherestartswithmultiple.xul @@ -0,0 +1,59 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - startswith multiple +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - startswith multiple"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="startswith" multiple="true" value="L,A"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_wherestartswithnegation.xul b/dom/xul/templates/tests/chrome/test_tmpl_wherestartswithnegation.xul new file mode 100644 index 000000000..4ab12c74d --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_wherestartswithnegation.xul @@ -0,0 +1,62 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - startswith negation +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - startswith negation"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="startswith" negate="true" value="L"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_wherestartswithunknownvariable.xul b/dom/xul/templates/tests/chrome/test_tmpl_wherestartswithunknownvariable.xul new file mode 100644 index 000000000..9fd794b92 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_wherestartswithunknownvariable.xul @@ -0,0 +1,55 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - startswith unknown variable +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"/> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - startswith unknown variable"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="startswith" value="?unknown"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_wherestartswithvariable.xul b/dom/xul/templates/tests/chrome/test_tmpl_wherestartswithvariable.xul new file mode 100644 index 000000000..41e2be959 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_wherestartswithvariable.xul @@ -0,0 +1,64 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - startswith variable +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/lion" value="Lion"/> + <label id="http://www.some-fictitious-zoo.com/mammals/hippopotamus" value="HIPPOPOTAMUS"/> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + <label id="http://www.some-fictitious-zoo.com/mammals/llama" value="LLAMA"/> + <label id="http://www.some-fictitious-zoo.com/mammals/polarbear" value="Polar Bear"/> + <label id="http://www.some-fictitious-zoo.com/mammals/aardvark" value="aardvark"/> + <label id="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo" value="Nine-banded Armadillo"/> + <label id="http://www.some-fictitious-zoo.com/mammals/gorilla" value="Gorilla"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - startswith variable"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="?name" rel="startswith" value="?name"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_wheresubjectequalsvariable.xul b/dom/xul/templates/tests/chrome/test_tmpl_wheresubjectequalsvariable.xul new file mode 100644 index 000000000..14c03898c --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_wheresubjectequalsvariable.xul @@ -0,0 +1,57 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - subject equals variable +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - subject equals variable"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="African Elephant" rel="equals" value="?name"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_wheresubjectstartswithvariable.xul b/dom/xul/templates/tests/chrome/test_tmpl_wheresubjectstartswithvariable.xul new file mode 100644 index 000000000..dd93aa285 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_wheresubjectstartswithvariable.xul @@ -0,0 +1,57 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + where - subject startswith variable +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label id="http://www.some-fictitious-zoo.com/mammals/africanelephant" value="African Elephant"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="where - subject startswith variable"; +var queryType = "rdf"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="rdf:null" ref="http://www.some-fictitious-zoo.com/mammals"> +<template id="template"> +<query id="query"> +<content uri="?uri"/> +<member container="?uri" child="?animal"/> +<triple subject="?animal" predicate="http://www.some-fictitious-zoo.com/rdf#name" object="?name"/> +</query> +<rule> +<conditions id="conditions"> +<where subject="African Elephantitis" rel="startswith" value="?name"/> +</conditions> +<action id="action"> +<label uri="?animal" value="?name"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_xmlquerysimple.xul b/dom/xul/templates/tests/chrome/test_tmpl_xmlquerysimple.xul new file mode 100644 index 000000000..9a576e370 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_xmlquerysimple.xul @@ -0,0 +1,48 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + xml query simple +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <button id="chrome://mochitests/content/chrome/dom/xul/templates/tests/chrome/animals.xml#Chamaeleo-chamaelon" label="Chameleon 2"/> + <button id="chrome://mochitests/content/chrome/dom/xul/templates/tests/chrome/animals.xml#Dromaius-novaehollandiae" label="Emu 12"/> + <button id="chrome://mochitests/content/chrome/dom/xul/templates/tests/chrome/animals.xml#Tyto-alba" label="Barn Owl 4"/> + <button id="chrome://mochitests/content/chrome/dom/xul/templates/tests/chrome/animals.xml#Corvus-corax" label="Raven 0"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="xml query simple"; +var queryType = "xml"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="animals.xml" querytype="xml" ref="."> +<template expr="class/species"> +<button uri="?" label="?name ?specimens"/> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithassign.xul b/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithassign.xul new file mode 100644 index 000000000..39992b43e --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithassign.xul @@ -0,0 +1,68 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + xml query with assign +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <hbox id="chrome://mochitests/content/chrome/dom/xul/templates/tests/chrome/animals.xml#Chamaeleo-chamaelon"> + <button label="Chameleon"/> + <label value="9"/> + </hbox> + <hbox id="chrome://mochitests/content/chrome/dom/xul/templates/tests/chrome/animals.xml#Dromaius-novaehollandiae"> + <button label="Emu"/> + <label value="3"/> + </hbox> + <hbox id="chrome://mochitests/content/chrome/dom/xul/templates/tests/chrome/animals.xml#Tyto-alba"> + <button label="Barn Owl"/> + <label value="8"/> + </hbox> + <hbox id="chrome://mochitests/content/chrome/dom/xul/templates/tests/chrome/animals.xml#Corvus-corax"> + <button label="Raven"/> + <label value="5"/> + </hbox> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="xml query with assign"; +var queryType = "xml"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="animals.xml" querytype="xml" ref="."> +<template> +<query expr="class/species"> +<assign var="?length" expr="string-length(@name)"/> +</query> +<action> +<hbox uri="?"> +<button label="?name"/> +<label value="?length"/> +</hbox> +</action> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithassignmentandcondition.xul b/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithassignmentandcondition.xul new file mode 100644 index 000000000..fdbbed321 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithassignmentandcondition.xul @@ -0,0 +1,69 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + xml query with assignment and condition +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <hbox anyid="true" container="true" empty="false"> + <label value="class"/> + <hbox anyid="true" container="true" empty="true"> + <label value="name"/> + </hbox> + </hbox> + <hbox anyid="true" container="true" empty="false"> + <label value="class"/> + <hbox anyid="true" container="true" empty="true"> + <label value="name"/> + </hbox> + <hbox anyid="true" container="true" empty="true"> + <label value="location"/> + </hbox> + </hbox> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="xml query with assignment and condition"; +var queryType = "xml"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="animals.xml" querytype="xml" ref="."> +<template> +<query> +<assign var="?name" expr="name()"/> +</query> +<rule> +<where subject="?name" rel="equals" negate="true" value="species"/> +<action> +<hbox uri="?"> +<label value="?name"/> +</hbox> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithassignmentandconditiondontrecurse.xul b/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithassignmentandconditiondontrecurse.xul new file mode 100644 index 000000000..8f7e747c3 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithassignmentandconditiondontrecurse.xul @@ -0,0 +1,60 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + xml query with assignment and condition dont-recurse +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <hbox anyid="true" container="true" empty="false"> + <label value="class"/> + </hbox> + <hbox anyid="true" container="true" empty="false"> + <label value="class"/> + </hbox> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="xml query with assignment and condition dont-recurse"; +var queryType = "xml"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="animals.xml" querytype="xml" ref="." flags="dont-recurse"> +<template> +<query> +<assign var="?name" expr="name()"/> +</query> +<rule> +<where subject="?name" rel="equals" negate="true" value="species"/> +<action> +<hbox uri="?"> +<label value="?name"/> +</hbox> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithbindinginbindings.xul b/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithbindinginbindings.xul new file mode 100644 index 000000000..90372f62c --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithbindinginbindings.xul @@ -0,0 +1,54 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + xml query with binding in bindings +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <checkbox anyid="true" container="true" empty="true" label="Reptiles"/> + <checkbox anyid="true" container="true" empty="true" label="Birds"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="xml query with binding in bindings"; +var queryType = "xml"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="animals.xml" querytype="xml" ref="."> +<template> +<query expr="class/name"/> +<rule id="rule"> +<bindings> +<binding subject="?" predicate="text()" object="?nm"/> +</bindings> +<action> +<checkbox uri="?" label="?nm"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithbindinginrule.xul b/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithbindinginrule.xul new file mode 100644 index 000000000..57ca2d838 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithbindinginrule.xul @@ -0,0 +1,64 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + xml query with binding in rule +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <label anyid="true" container="true" empty="true" value="Class Reptiles"/> + <label anyid="true" container="true" empty="true" value="Class Birds"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="xml query with binding in rule"; +var queryType = "xml"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +Components.classes["@mozilla.org/consoleservice;1"]. + getService(Components.interfaces.nsIConsoleService).reset(); + +expectLoggedMessages = function() +{ + // check log to ensure that two rows have been added + var initialNumber = Number(document.getElementById("root").firstChild.nextSibling.id.substring(3)); + expectConsoleMessage('', 'row' + initialNumber, true, true, '1 matching rule 1'); + expectConsoleMessage('', 'row' + (initialNumber + 1), true, true, '1 matching rule 1'); +} + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" + flags="logging" datasources="animals.xml" querytype="xml" ref="."> +<template> +<query expr="class/name"/> +<rule id="rule"> +<binding subject="?" predicate="concat('Class ', text())" object="?text"/> +<action> +<label uri="?" value="?text"/> +</action> +</rule> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithdifferentmember.xul b/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithdifferentmember.xul new file mode 100644 index 000000000..160d4bddb --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithdifferentmember.xul @@ -0,0 +1,48 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + xml query with different member +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <button id="chrome://mochitests/content/chrome/dom/xul/templates/tests/chrome/animals.xml#Chamaeleo-chamaelon" label="Chameleon 2"/> + <button id="chrome://mochitests/content/chrome/dom/xul/templates/tests/chrome/animals.xml#Dromaius-novaehollandiae" label="Emu 12"/> + <button id="chrome://mochitests/content/chrome/dom/xul/templates/tests/chrome/animals.xml#Tyto-alba" label="Barn Owl 4"/> + <button id="chrome://mochitests/content/chrome/dom/xul/templates/tests/chrome/animals.xml#Corvus-corax" label="Raven 0"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="xml query with different member"; +var queryType = "xml"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="animals.xml" querytype="xml" ref="."> +<template expr="class/species"> +<button uri="?memb" label="?name ?specimens"/> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithinlinedata.xul b/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithinlinedata.xul new file mode 100644 index 000000000..010a6ba49 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithinlinedata.xul @@ -0,0 +1,54 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + xml query with inline data +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <button anyid="true" label="Nathan"/> + <button anyid="true" label="Marie"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="xml query with inline data"; +var queryType = "xml"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<people id="data" xmlns:test="http://www.mozilla.com/test"> +<test:person name="Nathan"/> +<person name="Luke"/> +<xt:person xmlns:xt="http://www.mozilla.com/test" name="Marie"/> +</people> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="#data" querytype="xml" ref="."> +<template expr="test:person"> +<button uri="?" label="?name"/> +</template> +<button id="row6" label="Nathan"/> +<button id="row7" label="Marie"/> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithinlinedatawithmultiplequeries.xul b/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithinlinedatawithmultiplequeries.xul new file mode 100644 index 000000000..d9640fbde --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithinlinedatawithmultiplequeries.xul @@ -0,0 +1,71 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + xml query with inline data with multiple queries +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <button anyid="true" label="Nathan"/> + <button anyid="true" label="Marie"/> + <checkbox anyid="true" label="Nathan"/> + <checkbox anyid="true" label="Luke"/> + <checkbox anyid="true" label="Marie"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="xml query with inline data with multiple queries"; +var queryType = "xml"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<people id="data" xmlns:test="http://www.mozilla.com/test"> +<test:person name="Nathan"/> +<person name="Luke"/> +<xt:person xmlns:xt="http://www.mozilla.com/test" name="Marie"/> +</people> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="#data" querytype="xml" ref="."> +<template> +<queryset> +<query expr="test:person"/> +<action> +<button uri="?" label="?name"/> +</action> +</queryset> +<queryset> +<query/> +<action> +<checkbox uri="?" label="?name"/> +</action> +</queryset> +</template> +<button id="row9" label="Nathan"/> +<button id="row10" label="Marie"/> +<checkbox id="row11" label="Nathan"/> +<checkbox id="row12" label="Luke"/> +<checkbox id="row13" label="Marie"/> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithmultiplequeries.xul b/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithmultiplequeries.xul new file mode 100644 index 000000000..075d2c453 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithmultiplequeries.xul @@ -0,0 +1,70 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + xml query with multiple queries +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <button id="chrome://mochitests/content/chrome/dom/xul/templates/tests/chrome/animals.xml#Dromaius-novaehollandiae" label="(Dromaius-novaehollandiae) is a large bird"/> + <button id="chrome://mochitests/content/chrome/dom/xul/templates/tests/chrome/animals.xml#Tyto-alba" label="(Tyto-alba) Barn Owl"/> + <button id="chrome://mochitests/content/chrome/dom/xul/templates/tests/chrome/animals.xml#Corvus-corax" label="(Corvus-corax) Raven"/> + <label id="chrome://mochitests/content/chrome/dom/xul/templates/tests/chrome/animals.xml#Chamaeleo-chamaelon" value="Chameleon"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="xml query with multiple queries"; +var queryType = "xml"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="animals.xml" querytype="xml" ref="."> +<template> +<queryset> +<query expr="class[position()>1]/species"> +<assign var="?id" expr="concat('(', @id, ')')"/> +</query> +<rule> +<where subject="?id" rel="equals" value="(Dromaius-novaehollandiae)"/> +<action> +<button uri="?" label="?id is a large bird"/> +</action> +</rule> +<rule> +<binding subject="?" predicate="@name" object="?name"/> +<action> +<button uri="?" label="?id ?name"/> +</action> +</rule> +</queryset> +<queryset> +<query expr="class[name/text()='Reptiles']/species"/> +<action> +<label uri="?" value="?name"/> +</action> +</queryset> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithothertypes.xul b/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithothertypes.xul new file mode 100644 index 000000000..ce728b2b1 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithothertypes.xul @@ -0,0 +1,75 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + xml query with other types +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <groupbox anyid="true" container="true" empty="false"> + <caption label="Reptiles false"/> + <label id="chrome://mochitests/content/chrome/dom/xul/templates/tests/chrome/animals.xml#Chamaeleo-chamaelon" value="Chameleon"/> + </groupbox> + <groupbox anyid="true" container="true" empty="false"> + <caption label="Birds true"/> + <label id="chrome://mochitests/content/chrome/dom/xul/templates/tests/chrome/animals.xml#Dromaius-novaehollandiae" value="Emu"/> + <label id="chrome://mochitests/content/chrome/dom/xul/templates/tests/chrome/animals.xml#Tyto-alba" value="Barn Owl"/> + <label id="chrome://mochitests/content/chrome/dom/xul/templates/tests/chrome/animals.xml#Corvus-corax" value="Raven"/> + </groupbox> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="xml query with other types"; +var queryType = "xml"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="animals.xml" querytype="xml" ref="."> +<template> +<queryset> +<query expr="species"> +<assign var="?haschild" expr="boolean(location)"/> +<assign var="?name" expr="@name"/> +</query> +<action> +<label uri="?" value="?name"/> +</action> +</queryset> +<queryset> +<query> +<assign var="?name" expr="name/text()"/> +<assign var="?isbirds" expr="name/text() = 'Birds'"/> +</query> +<rule parent="vbox"> +<action> +<groupbox uri="?"> +<caption label="?name ?isbirds"/> +</groupbox> +</action> +</rule> +</queryset> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithsort.xul b/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithsort.xul new file mode 100644 index 000000000..b83fc3595 --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithsort.xul @@ -0,0 +1,51 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + xml query with sort +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <button id="chrome://mochitests/content/chrome/dom/xul/templates/tests/chrome/animals.xml#Tyto-alba" label="Barn Owl"/> + <button id="chrome://mochitests/content/chrome/dom/xul/templates/tests/chrome/animals.xml#Chamaeleo-chamaelon" label="Chameleon"/> + <button id="chrome://mochitests/content/chrome/dom/xul/templates/tests/chrome/animals.xml#Dromaius-novaehollandiae" label="Emu"/> + <button id="chrome://mochitests/content/chrome/dom/xul/templates/tests/chrome/animals.xml#Corvus-corax" label="Raven"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="xml query with sort"; +var queryType = "xml"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="animals.xml" querytype="xml" ref="." sort="?name" sortDirection="ascending"> +<template> +<query expr="class/species"/> +<action> +<button uri="?" label="?name"/> +</action> +</template> +</vbox> + +</window> diff --git a/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithsortotherfield.xul b/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithsortotherfield.xul new file mode 100644 index 000000000..ad1829d0d --- /dev/null +++ b/dom/xul/templates/tests/chrome/test_tmpl_xmlquerywithsortotherfield.xul @@ -0,0 +1,51 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> + +<!-- + xml query with sort other field +--> + +<window title="XUL Template Tests" width="500" height="600" + onload="test_template();" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;"/> + + <data id="output"> + <button id="chrome://mochitests/content/chrome/dom/xul/templates/tests/chrome/animals.xml#Tyto-alba" label="Barn Owl"/> + <button id="chrome://mochitests/content/chrome/dom/xul/templates/tests/chrome/animals.xml#Dromaius-novaehollandiae" label="Emu"/> + <button id="chrome://mochitests/content/chrome/dom/xul/templates/tests/chrome/animals.xml#Corvus-corax" label="Raven"/> + <button id="chrome://mochitests/content/chrome/dom/xul/templates/tests/chrome/animals.xml#Chamaeleo-chamaelon" label="Chameleon"/> + </data> + +<script src="templates_shared.js"/> + +<script> +<![CDATA[ +SimpleTest.waitForExplicitFinish(); + +var testid ="xml query with sort other field"; +var queryType = "xml"; +var isTreeBuilder = false; +var needsOpen = false; +var notWorkingYet = false; +var notWorkingYetDynamic = false; +var expectedOutput = document.getElementById("output"); + +var changes = []; +]]> +</script> + +<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="root" datasources="animals.xml" querytype="xml" ref="." sort="?id" sortDirection="descending"> +<template> +<query expr="class/species"/> +<action> +<button uri="?" label="?name"/> +</action> +</template> +</vbox> + +</window> diff --git a/dom/xul/test/1061864.html b/dom/xul/test/1061864.html new file mode 100644 index 000000000..ec8091a6b --- /dev/null +++ b/dom/xul/test/1061864.html @@ -0,0 +1,8 @@ +<html> +<body> +<script> +</script> +<iframe id="childiframe" src="data:text/html,Test"> +</iframe> +</body> +</html> diff --git a/dom/xul/test/398289-resource.xul b/dom/xul/test/398289-resource.xul new file mode 100644 index 000000000..71728e58b --- /dev/null +++ b/dom/xul/test/398289-resource.xul @@ -0,0 +1,42 @@ +<?xml version="1.0"?> + +<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> + +<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + buttonlabelaccept="OK" + ondialogaccept="alert('OK');" + buttonlabelcancel="Cancel" + ondialogcancel="alert('Cancel');"> + + <tabbox id="test" flex="1" persist="selectedIndex"> + + <tabs> + <tab label="One"/> + <tab label="Two"/> + </tabs> + + <tabpanels flex="1"> + + <vbox flex="1"> + <description>Text for tab ONE</description> + <description class="text-link" + onclick="window.open('https://bugzilla.mozilla.org/show_bug.cgi?id=398289');">(test case for bug 398289)</description> + <tree> + <treecols> + <treecol label="Header" flex="1"/> + </treecols> + </tree> + </vbox> + + <vbox flex="1"> + <description>Text for tab TWO</description> + <description>(When the document is reloaded, this content gets replaced by the one from the first tab.)</description> + </vbox> + + </tabpanels> + + </tabbox> + + <box height="1000"/> <!-- Push dialog buttons out of sight so that the animated default button isn't part of the snapshot --> + +</dialog> diff --git a/dom/xul/test/bug497875-iframe.xul b/dom/xul/test/bug497875-iframe.xul new file mode 100644 index 000000000..8365e7df0 --- /dev/null +++ b/dom/xul/test/bug497875-iframe.xul @@ -0,0 +1,6 @@ +<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?> +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"> +<box onerror="document.loadOverlay('file:///does-not-exist', null);" > +<html:script src="ftp://some.website.that.will.cause.an.error"/> +</box> +</window> diff --git a/dom/xul/test/chrome.ini b/dom/xul/test/chrome.ini new file mode 100644 index 000000000..d65bf168f --- /dev/null +++ b/dom/xul/test/chrome.ini @@ -0,0 +1,40 @@ +[DEFAULT] +support-files = + 398289-resource.xul + bug497875-iframe.xul + file_bug236853.rdf + overlay1_bug335375.xul + overlay2_bug335375.xul + window_bug583948.xul + window_bug757137.xul + 1061864.html + file_bug1271240.xul + file_bug1069772.xul + +[test_bug199692.xul] +[test_bug233643.xul] +[test_bug236853.xul] +[test_bug311681.xul] +[test_bug335375.xul] +[test_bug391002.xul] +[test_bug398289.html] +[test_bug403868.xul] +[test_bug414907.xul] +[test_bug418216.xul] +[test_bug445177.xul] +[test_bug449457.xul] +[test_bug468176.xul] +[test_bug497875.xul] +[test_bug583948.xul] +[test_bug640158_overlay_persist.xul] +[test_bug757137.xul] +[test_bug775972.xul] +[test_bug1061864_1.xul] +[test_bug1061864_2.xul] +[test_bug1070049_throw_from_script.xul] +[test_import_xul_to_content.xul] +[test_bug1271240.xul] +skip-if = os == "android" +[test_bug1069772.xul] +skip-if = os == "android" +[test_bug1290965.xul] diff --git a/dom/xul/test/file_bug1069772.xul b/dom/xul/test/file_bug1069772.xul new file mode 100644 index 000000000..b90985246 --- /dev/null +++ b/dom/xul/test/file_bug1069772.xul @@ -0,0 +1,143 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1069772 +--> +<window title="Mozilla Bug 1069772" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" onload="run();"> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + const {interfaces: Ci, classes: Cc, results: Cr, utils: Cu} = Components; + Cu.import("resource://testing-common/ContentTask.jsm"); + Cu.import("resource://testing-common/BrowserTestUtils.jsm"); + Cu.import("resource://gre/modules/Services.jsm"); + ContentTask.setTestScope(window.opener.wrappedJSObject); + + let imports = ['SimpleTest', 'ok', 'is']; + for (let name of imports) { + window[name] = window.opener.wrappedJSObject[name]; + } + + /** Test for Bug 1069772 **/ + function run() { + // test the transition: + // prerender => visible => hidden => visible + // on a non-remote browser + createPrerenderedBrowser(false) + .then(browser => verifyBrowser(browser, true, false)) + .then(browser => verifyVisibility(browser, 'prerender')) + .then(browser => makePrerenderedBrowserActive(browser)) + .then(browser => verifyBrowser(browser, false, false)) + .then(browser => verifyVisibility(browser, 'visible')) + .then(browser => hideBrowser(browser)) + .then(browser => verifyBrowser(browser, false, false)) + .then(browser => verifyVisibility(browser, 'hidden')) + .then(browser => showBrowser(browser)) + .then(browser => verifyBrowser(browser, false, false)) + .then(browser => verifyVisibility(browser, 'visible')) + + // test the transition: + // prerender => visible => hidden => visible + // on a remote browser + .then(() => createPrerenderedBrowser(true)) + .then(browser => verifyBrowser(browser, true, true)) + .then(browser => verifyVisibility(browser, 'prerender')) + .then(browser => makePrerenderedBrowserActive(browser)) + .then(browser => verifyBrowser(browser, false, true)) + .then(browser => verifyVisibility(browser, 'visible')) + .then(browser => hideBrowser(browser)) + .then(browser => verifyBrowser(browser, false, true)) + .then(browser => verifyVisibility(browser, 'hidden')) + .then(browser => showBrowser(browser)) + .then(browser => verifyBrowser(browser, false, true)) + .then(browser => verifyVisibility(browser, 'visible')) + + // finish test + .then(() => { + window.close(); + SimpleTest.finish(); + }); + } + + function createPrerenderedBrowser(remote) { + let browser = document.createElement('browser'); + browser.setAttribute('type', 'content'); + browser.setAttribute('prerendered', true); + browser.setAttribute('remote', remote); + browser.setAttribute('src', 'data:text/html;charset=UTF-8,<html><body>' + + '<iframe id="iframe" src="data:text/html;charset=UTF-8,Hello Frame!">' + + '</iframe></body></html>'); + + // wait for 'load' and 'pageshow' + let promises = []; + promises.push(BrowserTestUtils.browserLoaded(browser)); + promises.push(new Promise(resolve => + Services.mm.addMessageListener('test:pageshow', resolve))); + Services.mm.loadFrameScript('data:,' + + 'addEventListener("pageshow", ' + + '() => sendAsyncMessage("test:pageshow", null), false);', + true); + + document.getElementById('stack').appendChild(browser); + return Promise.all(promises).then(() => browser); + } + + function verifyBrowser(browser, prerendered, remote) { + let docShellOrTabParent = remote ? + browser.frameLoader.tabParent : browser.frameLoader.docShell; + ok(docShellOrTabParent, 'docShellOrTabParent should not be null'); + is(docShellOrTabParent.isPrerendered, prerendered, + 'isPrerendered should be ' + prerendered); + return browser; + } + + function verifyVisibility(browser, visibility) { + return ContentTask.spawn(browser, visibility, (v) => { + let iframe = content.document.getElementById('iframe'); + is(content.document.visibilityState, v, 'check doc.visibilityState'); + is(content.document.hidden, v != 'visible', 'check doc.hidden'); + is(iframe.contentDocument.visibilityState, v, 'check iframe doc.visibilityState'); + is(iframe.contentDocument.hidden, v != 'visible', 'check iframe doc.hidden'); + }).then(() => browser); + } + + function makePrerenderedBrowserActive(browser) { + let promise = waitForVisibilityChange(browser); + browser.setAttribute('prerendered', false); + browser.makePrerenderedBrowserActive(); + return promise.then(() => browser); + } + + function hideBrowser(browser) { + let promise = waitForVisibilityChange(browser); + browser.docShellIsActive = false; + return promise.then(() => browser); + } + + function showBrowser(browser) { + let promise = waitForVisibilityChange(browser); + browser.docShellIsActive = true; + return promise.then(() => browser); + } + + function waitForVisibilityChange(browser) { + return ContentTask.spawn(browser, null, () => { + return new Promise(resolve => { + let iframe = content.document.getElementById('iframe'); + iframe.contentDocument.addEventListener('visibilitychange', function listener() { + iframe.contentDocument.removeEventListener('visibilitychange', listener); + resolve(); + }); + }); + }); + } + + ]]> + </script> + <stack id="stack" flex="1" /> +</window> diff --git a/dom/xul/test/file_bug1271240.xul b/dom/xul/test/file_bug1271240.xul new file mode 100644 index 000000000..2edd5a1b4 --- /dev/null +++ b/dom/xul/test/file_bug1271240.xul @@ -0,0 +1,82 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1271240 +--> +<window title="Mozilla Bug 1271240" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" onload="run();"> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + function ok(cond, msg) { + opener.wrappedJSObject.ok(cond, msg); + } + + function is(actual, expected, msg) { + opener.wrappedJSObject.is(actual, expected, msg); + } + + /** Test for Bug 1271240 **/ + function run() { + // test non-remote, non-prerendered browser + createBrowser(false, false) + .then(browser => verifyBrowser(browser, false, false)) + + // test non-remote, prerendered browser + .then(() => createBrowser(true, false)) + .then(browser => verifyBrowser(browser, true, false)) + .then(browser => makePrerenderedBrowserActive(browser)) + .then(browser => verifyBrowser(browser, false, false)) + + // test remote, non-prerendered browser + .then(() => createBrowser(false, true)) + .then(browser => verifyBrowser(browser, false, true)) + + // test remote, prerendered browser + .then(() => createBrowser(true, true)) + .then(browser => verifyBrowser(browser, true, true)) + .then(browser => makePrerenderedBrowserActive(browser)) + .then(browser => verifyBrowser(browser, false, true)) + + // finish test + .then(() => { + opener.postMessage('finish', '*'); + window.close(); + }); + } + + function createBrowser(prerendered, remote) { + return new Promise(resolve => { + var browser = document.createElement('browser'); + browser.setAttribute('type', 'content'); + browser.setAttribute('prerendered', prerendered); + browser.setAttribute('remote', remote); + document.documentElement.appendChild(browser); + browser.loadURI('example.com'); + resolve(browser); + }); + } + + function verifyBrowser(browser, prerendered, remote) { + var docShellOrTabParent = remote ? + browser.frameLoader.tabParent : browser.frameLoader.docShell; + ok(docShellOrTabParent, 'docShellOrTabParent should not be null'); + is(docShellOrTabParent.isPrerendered, prerendered, + 'isPrerendered should be ' + prerendered); + return browser; + } + + function makePrerenderedBrowserActive(browser) { + browser.makePrerenderedBrowserActive(); + return browser; + } + + ]]> + </script> + <!-- <browser type="content-primary" flex="1" id="content" /> + <browser type="content-primary" flex="1" id="content-remote" remote="true" /> --> +</window> diff --git a/dom/xul/test/file_bug236853.rdf b/dom/xul/test/file_bug236853.rdf new file mode 100644 index 000000000..8d17bf691 --- /dev/null +++ b/dom/xul/test/file_bug236853.rdf @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:ex="http://www.ex.org/ex-rdf#"> + + <rdf:Description about="urn:root"> + <ex:nodes> + <rdf:Bag about="http://www.ex.org/nodes"> + <rdf:li> + <rdf:Description about="http://www.ex.org/nodes/A"/> + </rdf:li> + </rdf:Bag> + </ex:nodes> + </rdf:Description> +</rdf:RDF> diff --git a/dom/xul/test/mochitest.ini b/dom/xul/test/mochitest.ini new file mode 100644 index 000000000..4ef4e2b99 --- /dev/null +++ b/dom/xul/test/mochitest.ini @@ -0,0 +1,5 @@ +[DEFAULT] + +[test_bug486990.xul] +skip-if = toolkit == 'android' #TIMED_OUT +[test_bug749367.xul] diff --git a/dom/xul/test/overlay1_bug335375.xul b/dom/xul/test/overlay1_bug335375.xul new file mode 100644 index 000000000..a3448b7f8 --- /dev/null +++ b/dom/xul/test/overlay1_bug335375.xul @@ -0,0 +1,9 @@ +<?xml version="1.0"?> +<overlay id="overlay1" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <commandset id="test_commandset"> + <command id="cmd_test"/> + </commandset> + <toolbarbutton id="button_test" + command="cmd_test"/> +</overlay> diff --git a/dom/xul/test/overlay2_bug335375.xul b/dom/xul/test/overlay2_bug335375.xul new file mode 100644 index 000000000..cb37db642 --- /dev/null +++ b/dom/xul/test/overlay2_bug335375.xul @@ -0,0 +1,5 @@ +<?xml version="1.0"?> +<overlay id="overlay2" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <command id="cmd_test" accesskey="C"/> +</overlay> diff --git a/dom/xul/test/overlay_640158.xul b/dom/xul/test/overlay_640158.xul new file mode 100644 index 000000000..536beb529 --- /dev/null +++ b/dom/xul/test/overlay_640158.xul @@ -0,0 +1,8 @@ +<?xml version="1.0"?>
+<overlay id="overlay1"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <window id="rootwin">
+ <box id="bar" testattr="original"/>
+ </window>
+</overlay>
+
diff --git a/dom/xul/test/test_bug1061864_1.xul b/dom/xul/test/test_bug1061864_1.xul new file mode 100644 index 000000000..8fe3b8e30 --- /dev/null +++ b/dom/xul/test/test_bug1061864_1.xul @@ -0,0 +1,50 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1061864 +--> +<window title="Mozilla Bug 1061864" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" onload="RunTest();"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + /** Test for Bug 1061864 **/ + SimpleTest.waitForExplicitFinish(); + + function RunTest() + { + // Test that the docshell belonging to a prerendered frame loader will + // be created with the correct prerendered flag. + test(false, function() { + test(true, function() { + SimpleTest.finish(); + }); + }); + } + + function test(prerendered, callback) { + var parentIframe = document.createElement("iframe"); + if (prerendered) { + parentIframe.setIsPrerendered(); + } + parentIframe.onload = function() { + var docShell = parentIframe.frameLoader.docShell; + is(docShell.isPrerendered, prerendered, "The docshell is" + (prerendered ? "" : " not") + " prerendered"); + callback(); + } + document.documentElement.appendChild(parentIframe); + } + ]]> + </script> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1061864" + target="_blank">Mozilla Bug 1061864</a> + </body> +</window> diff --git a/dom/xul/test/test_bug1061864_2.xul b/dom/xul/test/test_bug1061864_2.xul new file mode 100644 index 000000000..e7ed5a630 --- /dev/null +++ b/dom/xul/test/test_bug1061864_2.xul @@ -0,0 +1,52 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1061864 +--> +<window title="Mozilla Bug 1061864" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" onload="RunTest();"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + /** Test for Bug 1061864 **/ + SimpleTest.waitForExplicitFinish(); + + function RunTest() + { + // Test that the docshell prerendered flag will be correctly inherited in + // prerendered documents. + test(false, function() { + test(true, function() { + SimpleTest.finish(); + }); + }); + } + + function test(prerendered, callback) { + var parentIframe = document.createElement("iframe"); + if (prerendered) { + parentIframe.setIsPrerendered(); + } + parentIframe.setAttribute("src", "1061864.html"); + parentIframe.onload = function() { + var childIframe = parentIframe.contentDocument.getElementById("childiframe"); + var childDocShell = childIframe.frameLoader.docShell; + is(childDocShell.isPrerendered, prerendered, "The docshell is" + (prerendered ? "" : " not") + " prerendered"); + callback(); + } + document.documentElement.appendChild(parentIframe); + } + ]]> + </script> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1061864" + target="_blank">Mozilla Bug 1061864</a> + </body> +</window> diff --git a/dom/xul/test/test_bug1069772.xul b/dom/xul/test/test_bug1069772.xul new file mode 100644 index 000000000..173113fdb --- /dev/null +++ b/dom/xul/test/test_bug1069772.xul @@ -0,0 +1,32 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1069772 +--> +<window title="Mozilla Bug 1069772" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + onload="loadTest();"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069772" + target="_blank">Mozilla Bug 1069772</a> + </body> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + /** Test for Bug 1069772 **/ + SimpleTest.waitForExplicitFinish(); + function loadTest() { + window.open("file_bug1069772.xul", "", "width=360,height=240,chrome"); + } + + ]]> + </script> +</window> diff --git a/dom/xul/test/test_bug1070049_throw_from_script.xul b/dom/xul/test/test_bug1070049_throw_from_script.xul new file mode 100644 index 000000000..baa04a89e --- /dev/null +++ b/dom/xul/test/test_bug1070049_throw_from_script.xul @@ -0,0 +1,41 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1070049 +--> +<window title="Mozilla Bug 1070049" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + /** Test for Bug 1070049 **/ + SimpleTest.waitForExplicitFinish(); + addLoadEvent(function() { + // Prevent the test from failing when the exception hits onerror. + SimpleTest.expectUncaughtException(); + + // Tell the test to expect exactly one console error with the given parameters, + // with SimpleTest.finish as a continuation function. + SimpleTest.monitorConsole(SimpleTest.finish, [{errorMessage: new RegExp('flimfniffle')}]); + + // Schedule the console accounting (and continuation) to run next, right + // after we throw (below). + SimpleTest.executeSoon(SimpleTest.endMonitorConsole); + + // Throw. + throw "flimfniffle"; + }); + ]]> + </script> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1070049" + target="_blank">Mozilla Bug 1070049</a> + </body> +</window> diff --git a/dom/xul/test/test_bug1271240.xul b/dom/xul/test/test_bug1271240.xul new file mode 100644 index 000000000..cfba37523 --- /dev/null +++ b/dom/xul/test/test_bug1271240.xul @@ -0,0 +1,38 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1271240 +--> +<window title="Mozilla Bug 1271240" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + onload="loadTest();"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1271240" + target="_blank">Mozilla Bug 1271240</a> + </body> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + /** Test for Bug 1271240 **/ + SimpleTest.waitForExplicitFinish(); + window.addEventListener('message', (event) => { + if (event.data == 'finish') { + SimpleTest.finish(); + } + }); + + function loadTest() { + window.open("file_bug1271240.xul", "", "chrome"); + } + + ]]> + </script> +</window> diff --git a/dom/xul/test/test_bug1290965.xul b/dom/xul/test/test_bug1290965.xul new file mode 100644 index 000000000..8701b55be --- /dev/null +++ b/dom/xul/test/test_bug1290965.xul @@ -0,0 +1,39 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:h="http://www.w3.org/1999/xhtml"> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + <toolbarbutton oncommand="++countera;" id="a">A</toolbarbutton> + <toolbarbutton oncommand="++counterb;" id="b">B</toolbarbutton> + <script type="text/javascript"> + <![CDATA[ + let aEl = document.getElementById('a'); + let bEl = document.getElementById('b'); + let countera = 0; + let counterb = 0; + + aEl.addEventListener('click', function (aEvent) { + aEvent.preventDefault(); + let cmdEvent = document.createEvent("xulcommandevent"); + cmdEvent.initCommandEvent("command", true, true, window, 0, + aEvent.ctrlKey, aEvent.altKey, aEvent.shiftKey, + aEvent.metaKey, null); + aEvent.currentTarget.dispatchEvent(cmdEvent); + }); + + bEl.addEventListener('click', function (aEvent) { + let cmdEvent = document.createEvent("xulcommandevent"); + cmdEvent.initCommandEvent("command", true, true, window, 0, + aEvent.ctrlKey, aEvent.altKey, aEvent.shiftKey, + aEvent.metaKey, null); + aEvent.currentTarget.dispatchEvent(cmdEvent); + }); + + bEl.click(); + aEl.click(); + + is(countera, 1, "Counter should be one as event fires once"); + is(counterb, 2, "Counter should be two as event fires twice"); + ]]> + </script> +</window> diff --git a/dom/xul/test/test_bug199692.xul b/dom/xul/test/test_bug199692.xul new file mode 100644 index 000000000..69a400a2a --- /dev/null +++ b/dom/xul/test/test_bug199692.xul @@ -0,0 +1,98 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=199692 +--> +<window title="Test for Bug 199692" + id="test_bug199692_xul" + xmlns:html="http://www.w3.org/1999/xhtml" + xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <vbox hidden="true"> + <bindings xmlns="http://www.mozilla.org/xbl" + xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <binding id="anon"> + <content> + <xul:label id="anon-label" value="ANON"/> + </content> + </binding> + </bindings> + </vbox> + +<body id="body" xmlns="http://www.w3.org/1999/xhtml"> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=199692">Mozilla Bug 199692</a> + +<vbox id="content" style="position: relative;" +xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <xul:label id="non-anon-label" value="a textbox!:" control="textbox"/> + <xul:textbox id="textbox" multiline="true" rows="4" /> + <xul:radiogroup style="outline: 2px solid orange;"> + <xul:radio id="unselected-radio" label="Orange" style="outline: 2px solid red;"/> + <xul:radio id="selected-radio" label="Violet" selected="true"/> + <xul:radio id="disabled-radio" label="Yellow" disabled="true"/> + </xul:radiogroup> + <hbox id="bound" style="-moz-binding:url('#anon'); border: 2px solid green;"></hbox> +</vbox> +<pre id="test"> + <script class="testbody" type="text/javascript"> +<![CDATA[ + SimpleTest.waitForExplicitFinish(); + + // Before onload, XUL docs have no root frame. + is(document.elementFromPoint(10,10), null, + "Calls to elementFromPoint before onload should return null"); + + var d = 10; + function middle(e) { + return { "x": e.boxObject.x + e.boxObject.width/2, + "y": e.boxObject.y + e.boxObject.height/2 }; + } + function lower_right(e) { + return { "x": e.boxObject.x + e.boxObject.width - d, + "y": e.boxObject.y + e.boxObject.height - d }; + } + function upper_left(e) { + return { "x": e.boxObject.x + d, + "y": e.boxObject.y + d }; + } + function scrollbar_button(e) { // a bit down from upper right + return { "x": e.boxObject.x + e.boxObject.width - d, + "y": e.boxObject.y + d + 15 }; + } + + function test(ptFunc, id, message) { + var pt = ptFunc($(id)); + var e = document.elementFromPoint(pt.x, pt.y); + ok(e != null, message + " (returned null)"); + is(e.id, id, message); + } + + function do_test() { + // Avoid hardcoding x,y pixel values, to better deal with differing default + // font sizes or other layout defaults. + + test(middle, 'textbox', "Point within textbox should return textbox element"); + test(lower_right, 'textbox', "Point on textbox's scrollbar should return textbox element"); + test(scrollbar_button, 'textbox', "Point on textbox's scrollbar button should return textbox element"); + test(middle, 'non-anon-label', "Point on label should return label"); + test(upper_left, 'bound', "Point on XBL content should return element with -moz-binding style"); + + SimpleTest.finish(); + } + $("textbox").setAttribute("value", + "lorem ipsum dolor sit amet " + + "lorem ipsum dolor sit amet " + + "lorem ipsum dolor sit amet " + + "lorem ipsum dolor sit amet " + + "lorem ipsum dolor sit amet " + + "lorem ipsum dolor sit amet " + + "lorem ipsum dolor sit amet "); // force scrollbars to appear + addLoadEvent(do_test); +]]> + </script> +</pre> +</body> +</window> diff --git a/dom/xul/test/test_bug233643.xul b/dom/xul/test/test_bug233643.xul new file mode 100644 index 000000000..a1cf22bc8 --- /dev/null +++ b/dom/xul/test/test_bug233643.xul @@ -0,0 +1,76 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:html="http://www.w3.org/1999/xhtml" + title="Test for Bug 233643"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=233643 +--> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <!-- NOTE: This testcase depends on the patch for bug 366770 + (ability to apply bindings with data: URIs). --> + + <!-- The data URI: below corresponds to: + <?xml version="1.0"?> + <bindings xmlns="http://www.mozilla.org/xbl" + xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <binding id="label"> + <content> + <xul:label anonid="foo" style="display: none; color: green;" value="PASS"/> + </content> + <implementation> + <property name="fooDisplay"> + <setter>document.getAnonymousElementByAttribute(this, "anonid", "foo").style.display = val;</setter> + <getter>return document.getAnonymousElementByAttribute(this, "anonid", "foo").style.display;</getter> + </property> + </implementation> + </binding> + </bindings> + --> + <html:style type="text/css"> + foo { + -moz-binding: url(data:text/xml,%3C%3Fxml%20version%3D%221.0%22%3F%3E%0A%3Cbindings%20xmlns%3D%22http%3A//www.mozilla.org/xbl%22%0A%20%20%20%20%20%20%20%20%20%20xmlns%3Axul%3D%22http%3A//www.mozilla.org/keymaster/gatekeeper/there.is.only.xul%22%3E%0A%20%20%3Cbinding%20id%3D%22label%22%3E%0A%20%20%20%20%3Ccontent%3E%0A%20%20%20%20%20%20%3Cxul%3Alabel%20anonid%3D%22foo%22%20style%3D%22display%3A%20none%3B%20color%3A%20green%3B%22%20value%3D%22PASS%22/%3E%0A%20%20%20%20%3C/content%3E%0A%20%20%20%20%3Cimplementation%3E%0A%20%20%20%20%20%20%3Cproperty%20name%3D%22fooDisplay%22%3E%0A%20%20%20%20%20%20%20%20%3Csetter%3Edocument.getAnonymousElementByAttribute%28this%2C%20%22anonid%22%2C%20%22foo%22%29.style.display%20%3D%20val%3B%3C/setter%3E%0A%20%20%20%20%20%20%20%20%3Cgetter%3Ereturn%20document.getAnonymousElementByAttribute%28this%2C%20%22anonid%22%2C%20%22foo%22%29.style.display%3B%3C/getter%3E%0A%20%20%20%20%20%20%3C/property%3E%0A%20%20%20%20%3C/implementation%3E%0A%20%20%3C/binding%3E%0A%3C/bindings%3E); + } + </html:style> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=233643">Mozilla Bug 233643</a> + <p id="display"> + <foo xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"/> + <foo xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"/> + </p> + <div id="content" style="display: none"> + <script class="testbody" type="text/javascript"> + <![CDATA[ + SimpleTest.waitForExplicitFinish(); + + function test_233643() { + var foos = document.getElementsByTagName("foo"); + ok(foos.length == 2, "Found 2 <foo> elements"); + + // Make sure that the binding was applied successfully + ok(foos[0].fooDisplay !== undefined, "Binding applied"); + + // Show both both child elements by setting their display to "". + foos[0].fooDisplay = ""; + foos[1].fooDisplay = ""; + + // Hide the second one. In a build with bug 233643, this hides both. + foos[1].fooDisplay = "none"; + + // Check that changing the second didn't affect the first. + ok(foos[0].fooDisplay != foos[1].fooDisplay, "XUL Element have their own style rules"); + + SimpleTest.finish(); + } + + function do_test() { + setTimeout(test_233643, 0); + } + addLoadEvent(do_test); + ]]> + </script> + </div> + </body> +</window> diff --git a/dom/xul/test/test_bug236853.xul b/dom/xul/test/test_bug236853.xul new file mode 100644 index 000000000..9b79deb75 --- /dev/null +++ b/dom/xul/test/test_bug236853.xul @@ -0,0 +1,37 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> +<window + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:svg="http://www.w3.org/2000/svg"> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + <script type="application/javascript"> + <![CDATA[ + SimpleTest.waitForExplicitFinish(); + window.onload = function() { + ok(true, "Didn't crash"); + SimpleTest.finish(); + } + ]]> + </script> + <vbox flex="1"> + <svg:svg datasources="file_bug236853.rdf" ref="urn:root"> + <template> + <rule> + <conditions> + <content uri="?root"/> + <triple subject="?root" + predicate="http://www.ex.org/ex-rdf#nodes" + object="?nodes"/> + <member container="?nodes" child="?node"/> + </conditions> + <action> + <!-- The line below causes Mozilla to crash --> + <svg:text uri="?node" x="64" y="64">Text</svg:text> + </action> + </rule> + </template> + <!--<svg:text x="64" y="64">Text</svg:text>--> + </svg:svg> + </vbox> +</window> diff --git a/dom/xul/test/test_bug311681.xul b/dom/xul/test/test_bug311681.xul new file mode 100644 index 000000000..2774d8731 --- /dev/null +++ b/dom/xul/test/test_bug311681.xul @@ -0,0 +1,106 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=311681 +--> +<window title="Mozilla Bug 311681" + xmlns:html="http://www.w3.org/1999/xhtml" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + +<body xmlns="http://www.w3.org/1999/xhtml"> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=311681">Mozilla Bug 311681</a> +<script class="testbody" type="text/javascript"> +<![CDATA[ + // Setup script + SimpleTest.waitForExplicitFinish(); + + // Make sure to trigger the hashtable case by asking for enough elements + // by ID. + for (var i = 0; i < 256; ++i) { + var x = document.getElementById(i); + } + + // save off the document.getElementById function, since getting it as a + // property off the document it causes a content flush. + var fun = document.getElementById; + + // Slot for our initial element with id "content" + var testNode; + + function getCont() { + return fun.call(document, "content"); + } + + function testClone() { + // Test to make sure that if we have multiple nodes with the same ID in + // a document we don't forget about one of them when the other is + // removed. + var newParent = $("display"); + var node = testNode.cloneNode(true); + isnot(node, testNode, "Clone should be a different node"); + + newParent.appendChild(node); + + // Check what getElementById returns, no flushing + is(getCont(), node, "Should be getting new node pre-flush 1") + + // Trigger a layout flush, just in case. + var itemHeight = newParent.offsetHeight/10; + + // Check what getElementById returns now. + is(getCont(), node, "Should be getting new node post-flush 1") + + clear(newParent); + + // Check what getElementById returns, no flushing + is(getCont(), testNode, "Should be getting orig node pre-flush 2"); + + // Trigger a layout flush, just in case. + var itemHeight = newParent.offsetHeight/10; + + // Check what getElementById returns now. + is(getCont(), testNode, "Should be getting orig node post-flush 2"); + + node = testNode.cloneNode(true); + newParent.appendChild(node); + testNode.parentNode.removeChild(testNode); + + // Check what getElementById returns, no flushing + is(getCont(), node, "Should be getting clone pre-flush"); + + // Trigger a layout flush, just in case. + var itemHeight = newParent.offsetHeight/10; + + // Check what getElementById returns now. + is(getCont(), node, "Should be getting clone post-flush"); + + } + + function clear(node) { + while (node.hasChildNodes()) { + node.removeChild(node.firstChild); + } + } + + addLoadEvent(testClone); + addLoadEvent(SimpleTest.finish); +]]> +</script> +<p id="display"></p> +<div id="content" style="display: none"> + <script class="testbody" type="text/javascript"> + <![CDATA[ + testNode = fun.call(document, "content"); + // Needs incremental XML parser + isnot(testNode, null, "Should have node here"); + ]]> + </script> +</div> +<pre id="test"> +</pre> +</body> + +</window> diff --git a/dom/xul/test/test_bug335375.xul b/dom/xul/test/test_bug335375.xul new file mode 100644 index 000000000..20b8dd1d2 --- /dev/null +++ b/dom/xul/test/test_bug335375.xul @@ -0,0 +1,50 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> +<?xul-overlay href="overlay1_bug335375.xul"?> +<?xul-overlay href="overlay2_bug335375.xul"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=335375 +--> +<window title="Mozilla Bug 335375" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + onload="RunTest();"> + + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <script type="application/javascript"> + <![CDATA[ + SimpleTest.waitForExplicitFinish(); + + function RunTest() + { + var cmd = document.getElementById("cmd_test"); + var button = document.getElementById("button_test"); + + is(cmd.getAttribute("accesskey"), "C", + "checking command has an accesskey"); + is(button.getAttribute("accesskey"), cmd.getAttribute("accesskey"), + "checking command and button have the same accesskey"); + + cmd.setAttribute("accesskey", "D"); + is(button.getAttribute("accesskey"), "D", + "checking button has inherited new accesskey from command"); + + SimpleTest.finish(); + } + ]]> + </script> + + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=335375" + target="_blank">Mozilla Bug 335375</a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"></pre> + </body> + + <commandset id="test_commandset"/> + <toolbarbutton id="button_test"/> + +</window> diff --git a/dom/xul/test/test_bug391002.xul b/dom/xul/test/test_bug391002.xul new file mode 100644 index 000000000..9387caeaf --- /dev/null +++ b/dom/xul/test/test_bug391002.xul @@ -0,0 +1,41 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=391002 +--> +<window title="Mozilla Bug 391002" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=391002" + target="_blank">Mozilla Bug 391002</a> + </body> + + <button id="btn1" command="cmd1"/> + + <button id="btn2"> + <observes id="observes" element="cmd1" attribute="label"/> + </button> + + <commandset> + <command id="cmd1" label="cmd1"/> + <command id="cmd2" label="cmd2"/> + </commandset> + + <!-- test code goes here --> + <script type="application/javascript"><![CDATA[ + + /** Test for Bug 391002 **/ + + $("btn1").setAttribute("command", "cmd2"); + is($("btn1").getAttribute("label"), $("cmd2").getAttribute("label")) + + $("observes").setAttribute("element", "cmd2"); + is($("btn2").getAttribute("label"), $("cmd2").getAttribute("label")) + + ]]></script> +</window> + diff --git a/dom/xul/test/test_bug398289.html b/dom/xul/test/test_bug398289.html new file mode 100644 index 000000000..4fc36c923 --- /dev/null +++ b/dom/xul/test/test_bug398289.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<html style="height: 100%"> +<head> + <title>Test for bug 398289</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" /> +</head> +<body style="height: 100%" onload="setTimeout(onBodyLoad, 0);"> + <iframe id="test" src="398289-resource.xul" width="100%" height="100%"> + </iframe> + + <script class="testbody" type="text/javascript"> + var snap1, snap2; + + SimpleTest.waitForExplicitFinish(); + + function onBodyLoad() { + window.frames[0].document.getElementById("test").selectedIndex = 0; + window.frames[0].document.getElementById("test").selectedIndex = 1; + + snap1 = snapshotWindow(window); + + document.getElementById("test").onload = onFrameLoad; + window.frames[0].location.reload(); + } + + function onFrameLoad() { + snap2 = snapshotWindow(window); + + var equal, str1, str2; + [equal, str1, str2] = compareSnapshots(snap1, snap2, true); + + ok(equal, "persistent attribute in tab box broken, expected: "+str1+" got: "+str2); + + SimpleTest.finish(); + } + </script> +</body> +</html> diff --git a/dom/xul/test/test_bug403868.xul b/dom/xul/test/test_bug403868.xul new file mode 100644 index 000000000..e82c1f62b --- /dev/null +++ b/dom/xul/test/test_bug403868.xul @@ -0,0 +1,83 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=403868 +--> +<window title="Mozilla Bug 403868" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=403868" + target="_blank">Mozilla Bug 403868</a> + <div id="content" style="display: none"/> + </body> + + <!-- test code goes here --> + <script type="application/javascript"><![CDATA[ + + /** Test for Bug 403868 **/ +function createSpan(id, insertionPoint) { + var s = document.createElementNS("http://www.w3.org/1999/xhtml", "span"); + s.id = id; + $("content").insertBefore(s, insertionPoint); + return s; +} + +var s1a = createSpan("test1", null); +is(document.getElementById("test1"), s1a, + "Only one span with id=test1 in the tree; should work!"); + +var s2a = createSpan("test1", null); +is(document.getElementById("test1"), s1a, + "Appending span with id=test1 doesn't change which one comes first"); + +var s3a = createSpan("test1", s2a); +is(document.getElementById("test1"), s1a, + "Inserting span with id=test1 not at the beginning; doesn't matter"); + +var s4a = createSpan("test1", s1a); +is(document.getElementById("test1"), s4a, + "Inserting span with id=test1 at the beginning changes which one is first"); + +s4a.parentNode.removeChild(s4a); +is(document.getElementById("test1"), s1a, + "First-created span with id=test1 is first again"); + +s1a.parentNode.removeChild(s1a); +is(document.getElementById("test1"), s3a, + "Third-created span with id=test1 is first now"); + +// Start the id hashtable +for (var i = 0; i < 256; ++i) { + document.getElementById("no-such-id-in-the-document" + i); +} + +var s1b = createSpan("test2", null); +is(document.getElementById("test2"), s1b, + "Only one span with id=test2 in the tree; should work!"); + +var s2b = createSpan("test2", null); +is(document.getElementById("test2"), s1b, + "Appending span with id=test2 doesn't change which one comes first"); + +var s3b = createSpan("test2", s2b); +is(document.getElementById("test2"), s1b, + "Inserting span with id=test2 not at the beginning; doesn't matter"); + +var s4b = createSpan("test2", s1b); +is(document.getElementById("test2"), s4b, + "Inserting span with id=test2 at the beginning changes which one is first"); + +s4b.parentNode.removeChild(s4b); +is(document.getElementById("test2"), s1b, + "First-created span with id=test2 is first again"); + +s1b.parentNode.removeChild(s1b); +is(document.getElementById("test2"), s3b, + "Third-created span with id=test2 is first now"); + + ]]></script> +</window> diff --git a/dom/xul/test/test_bug414907.xul b/dom/xul/test/test_bug414907.xul new file mode 100644 index 000000000..cc4ecedeb --- /dev/null +++ b/dom/xul/test/test_bug414907.xul @@ -0,0 +1,53 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=414907 +--> +<window title="Mozilla Bug 414907" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <vbox hidden="true"> + <bindings xmlns="http://www.mozilla.org/xbl" style="display: block;" + xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <binding id="anon"> + <implementation> + <constructor> + <![CDATA[ + var node = this.firstChild; + this.palette = node; + this.removeChild(node); + ]]> + </constructor> + </implementation> + </binding> + </bindings> + </vbox> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=414907" + target="_blank">Mozilla Bug 414907</a> + </body> + + <!-- test code goes here --> + <script type="application/javascript"><![CDATA[ + + SimpleTest.waitForExplicitFinish(); + window.onload = function onload() { + is($("box").palette.getAttribute("label"), $("command").getAttribute("label")); + SimpleTest.finish(); + } + + ]]></script> + + <command id="command" label="label"/> + + <box id="box" style="-moz-binding:url('#anon');"> + <button observes="command"/> + </box> + + + +</window> diff --git a/dom/xul/test/test_bug418216.xul b/dom/xul/test/test_bug418216.xul new file mode 100644 index 000000000..e1e42ce11 --- /dev/null +++ b/dom/xul/test/test_bug418216.xul @@ -0,0 +1,46 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=418216 +--> +<window title="Mozilla Bug 418216" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=418216" + target="_blank">Mozilla Bug 418216</a> + </body> + + <!-- test code goes here --> + <script type="application/javascript"><![CDATA[ + + /** Test for Bug 418216 **/ + SimpleTest.waitForExplicitFinish(); + window.onload = function onload() { + var element = $("item"); + $("container").removeChild($("item")); + $("broadcaster").removeAttribute("disabled"); + $("container").appendChild(element); + is($("item").hasAttribute("disabled"), $("broadcaster").hasAttribute("disabled")); + SimpleTest.finish(); + } + + + + + ]]></script> + +<box id="container"> +<textbox id="item"> + <observes element="broadcaster" attribute="disabled"/> +</textbox> +</box> + +<broadcasterset> + <broadcaster id="broadcaster" disabled="true"/> +</broadcasterset> + +</window> diff --git a/dom/xul/test/test_bug445177.xul b/dom/xul/test/test_bug445177.xul new file mode 100644 index 000000000..ef89b7d87 --- /dev/null +++ b/dom/xul/test/test_bug445177.xul @@ -0,0 +1,85 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=445177 +--> +<window title="Test for Bug 445177" + id="test_bug445177_xul" + xmlns:html="http://www.w3.org/1999/xhtml" + xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + +<body id="body" xmlns="http://www.w3.org/1999/xhtml"> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=445177">Mozilla Bug 445177</a> + + +<hbox id="b1" value="foo"/> +<hbox id="o1" observes="b1"/> + +<pre id="test"> + <script class="testbody" type="text/javascript"> +<![CDATA[ + SimpleTest.waitForExplicitFinish(); + function do_test() { + var b1 = document.getElementById("b1"); + var o1 = document.getElementById("o1"); + + is(o1.getAttribute("value"), b1.getAttribute("value"), "Wrong value (1)"); + + b1.setAttribute("value", "bar"); + is(o1.getAttribute("value"), b1.getAttribute("value"), "Wrong value (2)"); + + b1.removeAttribute("value"); + is(o1.hasAttribute("value"), b1.hasAttribute("value"), "Wrong value (3)"); + + o1.setAttribute("value", "foo"); + isnot(o1.getAttribute("value"), b1.getAttribute("value"), "Wrong value (4)"); + + b1.setAttribute("value", "foobar"); + is(o1.getAttribute("value"), b1.getAttribute("value"), "Wrong value (5)"); + + //After removing listener, changes to broadcaster shouldn't have any effect. + o1.parentNode.removeChild(o1); + b1.setAttribute("value", "foo"); + isnot(o1.getAttribute("value"), b1.getAttribute("value"), "Wrong value (6)"); + + b1.parentNode.appendChild(o1); + is(o1.getAttribute("value"), b1.getAttribute("value"), "Wrong value (7)"); + + o1.parentNode.removeChild(o1); + o1.removeAttribute("observes"); + o1.removeAttribute("value"); + b1.parentNode.appendChild(o1); + isnot(o1.getAttribute("value"), b1.getAttribute("value"), "Wrong value (8)"); + + document.addBroadcastListenerFor(b1, o1, "value"); + is(o1.getAttribute("value"), b1.getAttribute("value"), "Wrong value (9)"); + + o1.parentNode.removeChild(o1); + b1.setAttribute("value", "foobar"); + is(o1.getAttribute("value"), b1.getAttribute("value"), "Wrong value (10)"); + + b1.parentNode.appendChild(o1); + is(o1.getAttribute("value"), b1.getAttribute("value"), "Wrong value (11)"); + + o1.setAttribute("observes", "b1"); + is(o1.getAttribute("value"), b1.getAttribute("value"), "Wrong value (12)"); + + // When broadcaster isn't in document, changes to its attributes aren't + // reflected to listener. + b1.parentNode.removeChild(b1); + b1.setAttribute("value", "foo"); + isnot(o1.getAttribute("value"), b1.getAttribute("value"), "Wrong value (13)"); + + SimpleTest.finish(); + } + + addLoadEvent(do_test); +]]> + </script> +</pre> +</body> +</window> diff --git a/dom/xul/test/test_bug449457.xul b/dom/xul/test/test_bug449457.xul new file mode 100644 index 000000000..0401725f7 --- /dev/null +++ b/dom/xul/test/test_bug449457.xul @@ -0,0 +1,25 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=449457 +--> +<window title="Mozilla Bug 449457" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=449457" + target="_blank">Mozilla Bug 449457</a> + </body> + + <!-- test code goes here --> + <script type="application/javascript"><![CDATA[ + + /** Test for Bug 449457 **/ + document.popupNode = document; + ok(true, "This is just a leak test"); + + ]]></script> +</window> diff --git a/dom/xul/test/test_bug468176.xul b/dom/xul/test/test_bug468176.xul new file mode 100644 index 000000000..1f3312266 --- /dev/null +++ b/dom/xul/test/test_bug468176.xul @@ -0,0 +1,84 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=468176 +--> +<window title="Test for Bug 468176" + id="test_bug468176_xul" + xmlns:html="http://www.w3.org/1999/xhtml" + xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + +<body id="body" xmlns="http://www.w3.org/1999/xhtml"> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=468176">Mozilla Bug 468176</a> + +<xul:hbox id="b1" value="foo"/> + +<xul:hbox id="o1"> + <xul:observes id="inner" element="b1" attribute="*"/> +</xul:hbox> + +<pre id="test"> + <script class="testbody" type="text/javascript"> +<![CDATA[ + SimpleTest.waitForExplicitFinish(); + + var broadcastCount = 0; + function b_listener(evt) { + ++broadcastCount; + } + + function do_test() { + var b1 = document.getElementById("b1"); + var o1 = document.getElementById("o1"); + var inner = document.getElementById("inner"); + is(o1.getAttribute("value"), b1.getAttribute("value"), "Wrong value (1)"); + + inner.addEventListener("broadcast", b_listener, true); + b1.setAttribute("value", "bar"); + is(o1.getAttribute("value"), b1.getAttribute("value"), "Wrong value (2)"); + is(broadcastCount, 1, "Wrong value (3)"); + + b1.removeAttribute("value"); + is(o1.hasAttribute("value"), b1.hasAttribute("value"), "Wrong value (4)"); + is(broadcastCount, 2, "Wrong value (5)"); + + o1.setAttribute("value", "foo"); + isnot(o1.getAttribute("value"), b1.getAttribute("value"), "Wrong value (6)"); + is(broadcastCount, 2, "Wrong value (7)"); + + b1.setAttribute("value", "foobar"); + is(o1.getAttribute("value"), b1.getAttribute("value"), "Wrong value (8)"); + is(broadcastCount, 3, "Wrong value (9)"); + + b1.removeAttribute("value"); + is(o1.getAttribute("value"), b1.getAttribute("value"), "Wrong value (10)"); + is(broadcastCount, 4, "Wrong value (11)"); + + b1.removeAttribute("value"); + is(o1.getAttribute("value"), b1.getAttribute("value"), "Wrong value (12)"); + is(broadcastCount, 4, "Wrong value (13)"); + + o1.setAttribute("value", "bar"); + b1.setAttribute("value", "bar"); // This should still dispatch 'broadcast' + is(o1.getAttribute("value"), b1.getAttribute("value"), "Wrong value (14)"); + is(broadcastCount, 5, "Wrong value (15)"); + + //After removing listener, changes to broadcaster shouldn't have any effect. + o1.parentNode.removeChild(o1); + b1.setAttribute("value", "foo"); + isnot(o1.getAttribute("value"), b1.getAttribute("value"), "Wrong value (16)"); + is(broadcastCount, 5, "Wrong value (17)"); + + SimpleTest.finish(); + } + + addLoadEvent(do_test); +]]> + </script> +</pre> +</body> +</window> diff --git a/dom/xul/test/test_bug486990.xul b/dom/xul/test/test_bug486990.xul new file mode 100644 index 000000000..fb375a4cc --- /dev/null +++ b/dom/xul/test/test_bug486990.xul @@ -0,0 +1,155 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=486990 +--> +<window title="Mozilla Bug 486990" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + onload="setTimeout(runTests, 0);"> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"/> + <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=486990" + target="_blank">Mozilla Bug 486990</a> + + </body> + <div xmlns="http://www.w3.org/1999/xhtml"> + <select size="5" id="select"> + <option>1</option> + <option>2</option> + <option>3</option> + <option>4</option> + <option>5</option> + <option>6</option> + <option>7</option> + <option>8</option> + <option>9</option> + <option>10</option> + </select> + </div> + <menupopup id="cm" onpopupshowing="popupShowing(event);"> + <menuitem label="Mozilla" value="http://mozilla.org"/> + <menuitem label="Slashdot" value="http://slashdot.org"/> + <menuitem label="Sourceforge" value="http://sf.net"/> + <menuitem label="Freshmeat" value="http://freshmeat.net"/> + </menupopup> + <button label="test button" contextmenu="cm" id="testbutton"/> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + /** Test for Bug 486990 **/ + + SimpleTest.waitForExplicitFinish(); + + var prevented = false; + var eventCount = 0; + + function fooListener(evt) { + evt.preventDefault(); + prevented = evt.defaultPrevented; + ++eventCount; + }; + + var clickCount = 0; + var mouseDownCount = 0; + var mouseUpCount = 0; + function clickListener(evt) { + ++clickCount; + } + + function mouseDownListener(evt) { + ++mouseDownCount; + } + + function mouseUpListener(evt) { + ++mouseUpCount; + } + + var popupshowingcount = 0; + + function popupShowing(evt) { + ++popupshowingcount; + evt.preventDefault(); + } + + function contextMenuStopper(evt) { + evt.stopPropagation(); + } + + function contextMenuPreventer(evt) { + evt.preventDefault(); + } + + var tb; + function runTests() { + document.addEventListener("foo", fooListener, true); + var e1 = document.createEvent("Event"); + e1.initEvent("foo", true, true); + document.dispatchEvent(e1); + is(eventCount, 1, "Wrong event count"); + ok(prevented, "Default handling should have been prevented."); + + prevented = false; + var e2 = document.createEvent("Event"); + e2.initEvent("foo", false, false); + document.dispatchEvent(e1); + is(eventCount, 2, "Wrong event count"); + ok(prevented, "Default handling should have been prevented."); + + tb = document.getElementById("testbutton"); + dispatchTrustedContextMenuEvent(tb); + is(popupshowingcount, 1, "Should have got 'popupShowing' event!"); + + tb.addEventListener("contextmenu", contextMenuStopper, true); + dispatchTrustedContextMenuEvent(tb); + is(popupshowingcount, 2, "Should have got 'popupShowing' event!"); + + tb.addEventListener("contextmenu", contextMenuPreventer, true); + dispatchTrustedContextMenuEvent(tb); + is(popupshowingcount, 2, "Should not have got 'popupShowing' event!"); + + SpecialPowers.pushPrefEnv({"set": [["dom.event.contextmenu.enabled", false]]}, test2); + } + + function test2() { + dispatchTrustedContextMenuEvent(tb); + is(popupshowingcount, 3, "Should have got 'popupShowing' event!"); + + SpecialPowers.pushPrefEnv({"set": [["dom.event.contextmenu.enabled", true]]}, test3); + } + + function test3() { + dispatchTrustedContextMenuEvent(tb); + is(popupshowingcount, 3, "Should not have got 'popupshowing' event!"); + + var s = document.getElementById("select"); + s.addEventListener("click", clickListener, true); + s.addEventListener("mousedown", mouseDownListener, true); + s.addEventListener("mouseup", mouseUpListener, true); + + synthesizeMouse(s, 1, 10, {}, window); + is(clickCount, 1, "Should have got click event!"); + is(mouseDownCount, 1, "Should have got mousedown event!"); + is(mouseUpCount, 1, "Should have got mouseup event!"); + + // Dispatch to scrollbar. + synthesizeMouse(s, s.getBoundingClientRect().right - 3, 10, {}, window); + is(clickCount, 1, "Should not have got click event!"); + is(mouseDownCount, 2, "Should have got mousedown event!"); + is(mouseUpCount, 2, "Should have got mouseup event!"); + + SimpleTest.finish(); + } + + function dispatchTrustedContextMenuEvent(target) { + return sendMouseEvent({type:"contextmenu"}, target, window); + } + + ]]> + </script> +</window> diff --git a/dom/xul/test/test_bug497875.xul b/dom/xul/test/test_bug497875.xul new file mode 100644 index 000000000..a083daa53 --- /dev/null +++ b/dom/xul/test/test_bug497875.xul @@ -0,0 +1,53 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=497875 +--> +<window title="Mozilla Bug 497875" + xmlns:html="http://www.w3.org/1999/xhtml" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + +<body xmlns="http://www.w3.org/1999/xhtml"> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=497875">Mozilla Bug 497875</a> +<p id="display"><iframe id="iframe" src="bug497875-iframe.xul"></iframe></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> + +<script class="testbody" type="application/javascript"> +<![CDATA[ + +/** Test for Bug 497875 **/ + +SimpleTest.waitForExplicitFinish(); + +function done_test() { + var f = document.getElementById('iframe') + f.src="about:blank" + f.parentNode.removeChild(f) + ok(true, "this is a crashtest"); + SimpleTest.finish(); +} + +function do_test() { + setTimeout(function () {document.getElementById('iframe').contentWindow.location.reload()}, 500); + setTimeout(function () {document.getElementById('iframe').contentWindow.location.reload()}, 1000); + setTimeout(function () {document.getElementById('iframe').contentWindow.location.reload()}, 1500); + setTimeout(done_test, 2000); +} + +do_test(); + + +]]> +</script> + +</window> diff --git a/dom/xul/test/test_bug583948.xul b/dom/xul/test/test_bug583948.xul new file mode 100644 index 000000000..c0d6ebd49 --- /dev/null +++ b/dom/xul/test/test_bug583948.xul @@ -0,0 +1,38 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + +<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + +<body xmlns="http://www.w3.org/1999/xhtml"> + <div id="content" style="display: none"/> +</body> + +<script> +SimpleTest.waitForExplicitFinish(); + +var attempts = 0; + +function update() { + // without the crash fix, this usually crashes after 2 to 4 reloads + if (++attempts == 6) { + ok(true, "didn't crash after 6 attempts"); + otherWindow.close(); + SimpleTest.waitForFocus(function() { + SimpleTest.finish(); + }); + } else { + otherWindow.document.commandDispatcher.updateCommands(''); + setTimeout(function() { + otherWindow.location.reload() + }, 0); + } +} + +var otherWindow = window.open("window_bug583948.xul", "_new", "chrome"); +</script> + +</window> diff --git a/dom/xul/test/test_bug640158_overlay_persist.xul b/dom/xul/test/test_bug640158_overlay_persist.xul new file mode 100644 index 000000000..ad4817331 --- /dev/null +++ b/dom/xul/test/test_bug640158_overlay_persist.xul @@ -0,0 +1,51 @@ +<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=640158
+-->
+<window title="Mozilla Bug 640158" id="rootwin"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=640158"
+ target="_blank">Mozilla Bug 640158</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+
+ SimpleTest.waitForExplicitFinish();
+ window.onload = function onload() {
+ is($("foo").getAttribute("testattr"), "original", "Attribute should be in original state");
+ // Change and persist another value:
+ $("foo").setAttribute("testattr", "changed");
+ document.persist("foo", "testattr");
+ $("foo").setAttribute("testattr", "original");
+
+ // Hacky times: check that items which are overlaid do get persisted into correctly,
+ // by first creating an extra element and persisting the value before loading an
+ // overlay that changes that value - the persisted value should be reinstated.
+ let root = document.documentElement;
+ let bar = document.createElement("box");
+ bar.id = "bar";
+ bar.setAttribute("testattr", "changed"); // The overlay we load has 'original'
+ root.appendChild(bar);
+ document.persist("bar", "testattr");
+ document.loadOverlay(location.href.replace(/[^\\\/]*.xul/, "overlay_bug640158.xul"), function() {
+ is($("foo").getAttribute("testattr"), "original",
+ "Non-overlaid attribute should still be in original state");
+ is($("bar").getAttribute("testattr"), "changed",
+ "Overlaid attribute should have been changed.");
+ SimpleTest.finish();
+ });
+ }
+
+ ]]></script>
+
+ <box id="foo" testattr="original"/>
+
+</window>
+
diff --git a/dom/xul/test/test_bug749367.xul b/dom/xul/test/test_bug749367.xul new file mode 100644 index 000000000..478a119e8 --- /dev/null +++ b/dom/xul/test/test_bug749367.xul @@ -0,0 +1,39 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=749367 +--> +<window title="Mozilla Bug 749367" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + onload="setTimeout(runTests, 0);"> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=486990" + target="_blank">Mozilla Bug 486990</a> + </body> + + <!-- test code goes here --> + <script type="text/template"> + <![CDATA[ + SimpleTest.waitForExplicitFinish(); + function runTests() { + ok(false, "Shouldn't execute"); + SimpleTest.finish(); + } + ]]> + </script> + + <script type="text/javascript"> + <![CDATA[ + SimpleTest.waitForExplicitFinish(); + function runTests() { + ok(true, "Should execute"); + SimpleTest.finish(); + } + ]]> + </script> + +</window> diff --git a/dom/xul/test/test_bug757137.xul b/dom/xul/test/test_bug757137.xul new file mode 100644 index 000000000..e44a160b3 --- /dev/null +++ b/dom/xul/test/test_bug757137.xul @@ -0,0 +1,52 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + +<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + +<body xmlns="http://www.w3.org/1999/xhtml"> + <div id="content" style="display: none"/> +</body> + +<script> +var Ci = Components.interfaces; +var chromeWindowInterface = Ci.nsIDOMChromeWindow; + +SimpleTest.waitForExplicitFinish(); + +// Force off out-of-process mozbrowser because we need to grab its +// |window| synchronously from here. Out-of-process docshell creation +// for mozbrowser haves entirely differently. +// XXX why doesn't pushPrefEnv() work here? +var otherWindow = window.open("window_bug757137.xul", "", "chrome"); +ok(chromeWindowInterface !== null, 'nsIDOMChromeWindow interface is defined'); +var otherChromeWindow = null; +try { + otherChromeWindow = otherWindow.QueryInterface(chromeWindowInterface); +} catch(e) { + ok(false, 'exception when QI to ChromeWindow'); +} +ok(otherChromeWindow !== null, 'XUL window should QI to ChromeWindow'); + +SpecialPowers.pushPrefEnv({"set":[["dom.ipc.tabs.disabled", true]]}, startTest); + +function startTest() { + otherWindow.onload = function () { + var w = otherWindow.document.getElementById('f').contentWindow; + ok(w !== null, 'got the |window| for a mozbrowser iframe'); + var chromeWindow = null; + try { + var chromeWindow = w.QueryInterface(chromeWindowInterface); + } catch(e) { } + ok(chromeWindow === null, 'mozbrowser iframe should not get ChromeWindow'); + + otherWindow.close(); + SimpleTest.waitForFocus(SimpleTest.finish); + }; +} +</script> + +</window> diff --git a/dom/xul/test/test_bug775972.xul b/dom/xul/test/test_bug775972.xul new file mode 100644 index 000000000..eeb7ebdac --- /dev/null +++ b/dom/xul/test/test_bug775972.xul @@ -0,0 +1,35 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=775972 +--> +<window title="Mozilla Bug 775972" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + onload="test()"> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=775972" + target="_blank">Mozilla Bug 775972</a> + </body> + + <hbox id="container"><label value="test" id=""/></hbox> + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + /** Test for Bug 775972 **/ + + function test() { + var c = document.getElementById("container"); + var clone = c.cloneNode(true); + document.documentElement.appendChild(clone); + ok(true, "This shouldn't crash!"); + } + + + ]]> + </script> +</window> diff --git a/dom/xul/test/test_import_xul_to_content.xul b/dom/xul/test/test_import_xul_to_content.xul new file mode 100644 index 000000000..a0b26fa96 --- /dev/null +++ b/dom/xul/test/test_import_xul_to_content.xul @@ -0,0 +1,69 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> +<window title="Mozilla Importing XUL into Content" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1027299" + target="_blank">Mozilla Bug 1027299</a> + </body> + + <browser id="browserelt" src="about:blank" type="content"/> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + Components.utils.import("resource://gre/modules/Services.jsm"); + + SimpleTest.waitForExplicitFinish(); + + function expectWarning(expected, when, f) { + Services.console.reset(); + + f(); + + var sawWarning = false; + var msgs = Services.console.getMessageArray(); + for (var i = 0; i < msgs.length; i++) { + var msg = msgs[i]; + if (!msg || !(msg instanceof Components.interfaces.nsIScriptError)) { + continue; + } + + if (msg.category.includes("DOM") && msg.errorMessage.includes("Importing XUL")) { + sawWarning = true; + } + } + + ok(sawWarning == expected, "correct warning condition when " + when); + } + + var browser = document.getElementById("browserelt"); + browser.addEventListener("load", function() { + var doc = browser.contentDocument; + + // We add a <video> element, which contains anonymous XUL content. This should not warn. + var video = doc.createElement("video"); + expectWarning(false, "appending video", function() { + doc.documentElement.appendChild(video); + // Force a layout flush to make sure the anonymous content is added. + let dummy = doc.documentElement.offsetLeft; + }); + + // We add some XUL to a content document. This should generate a warning. + var elt = document.createElement("label"); + var newElt = doc.importNode(elt, false); + expectWarning(true, "appending XUL", function() { + doc.documentElement.appendChild(newElt); + }); + + SimpleTest.finish(); + }); + + ]]> + </script> +</window> diff --git a/dom/xul/test/window_bug583948.xul b/dom/xul/test/window_bug583948.xul new file mode 100644 index 000000000..d0f6a2692 --- /dev/null +++ b/dom/xul/test/window_bug583948.xul @@ -0,0 +1,8 @@ +<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?> +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" onload="opener.update()"> + +<command oncommandupdate="document.removeChild(document.documentElement)" commandupdater="true"/> +<box command="c"/> +<iframe/> + +</window> diff --git a/dom/xul/test/window_bug757137.xul b/dom/xul/test/window_bug757137.xul new file mode 100644 index 000000000..16f38ef20 --- /dev/null +++ b/dom/xul/test/window_bug757137.xul @@ -0,0 +1,6 @@ +<?xml version="1.0"?> +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:html="http://www.w3.org/1999/xhtml"> + <html:iframe id="f" mozbrowser="true" + src="data:text/html;charset=utf-8,%3C!DOCTYPE html>Hi" /> +</window> |