diff --git a/accessible/src/base/AccIterator.cpp b/accessible/src/base/AccIterator.cpp index 1629f20d9a56..23ace181c87e 100644 --- a/accessible/src/base/AccIterator.cpp +++ b/accessible/src/base/AccIterator.cpp @@ -37,10 +37,12 @@ #include "AccIterator.h" +#include "nsAccessibilityService.h" #include "nsAccessible.h" //////////////////////////////////////////////////////////////////////////////// -// nsAccIterator +// AccIterator +//////////////////////////////////////////////////////////////////////////////// AccIterator::AccIterator(nsAccessible *aAccessible, filters::FilterFuncPtr aFilterFunc, @@ -93,3 +95,45 @@ AccIterator::IteratorState::IteratorState(nsAccessible *aParent, mParent(aParent), mIndex(0), mParentState(mParentState) { } + + +//////////////////////////////////////////////////////////////////////////////// +// RelatedAccIterator +//////////////////////////////////////////////////////////////////////////////// + +RelatedAccIterator:: + RelatedAccIterator(nsDocAccessible* aDocument, nsIContent* aDependentContent, + nsIAtom* aRelAttr) : + mRelAttr(aRelAttr), mProviders(nsnull), mBindingParent(nsnull), mIndex(0) +{ + mBindingParent = aDependentContent->GetBindingParent(); + nsIAtom* IDAttr = mBindingParent ? + nsAccessibilityAtoms::anonid : aDependentContent->GetIDAttributeName(); + + nsAutoString id; + if (aDependentContent->GetAttr(kNameSpaceID_None, IDAttr, id)) + mProviders = aDocument->mDependentIDsHash.Get(id); +} + +nsAccessible* +RelatedAccIterator::Next() +{ + if (!mProviders) + return nsnull; + + while (mIndex < mProviders->Length()) { + nsDocAccessible::AttrRelProvider* provider = (*mProviders)[mIndex++]; + + // Return related accessible for the given attribute and if the provider + // content is in the same binding in the case of XBL usage. + if (provider->mRelAttr == mRelAttr && + (!mBindingParent || + mBindingParent == provider->mContent->GetBindingParent())) { + nsAccessible* related = GetAccService()->GetAccessible(provider->mContent); + if (related) + return related; + } + } + + return nsnull; +} diff --git a/accessible/src/base/AccIterator.h b/accessible/src/base/AccIterator.h index 6c07b62f7e67..a78aefd7da92 100644 --- a/accessible/src/base/AccIterator.h +++ b/accessible/src/base/AccIterator.h @@ -40,6 +40,7 @@ #include "filters.h" #include "nscore.h" +#include "nsDocAccessible.h" /** * Allows to iterate through accessible children or subtree complying with @@ -93,4 +94,41 @@ private: IteratorState *mState; }; + +/** + * Allows to traverse through related accessibles that are pointing to the given + * dependent accessible by relation attribute. + */ +class RelatedAccIterator +{ +public: + /** + * Constructor. + * + * @param aDocument [in] the document accessible the related + * & accessibles belong to. + * @param aDependentContent [in] the content of dependent accessible that + * relations were requested for + * @param aRelAttr [in] relation attribute that relations are + * pointed by + */ + RelatedAccIterator(nsDocAccessible* aDocument, nsIContent* aDependentContent, + nsIAtom* aRelAttr); + + /** + * Return next related accessible for the given dependent accessible. + */ + nsAccessible* Next(); + +private: + RelatedAccIterator(); + RelatedAccIterator(const RelatedAccIterator&); + RelatedAccIterator& operator = (const RelatedAccIterator&); + + nsIAtom* mRelAttr; + nsDocAccessible::AttrRelProviderArray* mProviders; + nsIContent* mBindingParent; + PRUint32 mIndex; +}; + #endif diff --git a/accessible/src/base/nsAccessibilityService.cpp b/accessible/src/base/nsAccessibilityService.cpp index 1cc59b595dbd..65b9a62e2474 100644 --- a/accessible/src/base/nsAccessibilityService.cpp +++ b/accessible/src/base/nsAccessibilityService.cpp @@ -844,15 +844,11 @@ static PRBool HasRelatedContent(nsIContent *aContent) return PR_FALSE; } - nsIAtom *relationAttrs[] = {nsAccessibilityAtoms::aria_labelledby, - nsAccessibilityAtoms::aria_describedby, - nsAccessibilityAtoms::aria_owns, - nsAccessibilityAtoms::aria_controls, - nsAccessibilityAtoms::aria_flowto}; - if (nsCoreUtils::FindNeighbourPointingToNode(aContent, relationAttrs, - NS_ARRAY_LENGTH(relationAttrs))) { + // If the given ID is referred by relation attribute then create an accessible + // for it. Take care of HTML elements only for now. + if (aContent->IsHTML() && + nsAccUtils::GetDocAccessibleFor(aContent)->IsDependentID(id)) return PR_TRUE; - } nsIContent *ancestorContent = aContent; while ((ancestorContent = ancestorContent->GetParent()) != nsnull) { diff --git a/accessible/src/base/nsAccessible.cpp b/accessible/src/base/nsAccessible.cpp index e0e0753bdb2f..37c6b25d8007 100644 --- a/accessible/src/base/nsAccessible.cpp +++ b/accessible/src/base/nsAccessible.cpp @@ -2037,25 +2037,28 @@ nsAccessible::GetRelationByType(PRUint32 aRelationType, // Relationships are defined on the same content node that the role would be // defined on. - nsresult rv; + nsresult rv = NS_OK_NO_RELATION_TARGET; switch (aRelationType) { case nsIAccessibleRelation::RELATION_LABEL_FOR: { + RelatedAccIterator iter(GetDocAccessible(), mContent, + nsAccessibilityAtoms::aria_labelledby); + + nsAccessible* related = nsnull; + while ((related = iter.Next())) { + rv = nsRelUtils::AddTarget(aRelationType, aRelation, related); + NS_ENSURE_SUCCESS(rv, rv); + } + if (mContent->Tag() == nsAccessibilityAtoms::label) { nsIAtom *IDAttr = mContent->IsHTML() ? nsAccessibilityAtoms::_for : nsAccessibilityAtoms::control; rv = nsRelUtils:: AddTargetFromIDRefAttr(aRelationType, aRelation, mContent, IDAttr); NS_ENSURE_SUCCESS(rv, rv); - - if (rv != NS_OK_NO_RELATION_TARGET) - return NS_OK; // XXX bug 381599, avoid performance problems } - - return nsRelUtils:: - AddTargetFromNeighbour(aRelationType, aRelation, mContent, - nsAccessibilityAtoms::aria_labelledby); + return rv; } case nsIAccessibleRelation::RELATION_LABELLED_BY: @@ -2091,13 +2094,14 @@ nsAccessible::GetRelationByType(PRUint32 aRelationType, case nsIAccessibleRelation::RELATION_DESCRIPTION_FOR: { - rv = nsRelUtils:: - AddTargetFromNeighbour(aRelationType, aRelation, mContent, - nsAccessibilityAtoms::aria_describedby); - NS_ENSURE_SUCCESS(rv, rv); + RelatedAccIterator iter(GetDocAccessible(), mContent, + nsAccessibilityAtoms::aria_describedby); - if (rv != NS_OK_NO_RELATION_TARGET) - return NS_OK; // XXX bug 381599, avoid performance problems + nsAccessible* related = nsnull; + while ((related = iter.Next())) { + rv = nsRelUtils::AddTarget(aRelationType, aRelation, related); + NS_ENSURE_SUCCESS(rv, rv); + } if (mContent->Tag() == nsAccessibilityAtoms::description && mContent->IsXUL()) { @@ -2109,18 +2113,23 @@ nsAccessible::GetRelationByType(PRUint32 aRelationType, nsAccessibilityAtoms::control); } - return NS_OK; + return rv; } case nsIAccessibleRelation::RELATION_NODE_CHILD_OF: { - rv = nsRelUtils:: - AddTargetFromNeighbour(aRelationType, aRelation, mContent, - nsAccessibilityAtoms::aria_owns); - NS_ENSURE_SUCCESS(rv, rv); + RelatedAccIterator iter(GetDocAccessible(), mContent, + nsAccessibilityAtoms::aria_owns); + nsAccessible* related = nsnull; + while ((related = iter.Next())) { + rv = nsRelUtils::AddTarget(aRelationType, aRelation, related); + NS_ENSURE_SUCCESS(rv, rv); + } + + // Got relation from aria-owns, don't calculate it from native markup. if (rv != NS_OK_NO_RELATION_TARGET) - return NS_OK; // XXX bug 381599, avoid performance problems + return NS_OK; // This is an ARIA tree or treegrid that doesn't use owns, so we need to // get the parent the hard way. @@ -2153,14 +2162,20 @@ nsAccessible::GetRelationByType(PRUint32 aRelationType, } } - return NS_OK; + return rv; } case nsIAccessibleRelation::RELATION_CONTROLLED_BY: { - return nsRelUtils:: - AddTargetFromNeighbour(aRelationType, aRelation, mContent, - nsAccessibilityAtoms::aria_controls); + RelatedAccIterator iter(GetDocAccessible(), mContent, + nsAccessibilityAtoms::aria_controls); + + nsAccessible* related = nsnull; + while ((related = iter.Next())) { + rv = nsRelUtils::AddTarget(aRelationType, aRelation, related); + NS_ENSURE_SUCCESS(rv, rv); + } + return rv; } case nsIAccessibleRelation::RELATION_CONTROLLER_FOR: @@ -2188,9 +2203,15 @@ nsAccessible::GetRelationByType(PRUint32 aRelationType, case nsIAccessibleRelation::RELATION_FLOWS_FROM: { - return nsRelUtils:: - AddTargetFromNeighbour(aRelationType, aRelation, mContent, - nsAccessibilityAtoms::aria_flowto); + RelatedAccIterator iter(GetDocAccessible(), mContent, + nsAccessibilityAtoms::aria_flowto); + + nsAccessible* related = nsnull; + while ((related = iter.Next())) { + rv = nsRelUtils::AddTarget(aRelationType, aRelation, related); + NS_ENSURE_SUCCESS(rv, rv); + } + return rv; } case nsIAccessibleRelation::RELATION_DEFAULT_BUTTON: @@ -3169,8 +3190,17 @@ nsAccessible::EnsureChildren() // State is embedded children until text leaf accessible is appended. mChildrenFlags = eEmbeddedChildren; // Prevent reentry + + // Notify the document about caching status. + nsDocAccessible* document = GetDocAccessible(); + if (document) + document->NotifyOfCachingStart(this); + CacheChildren(); + if (document) + document->NotifyOfCachingEnd(this); + return PR_FALSE; } diff --git a/accessible/src/base/nsAccessible.h b/accessible/src/base/nsAccessible.h index a01fbe3b7ef0..bd4b0b0bb2f4 100644 --- a/accessible/src/base/nsAccessible.h +++ b/accessible/src/base/nsAccessible.h @@ -51,7 +51,6 @@ #include "nsStringGlue.h" #include "nsTArray.h" #include "nsRefPtrHashtable.h" -#include "nsDataHashtable.h" class AccGroupInfo; class EmbeddedObjCollector; @@ -67,8 +66,6 @@ class nsIView; typedef nsRefPtrHashtable nsAccessibleHashtable; -typedef nsDataHashtable, nsAccessible*> - NodeToAccessibleMap; // see nsAccessible::GetAttrValue #define NS_OK_NO_ARIA_VALUE \ diff --git a/accessible/src/base/nsCoreUtils.cpp b/accessible/src/base/nsCoreUtils.cpp index dfe601b56ba3..50c0142f8277 100644 --- a/accessible/src/base/nsCoreUtils.cpp +++ b/accessible/src/base/nsCoreUtils.cpp @@ -47,7 +47,6 @@ #include "nsIDOM3Node.h" #include "nsIDOMDocument.h" #include "nsIDOMDocumentView.h" -#include "nsIDOMDocumentXBL.h" #include "nsIDOMHTMLDocument.h" #include "nsIDOMHTMLElement.h" #include "nsIDOMNodeList.h" @@ -805,67 +804,6 @@ nsCoreUtils::GetLanguageFor(nsIContent *aContent, nsIContent *aRootContent, walkUp = walkUp->GetParent(); } -void -nsCoreUtils::GetElementsByIDRefsAttr(nsIContent *aContent, nsIAtom *aAttr, - nsIArray **aRefElements) -{ - *aRefElements = nsnull; - - nsAutoString ids; - if (!aContent->GetAttr(kNameSpaceID_None, aAttr, ids)) - return; - - ids.CompressWhitespace(PR_TRUE, PR_TRUE); - - nsCOMPtr document = do_QueryInterface(aContent->GetOwnerDoc()); - NS_ASSERTION(document, "The given node is not in document!"); - if (!document) - return; - - nsCOMPtr xblDocument; - if (aContent->IsInAnonymousSubtree()) - xblDocument = do_QueryInterface(document); - - nsCOMPtr refElms = do_CreateInstance(NS_ARRAY_CONTRACTID); - - while (!ids.IsEmpty()) { - nsAutoString id; - PRInt32 idLength = ids.FindChar(' '); - NS_ASSERTION(idLength != 0, - "Should not be 0 because of CompressWhitespace() call above"); - - if (idLength == kNotFound) { - id = ids; - ids.Truncate(); - } else { - id = Substring(ids, 0, idLength); - ids.Cut(0, idLength + 1); - } - - // If content is anonymous subtree then use "anonid" attribute to get - // elements, otherwise search elements in DOM by ID attribute. - nsCOMPtr refElement; - if (xblDocument) { - nsCOMPtr elm = - do_QueryInterface(aContent->GetBindingParent()); - xblDocument->GetAnonymousElementByAttribute(elm, - NS_LITERAL_STRING("anonid"), - id, - getter_AddRefs(refElement)); - } else { - document->GetElementById(id, getter_AddRefs(refElement)); - } - - if (!refElement) - continue; - - refElms->AppendElement(refElement, PR_FALSE); - } - - NS_ADDREF(*aRefElements = refElms); - return; -} - void nsCoreUtils::GetElementsHavingIDRefsAttr(nsIContent *aRootContent, nsIContent *aContent, @@ -1165,3 +1103,78 @@ nsAccessibleDOMStringList::Contains(const nsAString& aString, PRBool *aResult) return NS_OK; } + + +//////////////////////////////////////////////////////////////////////////////// +// IDRefsIterator +//////////////////////////////////////////////////////////////////////////////// + +IDRefsIterator::IDRefsIterator(nsIContent* aContent, nsIAtom* aIDRefsAttr) : + mCurrIdx(0) +{ + if (!aContent->IsInDoc() || + !aContent->GetAttr(kNameSpaceID_None, aIDRefsAttr, mIDs)) + return; + + if (aContent->IsInAnonymousSubtree()) { + mXBLDocument = do_QueryInterface(aContent->GetOwnerDoc()); + mBindingParent = do_QueryInterface(aContent->GetBindingParent()); + } else { + mDocument = aContent->GetOwnerDoc(); + } +} + +const nsDependentSubstring +IDRefsIterator::NextID() +{ + for (; mCurrIdx < mIDs.Length(); mCurrIdx++) { + if (!NS_IsAsciiWhitespace(mIDs[mCurrIdx])) + break; + } + + if (mCurrIdx >= mIDs.Length()) + return nsDependentSubstring(); + + nsAString::index_type idStartIdx = mCurrIdx; + while (++mCurrIdx < mIDs.Length()) { + if (NS_IsAsciiWhitespace(mIDs[mCurrIdx])) + break; + } + + return Substring(mIDs, idStartIdx, mCurrIdx++ - idStartIdx); +} + +nsIContent* +IDRefsIterator::NextElem() +{ + while (true) { + const nsDependentSubstring id = NextID(); + if (id.IsEmpty()) + break; + + nsIContent* refContent = GetElem(id); + if (refContent) + return refContent; + } + + return nsnull; +} + +nsIContent* +IDRefsIterator::GetElem(const nsDependentSubstring& aID) +{ + if (mXBLDocument) { + // If content is anonymous subtree then use "anonid" attribute to get + // elements, otherwise search elements in DOM by ID attribute. + + nsCOMPtr refElm; + mXBLDocument->GetAnonymousElementByAttribute(mBindingParent, + NS_LITERAL_STRING("anonid"), + aID, + getter_AddRefs(refElm)); + nsCOMPtr refContent = do_QueryInterface(refElm); + return refContent; + } + + return mDocument->GetElementById(aID); +} diff --git a/accessible/src/base/nsCoreUtils.h b/accessible/src/base/nsCoreUtils.h index 22ed47970401..cf6cb01d98a9 100644 --- a/accessible/src/base/nsCoreUtils.h +++ b/accessible/src/base/nsCoreUtils.h @@ -41,6 +41,7 @@ #include "nsAccessibilityAtoms.h" +#include "nsIDOMDocumentXBL.h" #include "nsIDOMNode.h" #include "nsIContent.h" #include "nsIBoxObject.h" @@ -304,17 +305,6 @@ public: static void GetLanguageFor(nsIContent *aContent, nsIContent *aRootContent, nsAString& aLanguage); - /** - * Return the array of elements the given node is referred to by its - * IDRefs attribute. - * - * @param aContent [in] the given node - * @param aAttr [in] IDRefs attribute on the given node - * @param aRefElements [out] result array of elements - */ - static void GetElementsByIDRefsAttr(nsIContent *aContent, nsIAtom *aAttr, - nsIArray **aRefElements); - /** * Return the array of elements having IDRefs that points to the given node. * @@ -515,5 +505,39 @@ private: nsTArray mNames; }; +/** + * Used to iterate through IDs or elements pointed by IDRefs attribute. Note, + * any method used to iterate through IDs or elements moves iterator to next + * position. + */ +class IDRefsIterator +{ +public: + IDRefsIterator(nsIContent* aContent, nsIAtom* aIDRefsAttr); + + /** + * Return next ID. + */ + const nsDependentSubstring NextID(); + + /** + * Return next element. + */ + nsIContent* NextElem(); + + /** + * Return the element with the given ID. + */ + nsIContent* GetElem(const nsDependentSubstring& aID); + +private: + nsString mIDs; + nsAString::index_type mCurrIdx; + + nsIDocument* mDocument; + nsCOMPtr mXBLDocument; + nsCOMPtr mBindingParent; +}; + #endif diff --git a/accessible/src/base/nsDocAccessible.cpp b/accessible/src/base/nsDocAccessible.cpp index 3016e0e9d34e..0917e9276cbe 100644 --- a/accessible/src/base/nsDocAccessible.cpp +++ b/accessible/src/base/nsDocAccessible.cpp @@ -85,6 +85,16 @@ namespace dom = mozilla::dom; PRUint32 nsDocAccessible::gLastFocusedAccessiblesState = 0; +static nsIAtom** kRelationAttrs[] = +{ + &nsAccessibilityAtoms::aria_labelledby, + &nsAccessibilityAtoms::aria_describedby, + &nsAccessibilityAtoms::aria_owns, + &nsAccessibilityAtoms::aria_controls, + &nsAccessibilityAtoms::aria_flowto +}; + +static const PRUint32 kRelationAttrsLen = NS_ARRAY_LENGTH(kRelationAttrs); //////////////////////////////////////////////////////////////////////////////// // Constructor/desctructor @@ -93,8 +103,10 @@ nsDocAccessible:: nsDocAccessible(nsIDocument *aDocument, nsIContent *aRootContent, nsIWeakReference *aShell) : nsHyperTextAccessibleWrap(aRootContent, aShell), - mDocument(aDocument), mScrollPositionChangedTicks(0), mIsLoaded(PR_FALSE) + mDocument(aDocument), mScrollPositionChangedTicks(0), mIsLoaded(PR_FALSE), + mCacheRoot(nsnull), mIsPostCacheProcessing(PR_FALSE) { + mDependentIDsHash.Init(); // XXX aaronl should we use an algorithm for the initial cache size? mAccessibleCache.Init(kDefaultCacheSize); mNodeToAccessibleMap.Init(kDefaultCacheSize); @@ -134,6 +146,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDocAccessible, nsAccessible) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mEventQueue) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mChildDocuments) + tmp->mDependentIDsHash.Clear(); tmp->mNodeToAccessibleMap.Clear(); ClearCache(tmp->mAccessibleCache); NS_IMPL_CYCLE_COLLECTION_UNLINK_END @@ -155,8 +168,7 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDocAccessible) // However at some point we may push to implement the interfaces and // return nsDocAccessible to inherit from nsAccessibleWrap. - nsCOMPtr xulDoc(do_QueryInterface(mDocument)); - if (xulDoc) + if (mDocument && mDocument->IsXUL()) status = nsAccessible::QueryInterface(aIID, (void**)&foundInterface); else status = nsHyperTextAccessible::QueryInterface(aIID, @@ -664,6 +676,7 @@ nsDocAccessible::Shutdown() mWeakShell = nsnull; // Avoid reentrancy + mDependentIDsHash.Clear(); mNodeToAccessibleMap.Clear(); ClearCache(mAccessibleCache); @@ -929,10 +942,19 @@ nsDocAccessible::AttributeWillChange(nsIDocument *aDocument, PRInt32 aNameSpaceID, nsIAtom* aAttribute, PRInt32 aModType) { - // XXX TODO: bugs 381599 467143 472142 472143 + // XXX TODO: bugs 381599 (partially fixed by 573469), 467143, 472142, 472143. // Here we will want to cache whatever state we are potentially interested in, // such as the existence of aria-pressed for button (so we know if we need to // newly expose it as a toggle button) etc. + + // Update dependent IDs cache. + if (aModType == nsIDOMMutationEvent::MODIFICATION || + aModType == nsIDOMMutationEvent::REMOVAL) { + nsAccessible* accessible = + GetAccService()->GetAccessibleInWeakShell(aElement, mWeakShell); + if (accessible) + RemoveDependentIDsFor(accessible, aAttribute); + } } void @@ -943,6 +965,16 @@ nsDocAccessible::AttributeChanged(nsIDocument *aDocument, { AttributeChangedImpl(aElement, aNameSpaceID, aAttribute); + // Update dependent IDs cache. + if (aModType == nsIDOMMutationEvent::MODIFICATION || + aModType == nsIDOMMutationEvent::ADDITION) { + nsAccessible* accessible = + GetAccService()->GetAccessibleInWeakShell(aElement, mWeakShell); + + if (accessible) + AddDependentIDsFor(accessible, aAttribute); + } + // If it was the focused node, cache the new state if (aElement == gLastFocusedNode) { nsAccessible *focusedAccessible = GetAccService()->GetAccessible(aElement); @@ -1362,6 +1394,7 @@ nsDocAccessible::BindToDocument(nsAccessible* aAccessible, } aAccessible->SetRoleMapEntry(aRoleMapEntry); + AddDependentIDsFor(aAccessible); return true; } @@ -1373,6 +1406,8 @@ nsDocAccessible::UnbindFromDocument(nsAccessible* aAccessible) mNodeToAccessibleMap.Get(aAccessible->GetNode()) == aAccessible) mNodeToAccessibleMap.Remove(aAccessible->GetNode()); + RemoveDependentIDsFor(aAccessible); + #ifdef DEBUG NS_ASSERTION(mAccessibleCache.GetWeak(aAccessible->UniqueID()), "Unbinding the unbound accessible!"); @@ -1554,9 +1589,130 @@ nsDocAccessible::RecreateAccessible(nsINode* aNode) } } +void +nsDocAccessible::NotifyOfCachingStart(nsAccessible* aAccessible) +{ + if (!mCacheRoot) + mCacheRoot = aAccessible; +} + +void +nsDocAccessible::NotifyOfCachingEnd(nsAccessible* aAccessible) +{ + if (mCacheRoot == aAccessible && !mIsPostCacheProcessing) { + // Allow invalidation list insertions while container children are recached. + mIsPostCacheProcessing = PR_TRUE; + + // Invalidate children of container accessible for each element in + // invalidation list. + for (PRUint32 idx = 0; idx < mInvalidationList.Length(); idx++) { + nsIContent* content = mInvalidationList[idx]; + nsAccessible* container = + GetAccService()->GetCachedContainerAccessible(content); + container->InvalidateChildren(); + + // Make sure we keep children updated. While we're inside of caching loop + // then we must exist it with cached children. + container->EnsureChildren(); + } + mInvalidationList.Clear(); + + mCacheRoot = nsnull; + mIsPostCacheProcessing = PR_FALSE; + } +} + //////////////////////////////////////////////////////////////////////////////// // Protected members +void +nsDocAccessible::AddDependentIDsFor(nsAccessible* aRelProvider, + nsIAtom* aRelAttr) +{ + for (PRUint32 idx = 0; idx < kRelationAttrsLen; idx++) { + nsIAtom* relAttr = *kRelationAttrs[idx]; + if (aRelAttr && aRelAttr != relAttr) + continue; + + IDRefsIterator iter(aRelProvider->GetContent(), relAttr); + while (true) { + const nsDependentSubstring id = iter.NextID(); + if (id.IsEmpty()) + break; + + AttrRelProviderArray* providers = mDependentIDsHash.Get(id); + if (!providers) { + providers = new AttrRelProviderArray(); + if (providers) { + if (!mDependentIDsHash.Put(id, providers)) { + delete providers; + providers = nsnull; + } + } + } + + if (providers) { + AttrRelProvider* provider = + new AttrRelProvider(relAttr, aRelProvider->GetContent()); + if (provider) { + providers->AppendElement(provider); + + // We've got here during the children caching. If the referenced + // content is not accessible then store it to pend its container + // children invalidation (this happens immediately after the caching + // is finished). + nsIContent* dependentContent = iter.GetElem(id); + if (dependentContent && !GetCachedAccessible(dependentContent)) { + mInvalidationList.AppendElement(dependentContent); + } + } + } + } + + // If the relation attribute is given then we don't have anything else to + // check. + if (aRelAttr) + break; + } +} + +void +nsDocAccessible::RemoveDependentIDsFor(nsAccessible* aRelProvider, + nsIAtom* aRelAttr) +{ + for (PRUint32 idx = 0; idx < kRelationAttrsLen; idx++) { + nsIAtom* relAttr = *kRelationAttrs[idx]; + if (aRelAttr && aRelAttr != *kRelationAttrs[idx]) + continue; + + IDRefsIterator iter(aRelProvider->GetContent(), relAttr); + while (true) { + const nsDependentSubstring id = iter.NextID(); + if (id.IsEmpty()) + break; + + AttrRelProviderArray* providers = mDependentIDsHash.Get(id); + if (providers) { + for (PRUint32 jdx = 0; jdx < providers->Length(); ) { + AttrRelProvider* provider = (*providers)[jdx]; + if (provider->mRelAttr == relAttr && + provider->mContent == aRelProvider->GetContent()) + providers->RemoveElement(provider); + else + jdx++; + } + if (providers->Length() == 0) + mDependentIDsHash.Remove(id); + } + } + + // If the relation attribute is given then we don't have anything else to + // check. + if (aRelAttr) + break; + } +} + void nsDocAccessible::FireValueChangeForTextFields(nsAccessible *aAccessible) { diff --git a/accessible/src/base/nsDocAccessible.h b/accessible/src/base/nsDocAccessible.h index 7ea913d3687c..8a30c339ab94 100644 --- a/accessible/src/base/nsDocAccessible.h +++ b/accessible/src/base/nsDocAccessible.h @@ -44,6 +44,8 @@ #include "nsHyperTextAccessibleWrap.h" #include "nsEventShell.h" +#include "nsClassHashtable.h" +#include "nsDataHashtable.h" #include "nsIDocument.h" #include "nsIDocumentObserver.h" #include "nsIEditor.h" @@ -210,6 +212,16 @@ public: */ nsAccessible* GetCachedAccessibleByUniqueIDInSubtree(void* aUniqueID); + /** + * Return true if the given ID is referred by relation attribute. + * + * @note Different elements may share the same ID if they are hosted inside + * XBL bindings. Be careful the result of this method may be senseless + * while it's called for XUL elements (where XBL is used widely). + */ + PRBool IsDependentID(const nsAString& aID) const + { return mDependentIDsHash.Get(aID, nsnull); } + /** * Initialize the newly created accessible and put it into document caches. * @@ -241,6 +253,17 @@ public: */ void RecreateAccessible(nsINode* aNode); + /** + * Used to notify the document that the accessible caching is started or + * finished. + * + * While children are cached we may encounter the case there's no accessible + * for referred content by related accessible. Keep the caching root and + * these related nodes to invalidate their containers after root caching. + */ + void NotifyOfCachingStart(nsAccessible* aAccessible); + void NotifyOfCachingEnd(nsAccessible* aAccessible); + protected: virtual void GetBoundsRect(nsRect& aRect, nsIFrame** aRelativeFrame); @@ -267,6 +290,28 @@ protected: mChildDocuments.RemoveElement(aChildDocument); } + /** + * Add dependent IDs pointed by accessible element by relation attribute to + * cache. If the relation attribute is missed then all relation attributes + * are checked. + * + * @param aRelProvider [in] accessible that element has relation attribute + * @param aRelAttr [in, optional] relation attribute + */ + void AddDependentIDsFor(nsAccessible* aRelProvider, + nsIAtom* aRelAttr = nsnull); + + /** + * Remove dependent IDs pointed by accessible element by relation attribute + * from cache. If the relation attribute is absent then all relation + * attributes are checked. + * + * @param aRelProvider [in] accessible that element has relation attribute + * @param aRelAttr [in, optional] relation attribute + */ + void RemoveDependentIDsFor(nsAccessible* aRelProvider, + nsIAtom* aRelAttr = nsnull); + static void ScrollTimerCallback(nsITimer *aTimer, void *aClosure); /** @@ -299,14 +344,6 @@ protected: CharacterDataChangeInfo* aInfo, PRBool aIsInserted); - /** - * Used to define should the event be fired on a delay. - */ - enum EEventFiringType { - eNormalEvent, - eDelayedEvent - }; - /** * Fire a value change event for the the given accessible if it is a text * field (has a ROLE_ENTRY). @@ -347,7 +384,8 @@ protected: * Cache of accessibles within this document accessible. */ nsAccessibleHashtable mAccessibleCache; - NodeToAccessibleMap mNodeToAccessibleMap; + nsDataHashtable, nsAccessible*> + mNodeToAccessibleMap; nsCOMPtr mDocument; nsCOMPtr mScrollWatchTimer; @@ -366,9 +404,46 @@ protected: static nsIAtom *gLastFocusedFrameType; nsTArray > mChildDocuments; + + /** + * A storage class for pairing content with one of its relation attributes. + */ + class AttrRelProvider + { + public: + AttrRelProvider(nsIAtom* aRelAttr, nsIContent* aContent) : + mRelAttr(aRelAttr), mContent(aContent) { } + + nsIAtom* mRelAttr; + nsIContent* mContent; + + private: + AttrRelProvider(); + AttrRelProvider(const AttrRelProvider&); + AttrRelProvider& operator =(const AttrRelProvider&); + }; + + /** + * The cache of IDs pointed by relation attributes. + */ + typedef nsTArray > AttrRelProviderArray; + nsClassHashtable mDependentIDsHash; + + friend class RelatedAccIterator; + + /** + * Used for our caching algorithm. We store the root of the tree that needs + * caching, the list of nodes that should be invalidated, and whether we are + * processing the invalidation list. + * + * @see NotifyOfCachingStart/NotifyOfCachingEnd + */ + nsAccessible* mCacheRoot; + nsTArray mInvalidationList; + PRBool mIsPostCacheProcessing; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsDocAccessible, NS_DOCACCESSIBLE_IMPL_CID) -#endif +#endif diff --git a/accessible/src/base/nsRelUtils.cpp b/accessible/src/base/nsRelUtils.cpp index f272c4ded59f..7ca8a258f9e4 100644 --- a/accessible/src/base/nsRelUtils.cpp +++ b/accessible/src/base/nsRelUtils.cpp @@ -134,27 +134,16 @@ nsRelUtils::AddTargetFromIDRefsAttr(PRUint32 aRelationType, nsIAccessibleRelation **aRelation, nsIContent *aContent, nsIAtom *aAttr) { - nsCOMPtr refElms; - nsCoreUtils::GetElementsByIDRefsAttr(aContent, aAttr, getter_AddRefs(refElms)); + nsresult rv = NS_OK_NO_RELATION_TARGET; - if (!refElms) - return NS_OK_NO_RELATION_TARGET; - - PRUint32 count = 0; - nsresult rv = refElms->GetLength(&count); - if (NS_FAILED(rv) || count == 0) - return NS_OK_NO_RELATION_TARGET; - - nsCOMPtr content; - for (PRUint32 idx = 0; idx < count; idx++) { - content = do_QueryElementAt(refElms, idx, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - rv = AddTargetFromContent(aRelationType, aRelation, content); + nsIContent* refElm = nsnull; + IDRefsIterator iter(aContent, aAttr); + while ((refElm = iter.NextElem())) { + rv = AddTargetFromContent(aRelationType, aRelation, refElm); NS_ENSURE_SUCCESS(rv, rv); } - return NS_OK; + return rv; } nsresult diff --git a/accessible/src/base/nsTextEquivUtils.cpp b/accessible/src/base/nsTextEquivUtils.cpp index 0abdb8073c48..48dcc15f7266 100644 --- a/accessible/src/base/nsTextEquivUtils.cpp +++ b/accessible/src/base/nsTextEquivUtils.cpp @@ -92,26 +92,14 @@ nsTextEquivUtils::GetTextEquivFromIDRefs(nsAccessible *aAccessible, if (!content) return NS_OK; - nsCOMPtr refElms; - nsCoreUtils::GetElementsByIDRefsAttr(content, aIDRefsAttr, - getter_AddRefs(refElms)); - - if (!refElms) - return NS_OK; - - PRUint32 count = 0; - nsresult rv = refElms->GetLength(&count); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr refContent; - for (PRUint32 idx = 0; idx < count; idx++) { - refContent = do_QueryElementAt(refElms, idx, &rv); - NS_ENSURE_SUCCESS(rv, rv); - + nsIContent* refContent = nsnull; + IDRefsIterator iter(content, aIDRefsAttr); + while ((refContent = iter.NextElem())) { if (!aTextEquiv.IsEmpty()) aTextEquiv += ' '; - rv = AppendTextEquivFromContent(aAccessible, refContent, &aTextEquiv); + nsresult rv = AppendTextEquivFromContent(aAccessible, refContent, + &aTextEquiv); NS_ENSURE_SUCCESS(rv, rv); } diff --git a/accessible/src/html/nsHTMLTableAccessible.cpp b/accessible/src/html/nsHTMLTableAccessible.cpp index a72dbc63fa66..50d1da285bad 100644 --- a/accessible/src/html/nsHTMLTableAccessible.cpp +++ b/accessible/src/html/nsHTMLTableAccessible.cpp @@ -308,34 +308,27 @@ nsHTMLTableCellAccessible::GetHeaderCells(PRInt32 aRowOrColumnHeaderCell, nsIArray **aHeaderCells) { // Get header cells from @header attribute. - nsCOMPtr headerCellElms; - nsCoreUtils::GetElementsByIDRefsAttr(mContent, nsAccessibilityAtoms::headers, - getter_AddRefs(headerCellElms)); - - if (headerCellElms) { + IDRefsIterator iter(mContent, nsAccessibilityAtoms::headers); + nsIContent* headerCellElm = iter.NextElem(); + if (headerCellElm) { nsresult rv = NS_OK; nsCOMPtr headerCells = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); - PRUint32 count = 0; - rv = headerCellElms->GetLength(&count); - if (NS_SUCCEEDED(rv) && count > 0) { - nsCOMPtr headerCellContent; - for (PRUint32 idx = 0; idx < count; idx++) { - headerCellContent = do_QueryElementAt(headerCellElms, idx, &rv); - nsAccessible *headerCell = - GetAccService()->GetAccessibleInWeakShell(headerCellContent, mWeakShell); + do { + nsAccessible* headerCell = + GetAccService()->GetAccessibleInWeakShell(headerCellElm, mWeakShell); - if (headerCell && - (aRowOrColumnHeaderCell == nsAccUtils::eRowHeaderCells && - headerCell->Role() == nsIAccessibleRole::ROLE_ROWHEADER || - aRowOrColumnHeaderCell == nsAccUtils::eColumnHeaderCells && - headerCell->Role() == nsIAccessibleRole::ROLE_COLUMNHEADER)) - headerCells->AppendElement(static_cast(headerCell), - PR_FALSE); + if (headerCell && + (aRowOrColumnHeaderCell == nsAccUtils::eRowHeaderCells && + headerCell->Role() == nsIAccessibleRole::ROLE_ROWHEADER || + aRowOrColumnHeaderCell == nsAccUtils::eColumnHeaderCells && + headerCell->Role() == nsIAccessibleRole::ROLE_COLUMNHEADER)) { + headerCells->AppendElement(static_cast(headerCell), + PR_FALSE); } - } + } while ((headerCellElm = iter.NextElem())); NS_ADDREF(*aHeaderCells = headerCells); return NS_OK; diff --git a/accessible/tests/mochitest/relations/Makefile.in b/accessible/tests/mochitest/relations/Makefile.in index 813f7b6a4632..de3d64cf206e 100644 --- a/accessible/tests/mochitest/relations/Makefile.in +++ b/accessible/tests/mochitest/relations/Makefile.in @@ -50,6 +50,7 @@ _TEST_FILES =\ test_general.xul \ test_tabbrowser.xul \ test_tree.xul \ + test_update.html \ $(NULL) libs:: $(_TEST_FILES) diff --git a/accessible/tests/mochitest/relations/test_general.html b/accessible/tests/mochitest/relations/test_general.html index 5a62fc7c8a46..a8fd692c698a 100644 --- a/accessible/tests/mochitest/relations/test_general.html +++ b/accessible/tests/mochitest/relations/test_general.html @@ -65,6 +65,9 @@ testRelation(iframeDocAcc, RELATION_NODE_CHILD_OF, iframeAcc); // aria-controls + getAccessible("tab"); + todo(false, + "Getting an accessible tab, otherwise relations for tabpanel aren't cached. Bug 606924 will fix that."); testRelation("tabpanel", RELATION_CONTROLLED_BY, "tab"); testRelation("tab", RELATION_CONTROLLER_FOR, "tabpanel"); diff --git a/accessible/tests/mochitest/relations/test_general.xul b/accessible/tests/mochitest/relations/test_general.xul index 35ab6fd1c81e..6a689adc2476 100644 --- a/accessible/tests/mochitest/relations/test_general.xul +++ b/accessible/tests/mochitest/relations/test_general.xul @@ -66,6 +66,9 @@ testRelation(iframeDocAcc, RELATION_NODE_CHILD_OF, iframeAcc); // aria-controls + getAccessible("tab"); + todo(false, + "Getting an accessible tab, otherwise relations for tabpanel aren't cached. Bug 606924 will fix that."); testRelation("tabpanel", RELATION_CONTROLLED_BY, "tab"); testRelation("tab", RELATION_CONTROLLER_FOR, "tabpanel"); diff --git a/accessible/tests/mochitest/relations/test_update.html b/accessible/tests/mochitest/relations/test_update.html new file mode 100644 index 000000000000..99dcd6055b6e --- /dev/null +++ b/accessible/tests/mochitest/relations/test_update.html @@ -0,0 +1,162 @@ + + + + Test updating of accessible relations + + + + + + + + + + + + + + + + + + Mozilla Bug 573469 + +

+ +
+  
+ +
label
+
label2
+ + + + diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css index 59dbb84cebab..750221984f27 100644 --- a/browser/base/content/browser.css +++ b/browser/base/content/browser.css @@ -166,35 +166,46 @@ html|*.urlbar-input { /* over-link in location bar */ -.urlbar-over-link-layer[overlinkstate="fade-in"], -.urlbar-textbox-container:not([overlinkstate]) { - -moz-transition-property: color; - -moz-transition-duration: 150ms; - -moz-transition-timing-function: cubic-bezier(0.0, 0.6, 1.0, 1.0); -} - .urlbar-textbox-container[overlinkstate="fade-in"], -.urlbar-over-link-layer:not([overlinkstate]) { +.urlbar-over-link-layer[overlinkstate="fade-out"] { -moz-transition-property: color; -moz-transition-duration: 150ms; - -moz-transition-timing-function: linear; color: transparent; } -.urlbar-over-link-box[overlinkstate="fade-in"], -.urlbar-textbox-container-children:not([overlinkstate]) { +.urlbar-over-link-layer[overlinkstate="fade-in"], +.urlbar-textbox-container[overlinkstate="fade-out"] { + -moz-transition-property: color; + -moz-transition-duration: 150ms; + -moz-transition-timing-function: cubic-bezier(0.0, 1.0, 1.0, 1.0); +} + +.urlbar-over-link-box[overlinkstate="fade-in"] { -moz-transition-property: opacity; -moz-transition-duration: 150ms; opacity: 1; } -.urlbar-textbox-container-children[overlinkstate="fade-in"], -.urlbar-over-link-box:not([overlinkstate]) { +.urlbar-over-link-box[overlinkstate="fade-out"] { + -moz-transition-property: opacity; + -moz-transition-duration: 150ms; + -moz-transition-timing-function: cubic-bezier(0.0, 1.0, 1.0, 1.0); + opacity: 0; +} + +.urlbar-textbox-container-children[overlinkstate="fade-in"] { -moz-transition-property: opacity; -moz-transition-duration: 150ms; opacity: 0; } +.urlbar-textbox-container-children[overlinkstate="fade-out"] { + -moz-transition-property: opacity; + -moz-transition-duration: 150ms; + -moz-transition-timing-function: cubic-bezier(0.0, 1.0, 1.0, 1.0); + opacity: 1; +} + .urlbar-textbox-container[overlinkstate="showing"] { color: transparent; } @@ -207,6 +218,14 @@ html|*.urlbar-input { opacity: 0; } +.urlbar-over-link-layer:not([overlinkstate]) { + color: transparent; +} + +.urlbar-over-link-box:not([overlinkstate]) { + opacity: 0; +} + /* For results that are actions, their description text is shown instead of the URL - this needs to follow the locale's direction, unlike URLs. */ richlistitem[type~="action"]:-moz-locale-dir(rtl) > .ac-url-box { diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 5f5fc5825b24..9cabf1b4912a 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -3994,14 +3994,16 @@ var XULBrowserWindow = { this.defaultStatus = status; }, - setOverLink: function (link) { - // Encode bidirectional formatting characters. - // (RFC 3987 sections 3.2 and 4.1 paragraph 6) - link = link.replace(/[\u200e\u200f\u202a\u202b\u202c\u202d\u202e]/g, + setOverLink: function (url, anchorElt) { + if (gURLBar) { + // Encode bidirectional formatting characters. + // (RFC 3987 sections 3.2 and 4.1 paragraph 6) + url = url.replace(/[\u200e\u200f\u202a\u202b\u202c\u202d\u202e]/g, encodeURIComponent); - gURLBar.setOverLink(link); + gURLBar.setOverLink(url); + } }, - + // Called before links are navigated to to allow us to retarget them if needed. onBeforeLinkTraversal: function(originalTarget, linkURI, linkNode, isAppTab) { // Don't modify non-default targets or targets that aren't in top-level app @@ -4198,7 +4200,9 @@ var XULBrowserWindow = { else this.isImage.setAttribute('disabled', 'true'); + this.hideOverLinkImmediately = true; this.setOverLink("", null); + this.hideOverLinkImmediately = false; // We should probably not do this if the value has changed since the user // searched diff --git a/browser/base/content/browser.xul b/browser/base/content/browser.xul index 910cbf89c3d4..cd2add419c91 100644 --- a/browser/base/content/browser.xul +++ b/browser/base/content/browser.xul @@ -444,10 +444,12 @@ - - - - + + + + + + diff --git a/browser/base/content/tabview/drag.js b/browser/base/content/tabview/drag.js index 8b47fdf8e147..b6fad5b684c2 100644 --- a/browser/base/content/tabview/drag.js +++ b/browser/base/content/tabview/drag.js @@ -43,9 +43,17 @@ // The Drag that's currently in process. var drag = { info: null, - zIndex: 100 + zIndex: 100, + lastMoveTime: 0 }; +//---------- +//Variable: resize +//The resize (actually a Drag) that is currently in process +var resize = { + info: null, + lastMoveTime: 0 +}; // ########## // Class: Drag (formerly DragInfo) diff --git a/browser/base/content/tabview/groupitems.js b/browser/base/content/tabview/groupitems.js index ce351527928f..838cd5a6a7aa 100644 --- a/browser/base/content/tabview/groupitems.js +++ b/browser/base/content/tabview/groupitems.js @@ -564,17 +564,25 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), { this._sendToSubscribers("close"); this.removeTrenches(); - iQ(this.container).animate({ - opacity: 0, - "-moz-transform": "scale(.3)", - }, { - duration: 170, - complete: function() { - iQ(this).remove(); - Items.unsquish(); - } - }); - + if (this.hidden) { + iQ(this.container).remove(); + if (this.$undoContainer) { + this.$undoContainer.remove(); + this.$undoContainer = null; + } + Items.unsquish(); + } else { + iQ(this.container).animate({ + opacity: 0, + "-moz-transform": "scale(.3)", + }, { + duration: 170, + complete: function() { + iQ(this).remove(); + Items.unsquish(); + } + }); + } this.deleteData(); }, @@ -607,10 +615,107 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), { UI.setActiveTab( UI.getClosestTab(closeCenter) ); }, + // ---------- + // Function: _unhide + // Shows the hidden group. + _unhide: function GroupItem__unhide() { + let self = this; + + this._cancelFadeAwayUndoButtonTimer(); + this.hidden = false; + this.$undoContainer.remove(); + this.$undoContainer = null; + + iQ(this.container).show().animate({ + "-moz-transform": "scale(1)", + "opacity": 1 + }, { + duration: 170, + complete: function() { + self._children.forEach(function(child) { + iQ(child.container).show(); + }); + } + }); + + self._sendToSubscribers("groupShown", { groupItemId: self.id }); + }, + + // ---------- + // Function: closeHidden + // Removes the group item, its children and its container. + closeHidden: function GroupItem_closeHidden() { + let self = this; + + this._cancelFadeAwayUndoButtonTimer(); + + // when "TabClose" event is fired, the browser tab is about to close and our + // item "close" event is fired. And then, the browser tab gets closed. + // In other words, the group "close" event is fired before all browser + // tabs in the group are closed. The below code would fire the group "close" + // event only after all browser tabs in that group are closed. + let shouldRemoveTabItems = []; + let toClose = this._children.concat(); + toClose.forEach(function(child) { + child.removeSubscriber(self, "close"); + + let removed = child.close(); + if (removed) { + shouldRemoveTabItems.push(child); + } else { + // child.removeSubscriber() must be called before child.close(), + // therefore we call child.addSubscriber() if the tab is not removed. + child.addSubscriber(self, "close", function() { + self.remove(child); + }); + } + }); + + if (shouldRemoveTabItems.length != toClose.length) { + // remove children without the assiciated tab and show the group item + shouldRemoveTabItems.forEach(function(child) { + self.remove(child, { dontArrange: true }); + }); + + this.$undoContainer.fadeOut(function() { self._unhide() }); + } else { + this.close(); + } + }, + + // ---------- + // Function: _fadeAwayUndoButton + // Fades away the undo button + _fadeAwayUndoButton: function GroupItem__fadeAwayUdoButton() { + let self = this; + + if (this.$undoContainer) { + // if there is one or more orphan tabs or there is more than one group + // and other groupS are not empty, fade away the undo button. + let shouldFadeAway = GroupItems.getOrphanedTabs().length > 0; + + if (!shouldFadeAway && GroupItems.groupItems.length > 1) { + shouldFadeAway = + GroupItems.groupItems.some(function(groupItem) { + return (groupItem != self && groupItem.getChildren().length > 0); + }); + } + if (shouldFadeAway) { + self.$undoContainer.animate({ + color: "transparent", + opacity: 0 + }, { + duration: this._fadeAwayUndoButtonDuration, + complete: function() { self.closeHidden(); } + }); + } + } + }, + // ---------- // Function: _createUndoButton // Makes the affordance for undo a close group action - _createUndoButton: function() { + _createUndoButton: function GroupItem__createUndoButton() { let self = this; this.$undoContainer = iQ("
") .addClass("undo") @@ -629,6 +734,7 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), { }); this.hidden = true; + // hide group item and show undo container. setTimeout(function() { self.$undoContainer.animate({ "-moz-transform": "scale(1)", @@ -642,36 +748,17 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), { }); }, 50); + // add click handlers this.$undoContainer.click(function(e) { // Only do this for clicks on this actual element. if (e.target.nodeName != self.$undoContainer[0].nodeName) return; - self.$undoContainer.fadeOut(function() { - iQ(this).remove(); - self.hidden = false; - self._cancelFadeAwayUndoButtonTimer(); - self.$undoContainer = null; - - iQ(self.container).show().animate({ - "-moz-transform": "scale(1)", - "opacity": 1 - }, { - duration: 170, - complete: function() { - self._children.forEach(function(child) { - iQ(child.container).show(); - }); - } - }); - - self._sendToSubscribers("groupShown", { groupItemId: self.id }); - }); + self.$undoContainer.fadeOut(function() { self._unhide(); }); }); undoClose.click(function() { - self._cancelFadeAwayUndoButtonTimer(); - self.$undoContainer.fadeOut(function() { self._removeHiddenGroupItem(); }); + self.$undoContainer.fadeOut(function() { self.closeHidden(); }); }); this.setupFadeAwayUndoButtonTimer(); @@ -704,60 +791,6 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), { this._undoButtonTimeoutId = null; }, - // ---------- - // Fades away the undo button - _fadeAwayUndoButton: function() { - let self = this; - - if (this.$undoContainer) { - // if there is one or more orphan tabs or there is more than one group - // and other groupS are not empty, fade away the undo button. - let shouldFadeAway = GroupItems.getOrphanedTabs().length > 0; - - if (!shouldFadeAway && GroupItems.groupItems.length > 1) { - shouldFadeAway = - GroupItems.groupItems.some(function(groupItem) { - return (groupItem != self && groupItem.getChildren().length > 0); - }); - } - if (shouldFadeAway) { - self.$undoContainer.animate({ - color: "transparent", - opacity: 0 - }, { - duration: this.fadeAwayUndoButtonDuration, - complete: function() { self._removeHiddenGroupItem(); } - }); - } - } - }, - - // ---------- - // Removes the group item, its children and its container. - _removeHiddenGroupItem: function() { - let self = this; - - // close all children - let toClose = this._children.concat(); - toClose.forEach(function(child) { - child.removeSubscriber(self, "close"); - child.close(); - }); - - // remove all children - this.removeAll(); - GroupItems.unregister(this); - this._sendToSubscribers("close"); - this.removeTrenches(); - - iQ(this.container).remove(); - this.$undoContainer.remove(); - this.$undoContainer = null; - Items.unsquish(); - - this.deleteData(); - }, - // ---------- // Function: add // Adds an item to the groupItem. @@ -1047,6 +1080,10 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), { // Parameters: // options - passed to or <_stackArrange> arrange: function GroupItem_arrange(options) { + if (GroupItems._arrangePaused) { + GroupItems.pushArrange(this, options); + return; + } if (this.expanded) { this.topChild = null; var box = new Rect(this.expanded.bounds); @@ -1490,6 +1527,9 @@ let GroupItems = { _activeGroupItem: null, _activeOrphanTab: null, _cleanupFunctions: [], + _arrangePaused: false, + _arrangesPending: [], + _removingHiddenGroups: false, // ---------- // Function: init @@ -1521,6 +1561,51 @@ let GroupItems = { this.groupItems = null; }, + // ---------- + // Function: pauseArrange + // Bypass arrange() calls and collect for resolution in + // resumeArrange() + pauseArrange: function GroupItems_pauseArrange() { + Utils.assert(this._arrangePaused == false, + "pauseArrange has been called while already paused"); + Utils.assert(this._arrangesPending.length == 0, + "There are bypassed arrange() calls that haven't been resolved"); + this._arrangePaused = true; + }, + + // ---------- + // Function: pushArrange + // Push an arrange() call and its arguments onto an array + // to be resolved in resumeArrange() + pushArrange: function GroupItems_pushArrange(groupItem, options) { + Utils.assert(this._arrangePaused, + "Ensure pushArrange() called while arrange()s aren't paused"); + let i; + for (i = 0; i < this._arrangesPending.length; i++) + if (this._arrangesPending[i].groupItem === groupItem) + break; + let arrangeInfo = { + groupItem: groupItem, + options: options + }; + if (i < this._arrangesPending.length) + this._arrangesPending[i] = arrangeInfo; + else + this._arrangesPending.push(arrangeInfo); + }, + + // ---------- + // Function: resumeArrange + // Resolve bypassed and collected arrange() calls + resumeArrange: function GroupItems_resumeArrange() { + for (let i = 0; i < this._arrangesPending.length; i++) { + let g = this._arrangesPending[i]; + g.groupItem.arrange(g.options); + } + this._arrangesPending = []; + this._arrangePaused = false; + }, + // ---------- // Function: _handleAttrModified // watch for icon changes on app tabs @@ -2107,19 +2192,16 @@ let GroupItems = { // Function: removeHiddenGroups // Removes all hidden groups' data and its browser tabs. removeHiddenGroups: function GroupItems_removeHiddenGroups() { - iQ(".undo").remove(); - - // ToDo: encapsulate this in the group item. bug 594863 - this.groupItems.forEach(function(groupItem) { - if (groupItem.hidden) { - let toClose = groupItem._children.concat(); - toClose.forEach(function(child) { - child.removeSubscriber(groupItem, "close"); - child.close(); - }); + if (this._removingHiddenGroups) + return; + this._removingHiddenGroups = true; - groupItem.deleteData(); - } - }); + let groupItems = this.groupItems.concat(); + groupItems.forEach(function(groupItem) { + if (groupItem.hidden) + groupItem.closeHidden(); + }); + + this._removingHiddenGroups = false; } }; diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index ce22d6b5a2e2..24ba17c7566a 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -699,6 +699,7 @@ iQClass.prototype = { let events = [ 'keyup', 'keydown', + 'keypress', 'mouseup', 'mousedown', 'mouseover', diff --git a/browser/base/content/tabview/items.js b/browser/base/content/tabview/items.js index 5d3701e9b4dd..ce1548a040de 100644 --- a/browser/base/content/tabview/items.js +++ b/browser/base/content/tabview/items.js @@ -216,7 +216,6 @@ Item.prototype = { // ___ resize var self = this; - var resizeInfo = null; this.resizeOptions = { aspectRatio: self.keepProportional, minWidth: 90, @@ -224,16 +223,16 @@ Item.prototype = { start: function(e,ui) { if (this.isAGroupItem) GroupItems.setActiveGroupItem(this); - resizeInfo = new Drag(this, e, true); // true = isResizing + resize.info = new Drag(this, e, true); // true = isResizing }, resize: function(e,ui) { - resizeInfo.snap(UI.rtl ? 'topright' : 'topleft', false, self.keepProportional); + resize.info.snap(UI.rtl ? 'topright' : 'topleft', false, self.keepProportional); }, stop: function() { self.setUserSize(); self.pushAway(); - resizeInfo.stop(); - resizeInfo = null; + resize.info.stop(); + resize.info = null; } }; }, @@ -599,6 +598,9 @@ Item.prototype = { // ___ mousemove var handleMouseMove = function(e) { + // global drag tracking + drag.lastMoveTime = Date.now(); + // positioning var mouse = new Point(e.pageX, e.pageY); if (!startSent) { @@ -766,6 +768,9 @@ Item.prototype = { // ___ mousemove var handleMouseMove = function(e) { + // global resize tracking + resize.lastMoveTime = Date.now(); + var mouse = new Point(e.pageX, e.pageY); var box = self.getBounds(); if (UI.rtl) { diff --git a/browser/base/content/tabview/search.js b/browser/base/content/tabview/search.js index 0d845ef9821d..3a1dc8804173 100644 --- a/browser/base/content/tabview/search.js +++ b/browser/base/content/tabview/search.js @@ -132,7 +132,7 @@ var TabUtils = { // Function: favURLOf // Given a or a returns the URL of tab's favicon. faviconURLOf: function TabUtils_faviconURLOf(tab) { - return tab.image != undefined ? tab.image : tab.favEl.src; + return tab.image != undefined ? tab.image : tab.favImgEl.src; }, // --------- @@ -335,9 +335,9 @@ SearchEventHandlerClass.prototype = { // Handles all keypresses before the search interface is brought up. beforeSearchKeyHandler: function (event) { // Only match reasonable text-like characters for quick search. - var key = String.fromCharCode(event.which); // TODO: Also include funky chars. Bug 593904 - if (!key.match(/[A-Z0-9]/) || event.altKey || event.ctrlKey || event.metaKey) + if (!String.fromCharCode(event.which).match(/[a-zA-Z0-9]/) || event.altKey || + event.ctrlKey || event.metaKey) return; // If we are already in an input field, allow typing as normal. @@ -353,16 +353,17 @@ SearchEventHandlerClass.prototype = { // Handles all keypresses while search mode. inSearchKeyHandler: function (event) { var term = iQ("#searchbox").val(); - - if (event.which == event.DOM_VK_ESCAPE) - hideSearch(event); - if (event.which == event.DOM_VK_BACK_SPACE && term.length <= 1) + + if ((event.keyCode == event.DOM_VK_ESCAPE) || + (event.keyCode == event.DOM_VK_BACK_SPACE && term.length <= 1)) { hideSearch(event); + return; + } var matcher = new TabMatcher(term); var matches = matcher.matched(); var others = matcher.matchedTabsFromOtherWindows(); - if (event.which == event.DOM_VK_RETURN && (matches.length > 0 || others.length > 0)) { + if (event.keyCode == event.DOM_VK_RETURN && (matches.length > 0 || others.length > 0)) { hideSearch(event); if (matches.length > 0) matches[0].zoomIn(); @@ -378,9 +379,9 @@ SearchEventHandlerClass.prototype = { switchToBeforeMode: function switchToBeforeMode() { var self = this; if (this.currentHandler) - iQ(document).unbind("keydown", this.currentHandler); + iQ(window).unbind("keypress", this.currentHandler); this.currentHandler = function(event) self.beforeSearchKeyHandler(event); - iQ(document).keydown(self.currentHandler); + iQ(window).keypress(this.currentHandler); }, // ---------- @@ -390,9 +391,9 @@ SearchEventHandlerClass.prototype = { switchToInMode: function switchToInMode() { var self = this; if (this.currentHandler) - iQ(document).unbind("keydown", this.currentHandler); + iQ(window).unbind("keypress", this.currentHandler); this.currentHandler = function(event) self.inSearchKeyHandler(event); - iQ(document).keydown(self.currentHandler); + iQ(window).keypress(this.currentHandler); } }; @@ -488,13 +489,13 @@ function hideSearch(event){ event.stopPropagation(); } - let newEvent = document.createEvent("Events"); - newEvent.initEvent("tabviewsearchdisabled", false, false); - dispatchEvent(newEvent); - // Return focus to the tab window UI.blurAll(); gTabViewFrame.contentWindow.focus(); + + let newEvent = document.createEvent("Events"); + newEvent.initEvent("tabviewsearchdisabled", false, false); + dispatchEvent(newEvent); } function performSearch() { @@ -513,13 +514,13 @@ function ensureSearchShown(event){ var $search = iQ("#search"); var $searchbox = iQ("#searchbox"); iQ("#searchbutton").css({ opacity: 1 }); - - - if ($search.css("display") == "none") { + + + if (!isSearchEnabled()) { $search.show(); var mainWindow = gWindow.document.getElementById("main-window"); mainWindow.setAttribute("activetitlebarcolor", "#717171"); - + // Marshal the focusing, otherwise you end up with // a race condition where only sometimes would the // first keystroke be registered by the search box. @@ -529,18 +530,20 @@ function ensureSearchShown(event){ $searchbox[0].focus(); $searchbox[0].val = '0'; $searchbox.css({"z-index":"1015"}); - if (event != null){ - var keyCode = event.which + (event.shiftKey ? 0 : 32); - $searchbox.val(String.fromCharCode(keyCode)); - } - }, 0); + if (event != null) + $searchbox.val(String.fromCharCode(event.charCode)); - let newEvent = document.createEvent("Events"); - newEvent.initEvent("tabviewsearchenabled", false, false); - dispatchEvent(newEvent); + let newEvent = document.createEvent("Events"); + newEvent.initEvent("tabviewsearchenabled", false, false); + dispatchEvent(newEvent); + }, 0); } } +function isSearchEnabled() { + return iQ("#search").css("display") != "none"; +} + var SearchEventHandler = new SearchEventHandlerClass(); // Features to add: diff --git a/browser/base/content/tabview/tabitems.js b/browser/base/content/tabview/tabitems.js index fa706836b0a6..46b53367b0ad 100644 --- a/browser/base/content/tabview/tabitems.js +++ b/browser/base/content/tabview/tabitems.js @@ -70,8 +70,10 @@ function TabItem(tab, options) { this.canvasSizeForced = false; this.isShowingCachedData = false; - this.favEl = (iQ('.favicon>img', $div))[0]; + this.favEl = (iQ('.favicon', $div))[0]; + this.favImgEl = (iQ('.favicon>img', $div))[0]; this.nameEl = (iQ('.tab-title', $div))[0]; + this.thumbEl = (iQ('.thumb', $div))[0]; this.canvasEl = (iQ('.thumb canvas', $div))[0]; this.cachedThumbEl = (iQ('img.cached-thumb', $div))[0]; @@ -96,6 +98,8 @@ function TabItem(tab, options) { this.bounds = $div.bounds(); + this._lastTabUpdateTime = Date.now(); + // ___ superclass setup this._init($div[0]); @@ -194,6 +198,7 @@ function TabItem(tab, options) { iQ("
") .addClass('close') .appendTo($div); + this.closeEl = (iQ(".close", $div))[0]; iQ("
") .addClass('expander') @@ -322,10 +327,10 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), { this.bounds.copy(rect); else { var $container = iQ(this.container); - var $title = iQ('.tab-title', $container); - var $thumb = iQ('.thumb', $container); - var $close = iQ('.close', $container); - var $fav = iQ('.favicon', $container); + var $title = iQ(this.nameEl); + var $thumb = iQ(this.thumbEl); + var $close = iQ(this.closeEl); + var $fav = iQ(this.favEl); var css = {}; const fontSizeRange = new Range(8,15); @@ -461,12 +466,20 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), { // Function: close // Closes this item (actually closes the tab associated with it, which automatically // closes the item. + // Returns true if this tab is removed. close: function TabItem_close() { + // when "TabClose" event is fired, the browser tab is about to close and our + // item "close" is fired before the browser tab actually get closed. + // Therefore, we need "tabRemoved" event below. gBrowser.removeTab(this.tab); - this._sendToSubscribers("tabRemoved"); + let tabNotClosed = + Array.some(gBrowser.tabs, function(tab) { return tab == this.tab; }, this); + if (!tabNotClosed) + this._sendToSubscribers("tabRemoved"); // No need to explicitly delete the tab data, becasue sessionstore data // associated with the tab will automatically go away + return !tabNotClosed; }, // ---------- @@ -544,9 +557,14 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), { function onZoomDone() { UI.goToTab(tab); - if (isNewBlankTab) - gWindow.gURLBar.focus(); - + // tab might not be selected because hideTabView() is invoked after + // UI.goToTab() so we need to setup everything for the gBrowser.selectedTab + if (tab != gBrowser.selectedTab) { + UI.onTabSelect(gBrowser.selectedTab); + } else { + if (isNewBlankTab) + gWindow.gURLBar.focus(); + } if (childHitResult.callback) childHitResult.callback(); } @@ -581,8 +599,9 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), { onZoomDone(); } }); - } else + } else { setTimeout(onZoomDone, 0); + } } }, @@ -684,10 +703,11 @@ let TabItems = { items: [], paintingPaused: 0, _tabsWaitingForUpdate: [], - _heartbeatOn: false, - _heartbeatTiming: 100, // milliseconds between beats + _heartbeatOn: false, // see explanation at startHeartbeat() below + _heartbeatTiming: 100, // milliseconds between _checkHeartbeat() calls _lastUpdateTime: Date.now(), _eventListeners: [], + tempCanvas: null, // ---------- // Function: init @@ -696,6 +716,15 @@ let TabItems = { Utils.assert(window.AllTabs, "AllTabs must be initialized first"); var self = this; + let $canvas = iQ(""); + $canvas.appendTo(iQ("body")); + $canvas.hide(); + this.tempCanvas = $canvas[0]; + // 150 pixels is an empirical size, below which FF's drawWindow() + // algorithm breaks down + this.tempCanvas.width = 150; + this.tempCanvas.height = 150; + // When a tab is opened, create the TabItem this._eventListeners["open"] = function(tab) { if (tab.ownerDocument.defaultView != gWindow || tab.pinned) @@ -774,6 +803,7 @@ let TabItems = { if (shouldDefer && !isCurrentTab) { if (this._tabsWaitingForUpdate.indexOf(tab) == -1) this._tabsWaitingForUpdate.push(tab); + this.startHeartbeat(); } else this._update(tab); } catch(e) { @@ -802,8 +832,8 @@ let TabItems = { if (iconUrl == null) iconUrl = Utils.defaultFaviconURL; - if (iconUrl != tabItem.favEl.src) - tabItem.favEl.src = iconUrl; + if (iconUrl != tabItem.favImgEl.src) + tabItem.favImgEl.src = iconUrl; // ___ URL let tabUrl = tab.linkedBrowser.currentURI.spec; @@ -834,6 +864,9 @@ let TabItems = { } } + this._lastUpdateTime = Date.now(); + tabItem._lastTabUpdateTime = this._lastUpdateTime; + tabItem.tabCanvas.paint(); // ___ cache @@ -843,8 +876,6 @@ let TabItems = { } catch(e) { Utils.log(e); } - - this._lastUpdateTime = Date.now(); }, // ---------- @@ -901,55 +932,64 @@ let TabItems = { }, // ---------- - // Function: heartbeat - // Allows us to spreadout update calls over a period of time. - heartbeat: function TabItems_heartbeat() { - if (!this._heartbeatOn) + // Function: startHeartbeat + // Start a new heartbeat if there isn't one already started. + // The heartbeat is a chain of setTimeout calls that allows us to spread + // out update calls over a period of time. + // _heartbeatOn is used to make sure that we don't add multiple + // setTimeout chains. + startHeartbeat: function TabItems_startHeartbeat() { + if (!this._heartbeatOn) { + this._heartbeatOn = true; + let self = this; + setTimeout(function() { + self._checkHeartbeat(); + }, this._heartbeatTiming); + } + }, + + // ---------- + // Function: _checkHeartbeat + // This periodically checks for tabs waiting to be updated, and calls + // _update on them. + // Should only be called by startHeartbeat and resumePainting. + _checkHeartbeat: function TabItems__checkHeartbeat() { + this._heartbeatOn = false; + + if (this.isPaintingPaused()) return; - if (this._tabsWaitingForUpdate.length) { + if (this._tabsWaitingForUpdate.length && UI.isIdle()) { this._update(this._tabsWaitingForUpdate[0]); - // _update will remove the tab from the waiting list + //_update will remove the tab from the waiting list } - let self = this; if (this._tabsWaitingForUpdate.length) { - setTimeout(function() { - self.heartbeat(); - }, this._heartbeatTiming); - } else - this._hearbeatOn = false; - }, - - // ---------- - // Function: pausePainting - // Tells TabItems to stop updating thumbnails (so you can do - // animations without thumbnail paints causing stutters). - // pausePainting can be called multiple times, but every call to - // pausePainting needs to be mirrored with a call to . - pausePainting: function TabItems_pausePainting() { - this.paintingPaused++; - - if (this.isPaintingPaused() && this._heartbeatOn) - this._heartbeatOn = false; - }, - - // ---------- - // Function: resumePainting - // Undoes a call to . For instance, if you called - // pausePainting three times in a row, you'll need to call resumePainting - // three times before TabItems will start updating thumbnails again. - resumePainting: function TabItems_resumePainting() { - this.paintingPaused--; - - if (!this.isPaintingPaused() && - this._tabsWaitingForUpdate.length && - !this._heartbeatOn) { - this._heartbeatOn = true; - this.heartbeat(); + this.startHeartbeat(); } }, + // ---------- + // Function: pausePainting + // Tells TabItems to stop updating thumbnails (so you can do + // animations without thumbnail paints causing stutters). + // pausePainting can be called multiple times, but every call to + // pausePainting needs to be mirrored with a call to . + pausePainting: function TabItems_pausePainting() { + this.paintingPaused++; + }, + + // ---------- + // Function: resumePainting + // Undoes a call to . For instance, if you called + // pausePainting three times in a row, you'll need to call resumePainting + // three times before TabItems will start updating thumbnails again. + resumePainting: function TabItems_resumePainting() { + this.paintingPaused--; + if (!this.isPaintingPaused()) + this.startHeartbeat(); + }, + // ---------- // Function: isPaintingPaused // Returns a boolean indicating whether painting @@ -1106,8 +1146,6 @@ TabCanvas.prototype = { // ---------- // Function: paint paint: function TabCanvas_paint(evt) { - var ctx = this.canvas.getContext("2d"); - var w = this.canvas.width; var h = this.canvas.height; if (!w || !h) @@ -1119,19 +1157,59 @@ TabCanvas.prototype = { return; } - var scaler = w/fromWin.innerWidth; - - // TODO: Potentially only redraw the dirty rect? (Is it worth it?) - - ctx.save(); - ctx.scale(scaler, scaler); - try{ - ctx.drawWindow(fromWin, fromWin.scrollX, fromWin.scrollY, w/scaler, h/scaler, "#fff"); - } catch(e) { - Utils.error('paint', e); + let tempCanvas = TabItems.tempCanvas; + if (w < tempCanvas.width) { + // Small draw case where nearest-neighbor algorithm breaks down in Windows + // First draw to a larger canvas (150px wide), and then draw that image + // to the destination canvas. + + var tempCtx = tempCanvas.getContext("2d"); + + let canvW = tempCanvas.width; + let canvH = (h/w) * canvW; + + var scaler = canvW/fromWin.innerWidth; + + tempCtx.save(); + tempCtx.clearRect(0,0,tempCanvas.width,tempCanvas.height); + tempCtx.scale(scaler, scaler); + try{ + tempCtx.drawWindow(fromWin, fromWin.scrollX, fromWin.scrollY, + canvW/scaler, canvH/scaler, "#fff"); + } catch(e) { + Utils.error('paint', e); + } + tempCtx.restore(); + + // Now copy to tabitem canvas. No save/restore necessary. + var destCtx = this.canvas.getContext("2d"); + try{ + // the tempcanvas is square, so draw it as a square. + destCtx.drawImage(tempCanvas, 0, 0, w, w); + } catch(e) { + Utils.error('paint', e); + } + + } else { + // General case where nearest neighbor algorithm looks good + // Draw directly to the destination canvas + + var ctx = this.canvas.getContext("2d"); + + var scaler = w/fromWin.innerWidth; + + // TODO: Potentially only redraw the dirty rect? (Is it worth it?) + + ctx.save(); + ctx.scale(scaler, scaler); + try{ + ctx.drawWindow(fromWin, fromWin.scrollX, fromWin.scrollY, w/scaler, h/scaler, "#fff"); + } catch(e) { + Utils.error('paint', e); + } + + ctx.restore(); } - - ctx.restore(); }, // ---------- diff --git a/browser/base/content/tabview/ui.js b/browser/base/content/tabview/ui.js index c3f792173a5e..362e7518df37 100644 --- a/browser/base/content/tabview/ui.js +++ b/browser/base/content/tabview/ui.js @@ -88,6 +88,12 @@ let UI = { // An array of functions to be called at uninit time _cleanupFunctions: [], + // Constant: _maxInteractiveWait + // If the UI is in the middle of an operation, this is the max amount of + // milliseconds to wait between input events before we no longer consider + // the operation interactive. + _maxInteractiveWait: 250, + // Variable: _privateBrowsing // Keeps track of info related to private browsing, including: // transitionStage - what step we're on in entering/exiting PB @@ -171,6 +177,7 @@ let UI = { // ___ Storage + GroupItems.pauseArrange(); GroupItems.init(); let firstTime = true; @@ -221,9 +228,11 @@ let UI = { // initialized. let event = document.createEvent("Events"); event.initEvent("tabviewframeinitialized", true, false); - dispatchEvent(event); + dispatchEvent(event); } catch(e) { Utils.log(e); + } finally { + GroupItems.resumeArrange(); } }, @@ -320,6 +329,18 @@ let UI = { }); }, + // ---------- + // Function: isIdle + // Returns true if the last interaction was long enough ago to consider the + // UI idle. Used to determine whether interactivity would be sacrificed if + // the CPU was to become busy. + // + isIdle: function UI_isIdle() { + let time = Date.now(); + let maxEvent = Math.max(drag.lastMoveTime, resize.lastMoveTime); + return (time - maxEvent) > this._maxInteractiveWait; + }, + // ---------- // Function: getActiveTab // Returns the currently active tab as a @@ -450,6 +471,8 @@ let UI = { if (!this._isTabViewVisible()) return; + // another tab might be select if user decides to stay on a page when + // a onclose confirmation prompts. GroupItems.removeHiddenGroups(); TabItems.pausePainting(); @@ -678,6 +701,7 @@ let UI = { }, // ---------- + // Function: goToTab // Selects the given xul:tab in the browser. goToTab: function UI_goToTab(xulTab) { // If it's not focused, the onFocus listener would handle it. @@ -710,6 +734,11 @@ let UI = { if (this._isTabViewVisible()) this.hideTabView(); + // another tab might be selected when hideTabView() is invoked so a + // validation is needed. + if (this._currentTab != tab) + return; + let oldItem = null; let newItem = null; @@ -814,11 +843,16 @@ let UI = { var self = this; iQ(window).keyup(function(event) { - if (!event.metaKey) Keys.meta = false; + if (!event.metaKey) + Keys.meta = false; }); iQ(window).keydown(function(event) { - if (event.metaKey) Keys.meta = true; + if (event.metaKey) + Keys.meta = true; + + if (isSearchEnabled()) + return; function getClosestTabBy(norm) { if (!self.getActiveTab()) @@ -875,7 +909,7 @@ let UI = { event.keyCode == KeyEvent.DOM_VK_ENTER) { let activeTab = self.getActiveTab(); if (activeTab) - activeTab.zoomIn(); + activeTab.zoomIn(); event.stopPropagation(); event.preventDefault(); @@ -989,11 +1023,12 @@ let UI = { } function collapse() { + let center = phantom.bounds().center(); phantom.animate({ width: 0, height: 0, - top: phantom.position().x + phantom.height()/2, - left: phantom.position().y + phantom.width()/2 + top: center.y, + left: center.x }, { duration: 300, complete: function() { diff --git a/browser/base/content/test/browser_keywordSearch.js b/browser/base/content/test/browser_keywordSearch.js index 4b9b08eed147..38ed7ae48e4b 100644 --- a/browser/base/content/test/browser_keywordSearch.js +++ b/browser/base/content/test/browser_keywordSearch.js @@ -3,37 +3,41 @@ * http://creativecommons.org/publicdomain/zero/1.0/ **/ +var gTests = [ + { + name: "normal search (search service)", + testText: "test search", + searchURL: Services.search.originalDefaultEngine.getSubmission("test search").uri.spec + }, + { + name: "?-prefixed search (search service)", + testText: "? foo ", + searchURL: Services.search.originalDefaultEngine.getSubmission("foo").uri.spec + }, + { + name: "normal search (keyword.url)", + testText: "test search", + keywordURLPref: "http://example.com/?q=", + searchURL: "http://example.com/?q=test+search" + }, + { + name: "?-prefixed search (keyword.url)", + testText: "? foo ", + keywordURLPref: "http://example.com/?q=", + searchURL: "http://example.com/?q=foo" + }, + { + name: "encoding test (keyword.url)", + testText: "test encoded+%/", + keywordURLPref: "http://example.com/?q=", + searchURL: "http://example.com/?q=test+encoded%2B%25%2F" + } +]; + function test() { waitForExplicitFinish(); - let tab = gBrowser.selectedTab = gBrowser.addTab(); - let searchText = "test search"; - - let listener = { - onStateChange: function onLocationChange(webProgress, req, flags, status) { - ok(flags & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT, "only notified for document"); - - // Only care about starts - if (!(flags & Ci.nsIWebProgressListener.STATE_START)) - return; - - ok(req instanceof Ci.nsIChannel, "req is a channel"); - - let searchURL = Services.search.originalDefaultEngine.getSubmission(searchText).uri.spec; - is(req.originalURI.spec, searchURL, "search URL was loaded"); - info("Actual URI: " + req.URI.spec); - - Services.ww.unregisterNotification(observer); - gBrowser.removeProgressListener(this); - executeSoon(function () { - gBrowser.removeTab(tab); - finish(); - }); - } - } - gBrowser.addProgressListener(listener, Ci.nsIWebProgressListener.NOTIFY_STATE_DOCUMENT); - - let observer = { + let windowObserver = { observe: function(aSubject, aTopic, aData) { if (aTopic == "domwindowopened") { ok(false, "Alert window opened"); @@ -42,19 +46,67 @@ function test() { win.removeEventListener("load", arguments.callee, false); win.close(); }, false); - gBrowser.removeProgressListener(listener); - executeSoon(function () { - gBrowser.removeTab(tab); - finish(); - }); + executeSoon(finish); } - Services.ww.unregisterNotification(this); } }; - Services.ww.registerNotification(observer); + + Services.ww.registerNotification(windowObserver); + + let tab = gBrowser.selectedTab = gBrowser.addTab(); + + let listener = { + onStateChange: function onLocationChange(webProgress, req, flags, status) { + // Only care about document starts + let docStart = Ci.nsIWebProgressListener.STATE_IS_DOCUMENT | + Ci.nsIWebProgressListener.STATE_START; + if (!(flags & docStart)) + return; + + info("received document start"); + + ok(req instanceof Ci.nsIChannel, "req is a channel"); + is(req.originalURI.spec, gCurrTest.searchURL, "search URL was loaded"); + info("Actual URI: " + req.URI.spec); + + executeSoon(nextTest); + } + } + gBrowser.addProgressListener(listener); + + registerCleanupFunction(function () { + Services.ww.unregisterNotification(windowObserver); + + gBrowser.removeProgressListener(listener); + gBrowser.removeTab(tab); + }); + + nextTest(); +} + +var gCurrTest; +function nextTest() { + // Clear the pref before every test (and after the last) + try { + Services.prefs.clearUserPref("keyword.URL"); + } catch(ex) {} + + if (gTests.length) { + gCurrTest = gTests.shift(); + doTest(); + } else { + finish(); + } +} + +function doTest() { + info("Running test: " + gCurrTest.name); + + if (gCurrTest.keywordURLPref) + Services.prefs.setCharPref("keyword.URL", gCurrTest.keywordURLPref); // Simulate a user entering search terms - gURLBar.value = searchText; + gURLBar.value = gCurrTest.testText; gURLBar.focus(); EventUtils.synthesizeKey("VK_RETURN", {}); } diff --git a/browser/base/content/test/tabview/Makefile.in b/browser/base/content/test/tabview/Makefile.in index 53c605cc7899..7726115cca8d 100644 --- a/browser/base/content/test/tabview/Makefile.in +++ b/browser/base/content/test/tabview/Makefile.in @@ -48,6 +48,7 @@ _BROWSER_FILES = \ browser_tabview_apptabs.js \ browser_tabview_bug580412.js \ browser_tabview_bug587043.js \ + browser_tabview_bug587231.js \ browser_tabview_bug587990.js \ browser_tabview_bug589324.js \ browser_tabview_bug590606.js \ @@ -59,7 +60,9 @@ _BROWSER_FILES = \ browser_tabview_bug595804.js \ browser_tabview_bug595930.js \ browser_tabview_bug595943.js \ + browser_tabview_bug597399.js \ browser_tabview_bug598600.js \ + browser_tabview_bug599626.js \ browser_tabview_dragdrop.js \ browser_tabview_exit_button.js \ browser_tabview_group.js \ @@ -76,6 +79,7 @@ _BROWSER_FILES = \ head.js \ search1.html \ search2.html \ + test_bug599626.html \ $(NULL) # compartments: test disabled diff --git a/browser/base/content/test/tabview/browser_tabview_bug587231.js b/browser/base/content/test/tabview/browser_tabview_bug587231.js new file mode 100644 index 000000000000..c3e4f4d010d7 --- /dev/null +++ b/browser/base/content/test/tabview/browser_tabview_bug587231.js @@ -0,0 +1,125 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is tabview drag and drop test. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Sean Dunn + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +let activeTab; +let testTab; +let testGroup; +let contentWindow; + +function test() { + waitForExplicitFinish(); + contentWindow = document.getElementById("tab-view").contentWindow; + + // create new tab + testTab = gBrowser.addTab("http://mochi.test:8888/"); + + window.addEventListener("tabviewshown", onTabViewWindowLoaded, false); + TabView.toggle(); +} + +function onTabViewWindowLoaded() { + window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false); + ok(TabView.isVisible(), "Tab View is visible"); + + // create group + let testGroupRect = new contentWindow.Rect(20, 20, 300, 300); + testGroup = new contentWindow.GroupItem([], { bounds: testGroupRect }); + ok(testGroup.isEmpty(), "This group is empty"); + + // place tab in group + let testTabItem = testTab.tabItem; + + if (testTabItem.parent) + testTabItem.parent.remove(testTabItem); + testGroup.add(testTabItem); + + // record last update time of tab canvas + let initialUpdateTime = testTabItem._lastTabUpdateTime; + + // simulate resize + let resizer = contentWindow.iQ('.iq-resizable-handle', testGroup.container)[0]; + let offsetX = 100; + let offsetY = 100; + let delay = 500; + + let funcChain = new Array(); + funcChain.push(function() { + EventUtils.synthesizeMouse( + resizer, 1, 1, { type: "mousedown" }, contentWindow); + setTimeout(funcChain.shift(), delay); + }); + // drag + for (let i = 4; i >= 0; i--) { + funcChain.push(function() { + EventUtils.synthesizeMouse( + resizer, Math.round(offsetX/4), Math.round(offsetY/4), + { type: "mousemove" }, contentWindow); + setTimeout(funcChain.shift(), delay); + }); + } + funcChain.push(function() { + EventUtils.synthesizeMouse(resizer, 0, 0, { type: "mouseup" }, + contentWindow); + setTimeout(funcChain.shift(), delay); + }); + funcChain.push(function() { + // verify that update time has changed after last update + let lastTime = testTabItem._lastTabUpdateTime; + let hbTiming = contentWindow.TabItems._heartbeatTiming; + ok((lastTime - initialUpdateTime) > hbTiming, "Tab has been updated:"+lastTime+"-"+initialUpdateTime+">"+hbTiming); + + // clean up + testGroup.remove(testTab.tabItem); + testTab.tabItem.close(); + testGroup.close(); + + let currentTabs = contentWindow.TabItems.getItems(); + ok(currentTabs[0], "A tab item exists to make active"); + contentWindow.UI.setActiveTab(currentTabs[0]); + + window.addEventListener("tabviewhidden", finishTest, false); + TabView.toggle(); + }); + setTimeout(funcChain.shift(), delay); +} + +function finishTest() { + window.removeEventListener("tabviewhidden", finishTest, false); + ok(!TabView.isVisible(), "Tab View is not visible"); + + finish(); +} diff --git a/browser/base/content/test/tabview/browser_tabview_bug595191.js b/browser/base/content/test/tabview/browser_tabview_bug595191.js index 69bdb4f24da7..e4bc3d066004 100644 --- a/browser/base/content/test/tabview/browser_tabview_bug595191.js +++ b/browser/base/content/test/tabview/browser_tabview_bug595191.js @@ -55,10 +55,10 @@ function onTabViewWindowLoaded() { ok(searchButton, "Search button exists"); let onSearchEnabled = function() { - let search = contentWindow.document.getElementById("search"); - ok(search.style.display != "none", "Search is enabled"); contentWindow.removeEventListener( "tabviewsearchenabled", onSearchEnabled, false); + let search = contentWindow.document.getElementById("search"); + ok(search.style.display != "none", "Search is enabled"); escapeTest(contentWindow); } contentWindow.addEventListener("tabviewsearchenabled", onSearchEnabled, @@ -70,21 +70,16 @@ function onTabViewWindowLoaded() { function escapeTest(contentWindow) { let onSearchDisabled = function() { - let search = contentWindow.document.getElementById("search"); - - ok(search.style.display == "none", "Search is disabled"); - contentWindow.removeEventListener( "tabviewsearchdisabled", onSearchDisabled, false); + + let search = contentWindow.document.getElementById("search"); + ok(search.style.display == "none", "Search is disabled"); toggleTabViewTest(contentWindow); } contentWindow.addEventListener("tabviewsearchdisabled", onSearchDisabled, false); - // the search box focus()es in a function on the timeout queue, so we just - // want to queue behind it. - setTimeout( function() { - EventUtils.synthesizeKey("VK_ESCAPE", {}); - }, 0); + EventUtils.synthesizeKey("VK_ESCAPE", { type: "keypress" }, contentWindow); } function toggleTabViewTest(contentWindow) { @@ -92,14 +87,9 @@ function toggleTabViewTest(contentWindow) { contentWindow.removeEventListener("tabviewhidden", onTabViewHidden, false); ok(!TabView.isVisible(), "Tab View is hidden"); - finish(); } contentWindow.addEventListener("tabviewhidden", onTabViewHidden, false); - // When search is hidden, it focus()es on the background, so avert the - // race condition by delaying ourselves on the timeout queue - setTimeout( function() { - // Use keyboard shortcut to toggle back to browser view - EventUtils.synthesizeKey("e", { accelKey: true }); - }, 0); + // Use keyboard shortcut to toggle back to browser view + EventUtils.synthesizeKey("e", { accelKey: true }); } diff --git a/browser/base/content/test/tabview/browser_tabview_bug597399.js b/browser/base/content/test/tabview/browser_tabview_bug597399.js new file mode 100644 index 000000000000..20b6da68d9bc --- /dev/null +++ b/browser/base/content/test/tabview/browser_tabview_bug597399.js @@ -0,0 +1,82 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is a test for bug 597399. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Raymond Lee + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +function test() { + waitForExplicitFinish(); + + window.addEventListener("tabviewshown", onTabViewWindowLoaded, false); + TabView.toggle(); +} + +function onTabViewWindowLoaded() { + window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false); + + let contentWindow = document.getElementById("tab-view").contentWindow; + let number = -1; + + let onSearchEnabled = function() { + let searchBox = contentWindow.document.getElementById("searchbox"); + is(searchBox.value, number, "The seach box matches the number: " + number); + contentWindow.hideSearch(null); + } + let onSearchDisabled = function() { + if (++number <= 9) { + EventUtils.synthesizeKey(String(number), { }, contentWindow); + } else { + contentWindow.removeEventListener( + "tabviewsearchenabled", onSearchEnabled, false); + contentWindow.removeEventListener( + "tabviewsearchdisabled", onSearchDisabled, false); + + let endGame = function() { + window.removeEventListener("tabviewhidden", endGame, false); + + ok(!TabView.isVisible(), "Tab View is hidden"); + finish(); + } + window.addEventListener("tabviewhidden", endGame, false); + TabView.toggle(); + } + } + contentWindow.addEventListener( + "tabviewsearchenabled", onSearchEnabled, false); + contentWindow.addEventListener( + "tabviewsearchdisabled", onSearchDisabled, false); + + onSearchDisabled(); +} + diff --git a/browser/base/content/test/tabview/browser_tabview_bug599626.js b/browser/base/content/test/tabview/browser_tabview_bug599626.js new file mode 100644 index 000000000000..84abefd70987 --- /dev/null +++ b/browser/base/content/test/tabview/browser_tabview_bug599626.js @@ -0,0 +1,194 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is tabview bug 599626 test. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Raymond Lee + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +let handleDialog; + +function test() { + waitForExplicitFinish(); + + window.addEventListener("tabviewshown", onTabViewWindowLoaded, false); + TabView.toggle(); +} + +function onTabViewWindowLoaded() { + window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false); + + let contentWindow = document.getElementById("tab-view").contentWindow; + let groupItemOne = contentWindow.GroupItems.getActiveGroupItem(); + + // Create a group and make it active + let box = new contentWindow.Rect(10, 10, 300, 300); + let groupItemTwo = new contentWindow.GroupItem([], { bounds: box }); + contentWindow.GroupItems.setActiveGroupItem(groupItemTwo); + + let testTab = + gBrowser.addTab( + "http://mochi.test:8888/browser/browser/base/content/test/tabview/test_bug599626.html"); + let browser = gBrowser.getBrowserForTab(testTab); + let onLoad = function() { + browser.removeEventListener("load", onLoad, true); + + testStayOnPage(contentWindow, groupItemOne, groupItemTwo); + } + browser.addEventListener("load", onLoad, true); +} + +function testStayOnPage(contentWindow, groupItemOne, groupItemTwo) { + setupAndRun(contentWindow, groupItemOne, groupItemTwo, function(doc) { + groupItemTwo.addSubscriber(groupItemTwo, "groupShown", function() { + groupItemTwo.removeSubscriber(groupItemTwo, "groupShown"); + + is(gBrowser.tabs.length, 2, + "The total number of tab is 2 when staying on the page"); + is(contentWindow.TabItems.getItems().length, 2, + "The total number of tab items is 2 when staying on the page"); + + let onTabViewShown = function() { + window.removeEventListener("tabviewshown", onTabViewShown, false); + + // start the next test + testLeavePage(contentWindow, groupItemOne, groupItemTwo); + }; + window.addEventListener("tabviewshown", onTabViewShown, false); + TabView.toggle(); + }); + // stay on page + doc.documentElement.getButton("cancel").click(); + }); +} + +function testLeavePage(contentWindow, groupItemOne, groupItemTwo) { + setupAndRun(contentWindow, groupItemOne, groupItemTwo, function(doc) { + // clean up and finish the test + groupItemTwo.addSubscriber(groupItemTwo, "close", function() { + groupItemTwo.removeSubscriber(groupItemTwo, "close"); + + is(gBrowser.tabs.length, 1, + "The total number of tab is 1 after leaving the page"); + is(contentWindow.TabItems.getItems().length, 1, + "The total number of tab items is 1 after leaving the page"); + + let endGame = function() { + window.removeEventListener("tabviewhidden", endGame, false); + finish(); + }; + window.addEventListener("tabviewhidden", endGame, false); + }); + + // Leave page + doc.documentElement.getButton("accept").click(); + }); +} + +function setupAndRun(contentWindow, groupItemOne, groupItemTwo, callback) { + let closeButton = groupItemTwo.container.getElementsByClassName("close"); + ok(closeButton[0], "Group close button exists"); + // click the close button + EventUtils.sendMouseEvent({ type: "click" }, closeButton[0], contentWindow); + + let onTabViewHidden = function() { + window.removeEventListener("tabviewhidden", onTabViewHidden, false); + + handleDialog = function(doc) { + callback(doc); + }; + startCallbackTimer(); + }; + window.addEventListener("tabviewhidden", onTabViewHidden, false); + + let tabItem = groupItemOne.getChild(0); + tabItem.zoomIn(); +} + +// Copied from http://mxr.mozilla.org/mozilla-central/source/toolkit/components/places/tests/mochitest/prompt_common.js +let observer = { + QueryInterface : function (iid) { + const interfaces = [Ci.nsIObserver, Ci.nsISupports, Ci.nsISupportsWeakReference]; + + if (!interfaces.some( function(v) { return iid.equals(v) } )) + throw Components.results.NS_ERROR_NO_INTERFACE; + return this; + }, + + observe : function (subject, topic, data) { + let doc = getDialogDoc(); + if (doc) + handleDialog(doc); + else + startCallbackTimer(); // try again in a bit + } +}; + +function startCallbackTimer() { + // Delay before the callback twiddles the prompt. + const dialogDelay = 10; + + // Use a timer to invoke a callback to twiddle the authentication dialog + let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + timer.init(observer, dialogDelay, Ci.nsITimer.TYPE_ONE_SHOT); +} + +function getDialogDoc() { + // Find the which contains notifyWindow, by looking + // through all the open windows and all the in each. + let wm = Cc["@mozilla.org/appshell/window-mediator;1"]. + getService(Ci.nsIWindowMediator); + let enumerator = wm.getXULWindowEnumerator(null); + + while (enumerator.hasMoreElements()) { + let win = enumerator.getNext(); + let windowDocShell = win.QueryInterface(Ci.nsIXULWindow).docShell; + + let containedDocShells = windowDocShell.getDocShellEnumerator( + Ci.nsIDocShellTreeItem.typeChrome, + Ci.nsIDocShell.ENUMERATE_FORWARDS); + while (containedDocShells.hasMoreElements()) { + // Get the corresponding document for this docshell + let childDocShell = containedDocShells.getNext(); + // We don't want it if it's not done loading. + if (childDocShell.busyFlags != Ci.nsIDocShell.BUSY_FLAGS_NONE) + continue; + let childDoc = childDocShell.QueryInterface(Ci.nsIDocShell). + contentViewer.DOMDocument; + + if (childDoc.location.href == "chrome://global/content/commonDialog.xul") + return childDoc; + } + } + + return null; +} diff --git a/browser/base/content/test/tabview/test_bug599626.html b/browser/base/content/test/tabview/test_bug599626.html new file mode 100644 index 000000000000..590276e6aada --- /dev/null +++ b/browser/base/content/test/tabview/test_bug599626.html @@ -0,0 +1,10 @@ + + + + Test page + + diff --git a/browser/base/content/urlbarBindings.xml b/browser/base/content/urlbarBindings.xml index 55acac66f865..d6a4a4bf5c05 100644 --- a/browser/base/content/urlbarBindings.xml +++ b/browser/base/content/urlbarBindings.xml @@ -200,6 +200,7 @@ + + + + + + + + + + + + + diff --git a/browser/components/places/tests/browser/browser_sidebarpanels_click.js b/browser/components/places/tests/browser/browser_sidebarpanels_click.js index 026548142059..a77baf06adbb 100644 --- a/browser/components/places/tests/browser/browser_sidebarpanels_click.js +++ b/browser/components/places/tests/browser/browser_sidebarpanels_click.js @@ -45,8 +45,8 @@ function test() { const HISTORY_SIDEBAR_TREE_ID = "historyTree"; // Initialization. - let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"]. - getService(Ci.nsIWindowWatcher); + let os = Cc["@mozilla.org/observer-service;1"]. + getService(Ci.nsIObserverService); let bs = PlacesUtils.bookmarks; let hs = PlacesUtils.history; let sidebarBox = document.getElementById("sidebar-box"); @@ -125,22 +125,20 @@ function test() { preFunc(); function observer(aSubject, aTopic, aData) { - if (aTopic != "domwindowopened") - return; - ww.unregisterNotification(observer); - let alertDialog = aSubject.QueryInterface(Ci.nsIDOMWindow); - alertDialog.addEventListener("load", function () { - alertDialog.removeEventListener("load", arguments.callee, false); - info("alert dialog observed as expected"); - executeSoon(function () { - alertDialog.close(); + info("alert dialog observed as expected"); + os.removeObserver(observer, "common-dialog-loaded"); + os.removeObserver(observer, "tabmodal-dialog-loaded"); + + aSubject.Dialog.ui.button0.click(); + + executeSoon(function () { toggleSidebar(currentTest.sidebarName); currentTest.cleanup(); postFunc(); }); - }, false); } - ww.registerNotification(observer); + os.addObserver(observer, "common-dialog-loaded", false); + os.addObserver(observer, "tabmodal-dialog-loaded", false); // Select the inserted places item. currentTest.selectNode(tree); @@ -160,7 +158,7 @@ function test() { y = y.value + height.value / 2; // Simulate the click. EventUtils.synthesizeMouse(tree.body, x, y, {}, doc.defaultView); - // Now, wait for the domwindowopened observer to catch the alert dialog. + // Now, wait for the observer to catch the alert dialog. // If something goes wrong, the test will time out at this stage. // Note that for the history sidebar, the URL itself is not opened, // and Places will show the load-js-data-url-error prompt as an alert diff --git a/browser/components/sessionstore/test/browser/browser_586068-cascaded_restore.js b/browser/components/sessionstore/test/browser/browser_586068-cascaded_restore.js index 3d6432aefe84..b2c8a7f64365 100644 --- a/browser/components/sessionstore/test/browser/browser_586068-cascaded_restore.js +++ b/browser/components/sessionstore/test/browser/browser_586068-cascaded_restore.js @@ -69,10 +69,20 @@ function runNextTest() { // set an empty state & run the next test, or finish if (tests.length) { + // Enumerate windows and close everything but our primary window. We can't + // use waitForFocus() because apparently it's buggy. See bug 599253. + var windowsEnum = Services.wm.getEnumerator("navigator:browser"); + while (windowsEnum.hasMoreElements()) { + var currentWindow = windowsEnum.getNext(); + if (currentWindow != window) { + currentWindow.close(); + } + } + ss.setBrowserState(JSON.stringify({ windows: [{ tabs: [{ url: 'about:blank' }] }] })); - let test = tests.shift(); - info("running " + test.name); - executeSoon(test); + let currentTest = tests.shift(); + info("running " + currentTest.name); + executeSoon(currentTest); } else { ss.setBrowserState(stateBackup); diff --git a/browser/themes/winstripe/browser/browser-aero.css b/browser/themes/winstripe/browser/browser-aero.css index bb0419952fc3..df3dcb5aeaa2 100644 --- a/browser/themes/winstripe/browser/browser-aero.css +++ b/browser/themes/winstripe/browser/browser-aero.css @@ -105,7 +105,7 @@ margin-top: -1px; } - #main-window[sizemode="normal"] > #titlebar > #titlebar-content > #titlebar-buttonbox:-moz-lwtheme { + #main-window[sizemode="normal"] #titlebar-buttonbox:-moz-lwtheme { margin-top: -2px; } diff --git a/browser/themes/winstripe/browser/browser.css b/browser/themes/winstripe/browser/browser.css index 227940dc9aa6..44786e53c8a5 100644 --- a/browser/themes/winstripe/browser/browser.css +++ b/browser/themes/winstripe/browser/browser.css @@ -325,7 +325,7 @@ -moz-appearance: -moz-window-button-box; } -#main-window[sizemode="maximized"] > #titlebar > #titlebar-content > #titlebar-buttonbox { +#main-window[sizemode="maximized"] #titlebar-buttonbox { -moz-appearance: -moz-window-button-box-maximized; } @@ -339,7 +339,7 @@ -moz-appearance: -moz-window-button-maximize; } -#main-window[sizemode="maximized"] > #titlebar > #titlebar-content > #titlebar-buttonbox > #titlebar-max { +#main-window[sizemode="maximized"] #titlebar-max { -moz-appearance: -moz-window-button-restore; } @@ -1412,10 +1412,6 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action- padding-bottom: 1px; } -.tabbrowser-tabs:-moz-system-metric(touch-enabled) { - min-height: 7.3mozmm; -} - /* Tabs */ .tabbrowser-tab, .tabs-newtab-button { @@ -1558,6 +1554,10 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action- .tabs-newtab-button { min-width: 10mozmm; } + + .tab-content { + min-height: -moz-calc(6.8mozmm - 7px); /* subtract borders from the desired height */ + } } .tabbrowser-arrowscrollbox > .scrollbutton-up, diff --git a/build/mobile/devicemanager.py b/build/mobile/devicemanager.py index 712fcb304bbb..8dbf4f8bb636 100644 --- a/build/mobile/devicemanager.py +++ b/build/mobile/devicemanager.py @@ -37,6 +37,7 @@ # ***** END LICENSE BLOCK ***** import socket +import SocketServer import time, datetime import os import re @@ -58,14 +59,14 @@ class FileError(Exception): class DeviceManager: host = '' port = 0 - debug = 2 + debug = 2 _redo = False deviceRoot = None tempRoot = os.getcwd() base_prompt = '\$\>' prompt_sep = '\x00' prompt_regex = '.*' + base_prompt + prompt_sep - agentErrorRE = re.compile('^##AGENT-ERROR##.*') + agentErrorRE = re.compile('^##AGENT-WARNING##.*') def __init__(self, host, port = 20701): self.host = host @@ -272,7 +273,10 @@ class DeviceManager: return None def mkDir(self, name): - return self.sendCMD(['mkdr ' + name]) + if (self.dirExists(name)): + return name + else: + return self.sendCMD(['mkdr ' + name]) # make directory structure on the device def mkDirs(self, filename): @@ -306,7 +310,9 @@ class DeviceManager: match = ".*" + dirname + "$" dirre = re.compile(match) data = self.sendCMD(['cd ' + dirname, 'cwd']) - if (data == None): + # Because this is a compound command, cd can fail while cwd can succeed, + # we should check for agent error directly + if (data == None or self.agentErrorRE.match(data) ): return None retVal = self.stripPrompt(data) data = retVal.split('\n') @@ -549,11 +555,12 @@ class DeviceManager: # /reftest # /mochitest def getDeviceRoot(self): - if (not self.deviceRoot): - data = self.sendCMD(['testroot']) - if (data == None): - return '/tests' - self.deviceRoot = self.stripPrompt(data).strip('\n') + '/tests' + # This caching of deviceRoot is causing issues if things fail + # if (not self.deviceRoot): + data = self.sendCMD(['testroot']) + if (data == None): + return '/tests' + self.deviceRoot = self.stripPrompt(data).strip('\n') + '/tests' if (not self.dirExists(self.deviceRoot)): self.mkDir(self.deviceRoot) @@ -690,8 +697,8 @@ class DeviceManager: Application bundle - path to the application bundle on the device Destination - destination directory of where application should be installed to (optional) - Returns True or False depending on what we get back - TODO: we need a real way to know if this works or not + Returns None for success, or output if known failure + TODO: we need a better way to know if this works or not """ def installApp(self, appBundlePath, destPath=None): cmd = 'inst ' + appBundlePath @@ -699,9 +706,13 @@ class DeviceManager: cmd += ' ' + destPath data = self.sendCMD([cmd]) if (data is None): - return False - else: - return True + return None + + f = re.compile('Failure') + for line in data.split(): + if (f.match(line)): + return data + return None """ Uninstalls the named application from device and causes a reboot. @@ -714,9 +725,53 @@ class DeviceManager: cmd = 'uninst ' + appName if installPath: cmd += ' ' + installPath - self.sendCMD([cmd]) + data = self.sendCMD([cmd]) + if (self.debug > 3): print "uninstallAppAndReboot: " + str(data) return True + """ + Updates the application on the device. + Application bundle - path to the application bundle on the device + Process name of application - used to end the process if the applicaiton is + currently running + Destination - Destination directory to where the application should be + installed (optional) + ipAddr - IP address to await a callback ping to let us know that the device has updated + properly - defaults to current IP. + port - port to await a callback ping to let us know that the device has updated properly + defaults to 30000, and counts up from there if it finds a conflict + Returns True if succeeds, False if not + + NOTE: We have no real way to know if the device gets updated or not due to the + reboot that the udpate call forces on us. We can't install our own heartbeat + listener here because we run the risk of racing with other heartbeat listeners. + """ + def updateApp(self, appBundlePath, processName=None, destPath=None, ipAddr=None, port=None): + status = None + cmd = 'updt ' + if (processName == None): + # Then we pass '' for processName + cmd += "'' " + appBundlePath + else: + cmd += processName + ' ' + appBundlePath + + if (destPath): + cmd += " " + destPath + + ip, port = self.getCallbackIpAndPort(ipAddr, 30000) + + cmd += " %s %s" % (ip, port) + + if (self.debug > 3): print "updateApp using command: " + str(cmd) + + # Set up our callback server + callbacksvr = callbackServer(ip, port, self.debug) + data = self.sendCMD([cmd]) + status = callbacksvr.disconnect() + if (self.debug > 3): print "got status back: " + str(status) + + return status + """ return the current time on the device """ @@ -726,3 +781,112 @@ class DeviceManager: return None return self.stripPrompt(data).strip('\n') + """ + Connect the ipaddress and port for a callback ping. Defaults to current IP address + And ports starting at 30000. + NOTE: the detection for current IP address only works on Linux! + """ + def getCallbackIpAndPort(self, aIp, aPort): + ip = aIp + nettools = NetworkTools() + if (ip == None): + ip = nettools.getLanIp() + if (aPort != None): + port = nettools.findOpenPort(ip, aPort) + else: + port = nettools.findOpenPort(ip, 30000) + return ip, port + +gCallbackData = '' + +class callbackServer(): + def __init__(self, ip, port, debuglevel): + self.ip = ip + self.port = port + self.connected = False + self.debug = debuglevel + if (self.debug > 3) : print "Creating server with " + str(ip) + ":" + str(port) + self.server = SocketServer.TCPServer((ip, port), self.myhandler) + self.server_thread = Thread(target=self.server.serve_forever) + self.server_thread.setDaemon(True) + self.server_thread.start() + + def disconnect(self, step = 60, timeout = 600): + t = 0 + if (self.debug > 3): print "Calling disconnect on callback server" + while t < timeout: + if (gCallbackData): + # Got the data back + if (self.debug > 3): print "Got data back from agent: " + str(gCallbackData) + break + time.sleep(step) + t += step + + try: + if (self.debug > 3): print "Shutting down server now" + self.server.shutdown() + except: + print "Unable to shutdown callback server - check for a connection on port: " + str(self.port) + return gCallbackData + + class myhandler(SocketServer.BaseRequestHandler): + def handle(self): + global gCallbackData + gCallbackData = self.request.recv(1024) + #print "Callback Handler got data: " + str(gCallbackData) + self.request.send("OK") + +class NetworkTools: + def __init__(self): + pass + + # Utilities to get the local ip address + def getInterfaceIp(self, ifname): + if os.name != "nt": + import fcntl + import struct + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + return socket.inet_ntoa(fcntl.ioctl( + s.fileno(), + 0x8915, # SIOCGIFADDR + struct.pack('256s', ifname[:15]) + )[20:24]) + else: + return None + + def getLanIp(self): + ip = socket.gethostbyname(socket.gethostname()) + if ip.startswith("127.") and os.name != "nt": + interfaces = ["eth0","eth1","eth2","wlan0","wlan1","wifi0","ath0","ath1","ppp0"] + for ifname in interfaces: + try: + ip = self.getInterfaceIp(ifname) + break; + except IOError: + pass + return ip + + # Gets an open port starting with the seed by incrementing by 1 each time + def findOpenPort(self, ip, seed): + try: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + connected = False + if isinstance(seed, basestring): + seed = int(seed) + maxportnum = seed + 5000 # We will try at most 5000 ports to find an open one + while not connected: + try: + s.bind((ip, seed)) + connected = True + s.close() + except: + if seed > maxportnum: + print "Could not find open port after checking 5000 ports" + raise + seed += 1 + except: + print "Socket error trying to find open port" + + return seed + diff --git a/build/mobile/remoteautomation.py b/build/mobile/remoteautomation.py index 138033f30bad..846d24ffa77d 100644 --- a/build/mobile/remoteautomation.py +++ b/build/mobile/remoteautomation.py @@ -42,7 +42,7 @@ import os import socket from automation import Automation -from devicemanager import DeviceManager +from devicemanager import DeviceManager, NetworkTools class RemoteAutomation(Automation): _devicemanager = None @@ -99,31 +99,9 @@ class RemoteAutomation(Automation): # return app, ['--environ:NO_EM_RESTART=1'] + args return app, args - # Utilities to get the local ip address - def getInterfaceIp(self, ifname): - if os.name != "nt": - import fcntl - import struct - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - return socket.inet_ntoa(fcntl.ioctl( - s.fileno(), - 0x8915, # SIOCGIFADDR - struct.pack('256s', ifname[:15]) - )[20:24]) - else: - return None - def getLanIp(self): - ip = socket.gethostbyname(socket.gethostname()) - if ip.startswith("127.") and os.name != "nt": - interfaces = ["eth0","eth1","eth2","wlan0","wlan1","wifi0","ath0","ath1","ppp0"] - for ifname in interfaces: - try: - ip = self.getInterfaceIp(ifname) - break; - except IOError: - pass - return ip + nettools = NetworkTools() + return nettools.getLanIp() def Process(self, cmd, stdout = None, stderr = None, env = None, cwd = '.'): if stdout == None or stdout == -1: @@ -176,4 +154,3 @@ class RemoteAutomation(Automation): def kill(self): self.dm.killProcess(self.procName) - diff --git a/build/unix/gnu-ld-scripts/components-mapfile b/build/unix/gnu-ld-scripts/components-mapfile index e9286d711609..afa463230671 100644 --- a/build/unix/gnu-ld-scripts/components-mapfile +++ b/build/unix/gnu-ld-scripts/components-mapfile @@ -1,5 +1,6 @@ { global: NSModule; + NSGetModule; local: *; }; diff --git a/build/unix/gnu-ld-scripts/components-version-script b/build/unix/gnu-ld-scripts/components-version-script index 1fb7fc8e859c..6487671a4498 100644 --- a/build/unix/gnu-ld-scripts/components-version-script +++ b/build/unix/gnu-ld-scripts/components-version-script @@ -1,5 +1,6 @@ EXPORTED { global: NSModule; + NSGetModule; local: *; }; diff --git a/build/unix/gnu-ld-scripts/jemalloc-standalone-linkage-version-script b/build/unix/gnu-ld-scripts/jemalloc-standalone-linkage-version-script new file mode 100644 index 000000000000..59b3a4c1e669 --- /dev/null +++ b/build/unix/gnu-ld-scripts/jemalloc-standalone-linkage-version-script @@ -0,0 +1,19 @@ +{ + global: + _malloc_postfork; + _malloc_prefork; + jemalloc_stats; + malloc_usable_size; + posix_memalign; + free; + realloc; + calloc; + malloc; + memalign; + valloc; + __free_hook; + __malloc_hook; + __realloc_hook; + __memalign_hook; + local: *; +}; diff --git a/caps/src/nsScriptSecurityManager.cpp b/caps/src/nsScriptSecurityManager.cpp index 273c1b09a3b7..ebe11885d216 100644 --- a/caps/src/nsScriptSecurityManager.cpp +++ b/caps/src/nsScriptSecurityManager.cpp @@ -106,12 +106,6 @@ nsIStringBundle *nsScriptSecurityManager::sStrBundle = nsnull; JSRuntime *nsScriptSecurityManager::sRuntime = 0; PRBool nsScriptSecurityManager::sStrictFileOriginPolicy = PR_TRUE; -// Info we need about the JSClasses used by XPConnects wrapped -// natives, to avoid having to QI to nsIXPConnectWrappedNative all the -// time when doing security checks. -static JSEqualityOp sXPCWrappedNativeEqualityOps; - - /////////////////////////// // Convenience Functions // /////////////////////////// @@ -2414,11 +2408,8 @@ nsScriptSecurityManager::doGetObjectPrincipal(JSObject *aObj do { // Note: jsClass is set before this loop, and also at the // *end* of this loop. - - // NOTE: These class and equality hook checks better match - // what IS_WRAPPER_CLASS() does in xpconnect! - if (jsClass->ext.equality == js::Valueify(sXPCWrappedNativeEqualityOps)) { + if (IS_WRAPPER_CLASS(jsClass)) { result = sXPConnect->GetPrincipal(aObj, #ifdef DEBUG aAllowShortCircuit @@ -3423,7 +3414,6 @@ nsresult nsScriptSecurityManager::Init() JS_SetRuntimeSecurityCallbacks(sRuntime, &securityCallbacks); NS_ASSERTION(!oldcallbacks, "Someone else set security callbacks!"); - sXPConnect->GetXPCWrappedNativeJSClassInfo(&sXPCWrappedNativeEqualityOps); return NS_OK; } diff --git a/caps/tests/Makefile.in b/caps/tests/Makefile.in index 817c101472d8..f78ff7f45041 100644 --- a/caps/tests/Makefile.in +++ b/caps/tests/Makefile.in @@ -43,6 +43,6 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk -DIRS = mochitest browser +DIRS = mochitest include $(topsrcdir)/config/rules.mk diff --git a/caps/tests/browser/Makefile.in b/caps/tests/browser/Makefile.in deleted file mode 100644 index 7a4c6030dbb0..000000000000 --- a/caps/tests/browser/Makefile.in +++ /dev/null @@ -1,54 +0,0 @@ -# -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# http://www.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# the Mozilla Foundation. -# Portions created by the Initial Developer are Copyright (C) 2010 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Justin Lebar -# -# Alternatively, the contents of this file may be used under the terms of -# either of the GNU General Public License Version 2 or later (the "GPL"), -# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -DEPTH = ../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ -relativesrcdir = caps/tests/browser - -include $(DEPTH)/config/autoconf.mk - -include $(topsrcdir)/config/rules.mk - -_BROWSER_TEST_FILES = \ - browser_bug571289.js \ - $(NULL) - -libs:: $(_BROWSER_TEST_FILES) - $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir) diff --git a/caps/tests/browser/browser_bug571289.js b/caps/tests/browser/browser_bug571289.js deleted file mode 100644 index fb3967486bd0..000000000000 --- a/caps/tests/browser/browser_bug571289.js +++ /dev/null @@ -1,24 +0,0 @@ -function test() { - // Access an xpcom component from off the main thread. - - let threadman = Components.classes["@mozilla.org/thread-manager;1"]. - getService(Ci.nsIThreadManager); - let thread = threadman.newThread(0); - - thread.dispatch( - function() { - // Get another handle on the thread manager, this time from within the - // thread we created. - let threadman2 = Components.classes["@mozilla.org/thread-manager;1"]. - getService(Ci.nsIThreadManager); - - // Now do something with the thread manager. It doesn't really matter - // what we do. - if (threadman2.mainThread == threadman2.currentThread) - ok(false, "Shouldn't be on the main thread."); - - }, 1); // dispatch sync - - thread.shutdown(); - ok(true, "Done!"); -} diff --git a/config/autoconf.mk.in b/config/autoconf.mk.in index 72df00d7df49..78ffd066ec59 100644 --- a/config/autoconf.mk.in +++ b/config/autoconf.mk.in @@ -171,6 +171,7 @@ VPX_ASM_SUFFIX = @VPX_ASM_SUFFIX@ VPX_X86_ASM = @VPX_X86_ASM@ VPX_ARM_ASM = @VPX_ARM_ASM@ NS_PRINTING = @NS_PRINTING@ +MOZ_PDF_PRINTING = @MOZ_PDF_PRINTING@ MOZ_CRASHREPORTER = @MOZ_CRASHREPORTER@ MOZ_HELP_VIEWER = @MOZ_HELP_VIEWER@ MOC= @MOC@ @@ -195,6 +196,7 @@ RM = rm -f MOZ_UI_LOCALE = @MOZ_UI_LOCALE@ MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS = @MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS@ +MOZ_JEMALLOC_STANDALONE_GLUE_LDOPTS = @MOZ_JEMALLOC_STANDALONE_GLUE_LDOPTS@ MOZ_COMPONENT_NSPR_LIBS=@MOZ_COMPONENT_NSPR_LIBS@ MOZ_FIX_LINK_PATHS=@MOZ_FIX_LINK_PATHS@ diff --git a/config/config.mk b/config/config.mk index 95232c01bfdc..85698d62fb1b 100644 --- a/config/config.mk +++ b/config/config.mk @@ -162,6 +162,11 @@ MOZ_WIDGET_SUPPORT_LIBS = $(DIST)/lib/$(LIB_PREFIX)widgetsupport_s.$(LIB_SUFF ifdef MOZ_MEMORY ifneq (,$(filter-out WINNT WINCE,$(OS_ARCH))) JEMALLOC_LIBS = $(MKSHLIB_FORCE_ALL) $(call EXPAND_MOZLIBNAME,jemalloc) $(MKSHLIB_UNFORCE_ALL) +# If we are linking jemalloc into a program, we want the jemalloc symbols +# to be exported +ifneq (,$(SIMPLE_PROGRAMS)$(PROGRAM)) +JEMALLOC_LIBS += $(MOZ_JEMALLOC_STANDALONE_GLUE_LDOPTS) +endif endif endif diff --git a/configure.in b/configure.in index 35401e77c334..0646e498b78e 100644 --- a/configure.in +++ b/configure.in @@ -301,8 +301,8 @@ if test "$target" = "arm-android-eabi" ; then STRIP="$android_toolchain"/bin/arm-eabi-strip CPPFLAGS="-I$android_platform/usr/include $CPPFLAGS" - CFLAGS="-mandroid -I$android_platform/usr/include -msoft-float -fno-short-enums -fno-exceptions -march=armv5te -mthumb-interwork $CFLAGS" - CXXFLAGS="-mandroid -I$android_platform/usr/include -msoft-float -fno-short-enums -fno-exceptions -march=armv5te -mthumb-interwork $CXXFLAGS" + CFLAGS="-mandroid -I$android_platform/usr/include -msoft-float -fno-short-enums -fno-exceptions $CFLAGS" + CXXFLAGS="-mandroid -I$android_platform/usr/include -msoft-float -fno-short-enums -fno-exceptions $CXXFLAGS" dnl Add -llog by default, since we use it all over the place. dnl Add --allow-shlib-undefined, because libGLESv2 links to an @@ -2415,7 +2415,7 @@ ia64*-hpux*) LIBXUL_LIBS='$(LIBXUL_DIST)/lib/xpcom.lib $(LIBXUL_DIST)/lib/xul.lib $(LIBXUL_DIST)/lib/mozalloc.lib' MOZ_COMPONENT_NSPR_LIBS='$(NSPR_LIBS)' if test $_MSC_VER -ge 1400; then - LDFLAGS="$LDFLAGS -NXCOMPAT" + LDFLAGS="$LDFLAGS -LARGEADDRESSAWARE -NXCOMPAT" dnl For profile-guided optimization PROFILE_GEN_CFLAGS="-GL" PROFILE_GEN_LDFLAGS="-LTCG:PGINSTRUMENT" @@ -2893,11 +2893,13 @@ esac AC_SUBST(NO_LD_ARCHIVE_FLAGS) dnl ======================================================== -dnl = Flags to strip unused symbols from .so components +dnl = Flags to strip unused symbols from .so components and +dnl = to export jemalloc symbols when linking a program dnl ======================================================== case "$target" in *-linux*|*-kfreebsd*-gnu|*-gnu*) MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS='-Wl,--version-script -Wl,$(BUILD_TOOLS)/gnu-ld-scripts/components-version-script' + MOZ_JEMALLOC_STANDALONE_GLUE_LDOPTS='-rdynamic -Wl,--version-script -Wl,$(BUILD_TOOLS)/gnu-ld-scripts/jemalloc-standalone-linkage-version-script' ;; *-solaris*) if test -z "$GNU_CC"; then @@ -4992,6 +4994,7 @@ MOZ_XTF=1 MOZ_XUL=1 MOZ_ZIPWRITER=1 NS_PRINTING=1 +MOZ_PDF_PRINTING= MOZ_DISABLE_DOMCRYPTO= NSS_DISABLE_DBM= NECKO_WIFI=1 @@ -5031,7 +5034,6 @@ case "${target}" in NSS_DISABLE_DBM=1 USE_ARM_KUSER=1 MOZ_INSTALLER= - NS_PRINTING= NECKO_WIFI= MOZ_THUMB2=1 MOZ_THEME_FASTSTRIPE=1 @@ -5187,6 +5189,7 @@ cairo-windows) NS_PRINTING= ;; esac + MOZ_PDF_PRINTING=1 ;; cairo-gtk2|cairo-gtk2-x11) @@ -5203,6 +5206,7 @@ cairo-gtk2|cairo-gtk2-x11) TK_CFLAGS='$(MOZ_GTK2_CFLAGS)' TK_LIBS='$(MOZ_GTK2_LIBS)' AC_DEFINE(MOZ_WIDGET_GTK2) + MOZ_PDF_PRINTING=1 ;; cairo-gtk2-dfb) @@ -5220,6 +5224,7 @@ cairo-gtk2-dfb) AC_MSG_WARN([Disabling X when DirectFB is specified.]) no_x=yes fi + MOZ_PDF_PRINTING=1 ;; cairo-qt) @@ -5237,6 +5242,7 @@ cairo-qt) TK_CFLAGS='$(MOZ_QT_CFLAGS)' TK_LIBS='$(MOZ_QT_LIBS)' AC_DEFINE(MOZ_WIDGET_QT) + MOZ_PDF_PRINTING=1 ;; cairo-beos) @@ -5251,6 +5257,7 @@ cairo-os2) USE_FC_FREETYPE=1 TK_CFLAGS='$(MOZ_CAIRO_CFLAGS)' TK_LIBS='$(MOZ_CAIRO_LIBS)' + MOZ_PDF_PRINTING=1 ;; cairo-cocoa) @@ -5271,11 +5278,20 @@ cairo-cocoa) cairo-android) AC_DEFINE(MOZ_WIDGET_ANDROID) MOZ_WIDGET_TOOLKIT=android + TK_CFLAGS='$(MOZ_CAIRO_CFLAGS)' + TK_LIBS='$(MOZ_CAIRO_LIBS)' MOZ_WEBGL=1 + MOZ_PDF_PRINTING=1 ;; esac +AC_SUBST(MOZ_PDF_PRINTING) +if test "$MOZ_PDF_PRINTING"; then + PDF_SURFACE_FEATURE="#define CAIRO_HAS_PDF_SURFACE 1" + AC_DEFINE(MOZ_PDF_PRINTING) +fi + if test "$MOZ_ENABLE_XREMOTE"; then AC_DEFINE(MOZ_ENABLE_XREMOTE) fi @@ -7098,6 +7114,16 @@ if test -n "$MOZ_THUMB2"; then AC_MSG_ERROR([--enable-thumb2 is not supported for non-ARM CPU architectures]) ;; esac +else + case "$target_cpu" in + arm*) + if test "$GNU_CC"; then + CFLAGS="$CFLAGS -march=armv5te -mthumb-interwork -Wa, -march=armv5te -Wa, -mthumb-interwork" + CXXFLAGS="$CXXFLAGS -march=armv5te -mthumb-interwork -Wa, -march=armv5te -Wa, -mthumb-interwork" + ASFLAGS="$ASFLAGS -march=armv5te -mthumb-interwork" + fi + ;; + esac fi AC_SUBST(MOZ_THUMB2) @@ -8499,7 +8525,6 @@ if test "$MOZ_TREE_CAIRO"; then XLIB_SURFACE_FEATURE="#define CAIRO_HAS_XLIB_SURFACE 1" XLIB_XRENDER_SURFACE_FEATURE="#define CAIRO_HAS_XLIB_XRENDER_SURFACE 1" PS_SURFACE_FEATURE="#define CAIRO_HAS_PS_SURFACE 1" - PDF_SURFACE_FEATURE="#define CAIRO_HAS_PDF_SURFACE 1" FT_FONT_FEATURE="#define CAIRO_HAS_FT_FONT 1" MOZ_ENABLE_CAIRO_FT=1 CAIRO_FT_CFLAGS="$FT2_CFLAGS" @@ -8538,12 +8563,10 @@ if test "$MOZ_TREE_CAIRO"; then AC_CHECK_HEADER(d3d10.h, MOZ_ENABLE_D3D10_LAYER=1) fi - PDF_SURFACE_FEATURE="#define CAIRO_HAS_PDF_SURFACE 1" fi if test "$MOZ_WIDGET_TOOLKIT" = "os2"; then OS2_SURFACE_FEATURE="#define CAIRO_HAS_OS2_SURFACE 1" FT_FONT_FEATURE="#define CAIRO_HAS_FT_FONT 1" - PDF_SURFACE_FEATURE="#define CAIRO_HAS_PDF_SURFACE 1" MOZ_ENABLE_CAIRO_FT=1 CAIRO_FT_CFLAGS="-I${MZFTCFGFT2}/include" CAIRO_FT_LIBS="-L${MZFTCFGFT2}/lib -lmozft -lmzfntcfg" @@ -8924,6 +8947,7 @@ AC_SUBST(PKG_SKIP_STRIP) AC_SUBST(USE_ELF_DYNSTR_GC) AC_SUBST(INCREMENTAL_LINKER) AC_SUBST(MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS) +AC_SUBST(MOZ_JEMALLOC_STANDALONE_GLUE_LDOPTS) AC_SUBST(MOZ_COMPONENT_NSPR_LIBS) AC_SUBST(MOZ_FIX_LINK_PATHS) diff --git a/content/base/crashtests/606729-1.html b/content/base/crashtests/606729-1.html new file mode 100644 index 000000000000..c81479c20b2d --- /dev/null +++ b/content/base/crashtests/606729-1.html @@ -0,0 +1 @@ + diff --git a/content/base/crashtests/crashtests.list b/content/base/crashtests/crashtests.list index b8d4b5f08710..a8e22a8632f0 100644 --- a/content/base/crashtests/crashtests.list +++ b/content/base/crashtests/crashtests.list @@ -74,3 +74,4 @@ load 582601.html load 575462.svg load 595606-1.html load 595606-2.html +load 606729-1.html diff --git a/content/base/public/nsIContentUtils.h b/content/base/public/nsIContentUtils.h index 196d6bbd6715..34320650318f 100644 --- a/content/base/public/nsIContentUtils.h +++ b/content/base/public/nsIContentUtils.h @@ -42,6 +42,8 @@ #include "nsAString.h" #include "nsMargin.h" +class nsIInterfaceRequestor; + // {3682DD99-8560-44f4-9B8F-CCCE9D7B96FB} #define NS_ICONTENTUTILS_IID \ { 0x3682dd99, 0x8560, 0x44f4, \ @@ -71,4 +73,20 @@ public: NS_DEFINE_STATIC_IID_ACCESSOR(nsIContentUtils, NS_ICONTENTUTILS_IID) +// {60083ad4-f7ed-488b-a706-bacb378fe1a5} +#define NS_ICONTENTUTILS2_IID \ +{ 0x60083ad4, 0xf7ed, 0x488b, \ +{ 0xa7, 0x06, 0xba, 0xcb, 0x37, 0x8f, 0xe1, 0xa5 } } + +class nsIContentUtils2 : public nsISupports +{ +public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICONTENTUTILS2_IID) + NS_DECL_ISUPPORTS + + virtual nsIInterfaceRequestor* GetSameOriginChecker(); +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIContentUtils2, NS_ICONTENTUTILS2_IID) + #endif /* nsIContentUtils_h__ */ diff --git a/content/base/public/nsIScriptElement.h b/content/base/public/nsIScriptElement.h index ded56f833142..2e65e514c34e 100644 --- a/content/base/public/nsIScriptElement.h +++ b/content/base/public/nsIScriptElement.h @@ -70,6 +70,7 @@ public: mFrozen(PR_FALSE), mDefer(PR_FALSE), mAsync(PR_FALSE), + mExternal(PR_FALSE), mParserCreated(aFromParser == mozilla::dom::FROM_PARSER_FRAGMENT ? mozilla::dom::NOT_FROM_PARSER : aFromParser), // Fragment parser-created scripts (if executable) @@ -126,6 +127,15 @@ public: return mAsync; } + /** + * Is the script an external script? + */ + PRBool GetScriptExternal() + { + NS_PRECONDITION(mFrozen, "Not ready for this call yet!"); + return mExternal; + } + /** * Returns how the element was created. */ @@ -249,6 +259,12 @@ protected: */ PRPackedBool mAsync; + /** + * The effective externalness. A script can be external with mUri being null + * if the src attribute contained an invalid URL string. + */ + PRPackedBool mExternal; + /** * Whether this element was parser-created. */ diff --git a/content/base/public/nsIScriptLoader.idl b/content/base/public/nsIScriptLoader.idl deleted file mode 100644 index 9ca1281288a4..000000000000 --- a/content/base/public/nsIScriptLoader.idl +++ /dev/null @@ -1,106 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla. - * - * The Initial Developer of the Original Code is - * Netscape Communications. - * Portions created by the Initial Developer are Copyright (C) 2001 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Vidur Apparao (original author) - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "nsISupports.idl" - -interface nsIDocument; -interface nsIScriptElement; -interface nsIScriptLoaderObserver; - -[scriptable, uuid(339a4eb5-dac6-4034-8c43-f4f8c645ce57)] -interface nsIScriptLoader : nsISupports { - /** - * Initialize loader with a document. The container of this document - * will be used for getting script evaluation information, including - * the context in which to do the evaluation. The loader maintains a - * strong reference to the document. - * - * @param aDocument The document to use as the basis for script - * processing. - */ - void init(in nsIDocument aDocument); - - /** - * The loader maintains a strong reference to the document with - * which it is initialized. This call forces the reference to - * be dropped. - */ - void dropDocumentReference(); - - /** - * Add an observer for all scripts loaded through this loader. - * - * @param aObserver observer for all script processing. - */ - void addObserver(in nsIScriptLoaderObserver aObserver); - - /** - * Remove an observer. - * - * @param aObserver observer to be removed - */ - void removeObserver(in nsIScriptLoaderObserver aObserver); - - /** - * Process a script element. This will include both loading the - * source of the element if it is not inline and evaluating - * the script itself. - * - * @param aElement The element representing the script to be loaded and - * evaluated. - * @param aObserver An observer for this script load only - * - */ - void processScriptElement(in nsIScriptElement aElement, - in nsIScriptLoaderObserver aObserver); - - /** - * Gets the currently executing script. This is useful if you want to - * generate a unique key based on the currently executing script. - */ - nsIScriptElement getCurrentScript(); - - /** - * Whether the loader is enabled or not. - * When disabled, processing of new script elements is disabled. - * Any call to processScriptElement() will fail with a return code of - * NS_ERROR_NOT_AVAILABLE. Note that this DOES NOT disable - * currently loading or executing scripts. - */ - attribute boolean enabled; -}; diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index 3f7b0e82c9d6..13df029ca44f 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -592,6 +592,7 @@ nsContentUtils::InitializeEventTable() { { nsGkAtoms::onpageshow, NS_PAGE_SHOW, EventNameType_HTML, NS_EVENT }, { nsGkAtoms::onpagehide, NS_PAGE_HIDE, EventNameType_HTML, NS_EVENT }, + { nsGkAtoms::onMozBeforeResize, NS_BEFORERESIZE_EVENT, EventNameType_None, NS_EVENT }, { nsGkAtoms::onresize, NS_RESIZE_EVENT, (EventNameType_HTMLXUL | EventNameType_SVGSVG), NS_EVENT }, { nsGkAtoms::onscroll, NS_SCROLL_EVENT, @@ -5536,6 +5537,11 @@ nsContentUtils::WrapNative(JSContext *cx, JSObject *scope, nsISupports *native, return NS_OK; } + JSObject *wrapper = xpc_GetCachedSlimWrapper(cache, scope, vp); + if (wrapper) { + return NS_OK; + } + NS_ENSURE_TRUE(sXPConnect && sThreadJSContextStack, NS_ERROR_UNEXPECTED); // Keep sXPConnect and sThreadJSContextStack alive. If we're on the main @@ -6489,3 +6495,11 @@ nsIContentUtils::FindInternalContentViewer(const char* aType, return NULL; } + +NS_IMPL_ISUPPORTS1(nsIContentUtils2, nsIContentUtils2) + +nsIInterfaceRequestor* +nsIContentUtils2::GetSameOriginChecker() +{ + return nsContentUtils::GetSameOriginChecker(); +} diff --git a/content/base/src/nsGkAtomList.h b/content/base/src/nsGkAtomList.h index e3e36306d1b5..7fe052fdb8b8 100644 --- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -709,6 +709,7 @@ GK_ATOM(onpopupshown, "onpopupshown") GK_ATOM(onreadystatechange, "onreadystatechange") GK_ATOM(onRequest, "onRequest") GK_ATOM(onreset, "onreset") +GK_ATOM(onMozBeforeResize, "onMozBeforeResize") GK_ATOM(onresize, "onresize") GK_ATOM(onscroll, "onscroll") GK_ATOM(onselect, "onselect") diff --git a/content/base/src/nsScriptLoader.cpp b/content/base/src/nsScriptLoader.cpp index 4d20dcf0a2ef..7b4430c9665d 100644 --- a/content/base/src/nsScriptLoader.cpp +++ b/content/base/src/nsScriptLoader.cpp @@ -546,10 +546,13 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement) // Step 9. in the HTML5 spec - nsCOMPtr scriptURI = aElement->GetScriptURI(); nsRefPtr request; - if (scriptURI) { + if (aElement->GetScriptExternal()) { // external script + nsCOMPtr scriptURI = aElement->GetScriptURI(); + if (!scriptURI) { + return NS_ERROR_NOT_AVAILABLE; + } nsTArray::index_type i = mPreloads.IndexOf(scriptURI.get(), 0, PreloadURIComparator()); if (i != nsTArray::NoIndex) { diff --git a/content/base/test/Makefile.in b/content/base/test/Makefile.in index 146ec3391838..8a4ec3e17971 100644 --- a/content/base/test/Makefile.in +++ b/content/base/test/Makefile.in @@ -442,6 +442,7 @@ _TEST_FILES2 = \ file_bug604660-5.xml \ file_bug604660-6.xsl \ test_bug605982.html \ + test_bug606729.html \ test_treewalker_nextsibling.xml \ $(NULL) diff --git a/content/base/test/test_bug606729.html b/content/base/test/test_bug606729.html new file mode 100644 index 000000000000..65b72d3bb180 --- /dev/null +++ b/content/base/test/test_bug606729.html @@ -0,0 +1,40 @@ + + + + + Test for Bug 606729 + + + + + +Mozilla Bug 606729 +

+ +
+
+
+
+
+ + + + diff --git a/content/canvas/public/nsICanvasRenderingContextInternal.h b/content/canvas/public/nsICanvasRenderingContextInternal.h index 9140c8e1c041..dc1f18f3bc21 100644 --- a/content/canvas/public/nsICanvasRenderingContextInternal.h +++ b/content/canvas/public/nsICanvasRenderingContextInternal.h @@ -50,6 +50,7 @@ class nsHTMLCanvasElement; class gfxContext; class gfxASurface; +class nsIPropertyBag; namespace mozilla { namespace layers { @@ -115,6 +116,14 @@ public: // Redraw the dirty rectangle of this canvas. NS_IMETHOD Redraw(const gfxRect &dirty) = 0; + // Passes a generic nsIPropertyBag options argument, along with the + // previous one, if any. Optional. + NS_IMETHOD SetContextOptions(nsIPropertyBag *aNewOptions) { return NS_OK; } + + // + // shmem support + // + // If this context can be set to use Mozilla's Shmem segments as its backing // store, this will set it to that state. Note that if you have drawn // anything into this canvas before changing the shmem state, it will be diff --git a/content/canvas/src/CustomQS_WebGL.h b/content/canvas/src/CustomQS_WebGL.h index 465fa7221ebc..b7a1db689ca4 100644 --- a/content/canvas/src/CustomQS_WebGL.h +++ b/content/canvas/src/CustomQS_WebGL.h @@ -82,14 +82,14 @@ helper_isFloat32Array(JSObject *obj) { * BufferData_array (int, js::TypedArray *, int) */ static JSBool -nsICanvasRenderingContextWebGL_BufferData(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_BufferData(JSContext *cx, uintN argc, jsval *vp) { XPC_QS_ASSERT_CONTEXT_OK(cx); JSObject *obj = JS_THIS_OBJECT(cx, vp); if (!obj) return JS_FALSE; - nsICanvasRenderingContextWebGL *self; + nsIDOMWebGLRenderingContext *self; xpc_qsSelfRef selfref; js::AutoValueRooter tvr(cx); if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr, tvr.jsval_addr(), nsnull)) @@ -149,14 +149,14 @@ nsICanvasRenderingContextWebGL_BufferData(JSContext *cx, uintN argc, jsval *vp) * BufferSubData_array (int, int, js::TypedArray *) */ static JSBool -nsICanvasRenderingContextWebGL_BufferSubData(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_BufferSubData(JSContext *cx, uintN argc, jsval *vp) { XPC_QS_ASSERT_CONTEXT_OK(cx); JSObject *obj = JS_THIS_OBJECT(cx, vp); if (!obj) return JS_FALSE; - nsICanvasRenderingContextWebGL *self; + nsIDOMWebGLRenderingContext *self; xpc_qsSelfRef selfref; js::AutoValueRooter tvr(cx); if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr, tvr.jsval_addr(), nsnull)) @@ -215,7 +215,7 @@ nsICanvasRenderingContextWebGL_BufferSubData(JSContext *cx, uintN argc, jsval *v * ReadPixels(int, int, int, int, uint, uint, ArrayBufferView) */ static JSBool -nsICanvasRenderingContextWebGL_ReadPixels(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_ReadPixels(JSContext *cx, uintN argc, jsval *vp) { XPC_QS_ASSERT_CONTEXT_OK(cx); JSObject *obj = JS_THIS_OBJECT(cx, vp); @@ -224,7 +224,7 @@ nsICanvasRenderingContextWebGL_ReadPixels(JSContext *cx, uintN argc, jsval *vp) nsresult rv; - nsICanvasRenderingContextWebGL *self; + nsIDOMWebGLRenderingContext *self; xpc_qsSelfRef selfref; js::AutoValueRooter tvr(cx); if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr, tvr.jsval_addr(), nsnull)) @@ -278,7 +278,7 @@ nsICanvasRenderingContextWebGL_ReadPixels(JSContext *cx, uintN argc, jsval *vp) * TexImage2D(uint, int, uint, uint, uint, ImageData) */ static JSBool -nsICanvasRenderingContextWebGL_TexImage2D(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_TexImage2D(JSContext *cx, uintN argc, jsval *vp) { XPC_QS_ASSERT_CONTEXT_OK(cx); JSObject *obj = JS_THIS_OBJECT(cx, vp); @@ -287,7 +287,7 @@ nsICanvasRenderingContextWebGL_TexImage2D(JSContext *cx, uintN argc, jsval *vp) nsresult rv; - nsICanvasRenderingContextWebGL *self; + nsIDOMWebGLRenderingContext *self; xpc_qsSelfRef selfref; js::AutoValueRooter tvr(cx); if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr, tvr.jsval_addr(), nsnull)) @@ -396,7 +396,7 @@ nsICanvasRenderingContextWebGL_TexImage2D(JSContext *cx, uintN argc, jsval *vp) * TexSubImage2D(uint, int, int, int, uint, uint, ImageData) */ static JSBool -nsICanvasRenderingContextWebGL_TexSubImage2D(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_TexSubImage2D(JSContext *cx, uintN argc, jsval *vp) { XPC_QS_ASSERT_CONTEXT_OK(cx); JSObject *obj = JS_THIS_OBJECT(cx, vp); @@ -405,7 +405,7 @@ nsICanvasRenderingContextWebGL_TexSubImage2D(JSContext *cx, uintN argc, jsval *v nsresult rv; - nsICanvasRenderingContextWebGL *self; + nsIDOMWebGLRenderingContext *self; xpc_qsSelfRef selfref; js::AutoValueRooter tvr(cx); if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr, tvr.jsval_addr(), nsnull)) @@ -504,7 +504,7 @@ nsICanvasRenderingContextWebGL_TexSubImage2D(JSContext *cx, uintN argc, jsval *v /* NOTE: There is a TN version of this below, update it as well */ static inline JSBool -helper_nsICanvasRenderingContextWebGL_Uniform_x_iv(JSContext *cx, uintN argc, jsval *vp, int nElements) +helper_nsIDOMWebGLRenderingContext_Uniform_x_iv(JSContext *cx, uintN argc, jsval *vp, int nElements) { XPC_QS_ASSERT_CONTEXT_OK(cx); JSObject *obj = JS_THIS_OBJECT(cx, vp); @@ -513,7 +513,7 @@ helper_nsICanvasRenderingContextWebGL_Uniform_x_iv(JSContext *cx, uintN argc, js nsresult rv; - nsICanvasRenderingContextWebGL *self; + nsIDOMWebGLRenderingContext *self; xpc_qsSelfRef selfref; js::AutoValueRooter tvr(cx); if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr, tvr.jsval_addr(), nsnull)) @@ -578,7 +578,7 @@ helper_nsICanvasRenderingContextWebGL_Uniform_x_iv(JSContext *cx, uintN argc, js /* NOTE: There is a TN version of this below, update it as well */ static inline JSBool -helper_nsICanvasRenderingContextWebGL_Uniform_x_fv(JSContext *cx, uintN argc, jsval *vp, int nElements) +helper_nsIDOMWebGLRenderingContext_Uniform_x_fv(JSContext *cx, uintN argc, jsval *vp, int nElements) { XPC_QS_ASSERT_CONTEXT_OK(cx); JSObject *obj = JS_THIS_OBJECT(cx, vp); @@ -587,7 +587,7 @@ helper_nsICanvasRenderingContextWebGL_Uniform_x_fv(JSContext *cx, uintN argc, js nsresult rv; - nsICanvasRenderingContextWebGL *self; + nsIDOMWebGLRenderingContext *self; xpc_qsSelfRef selfref; js::AutoValueRooter tvr(cx); if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr, tvr.jsval_addr(), nsnull)) @@ -652,7 +652,7 @@ helper_nsICanvasRenderingContextWebGL_Uniform_x_fv(JSContext *cx, uintN argc, js /* NOTE: There is a TN version of this below, update it as well */ static inline JSBool -helper_nsICanvasRenderingContextWebGL_UniformMatrix_x_fv(JSContext *cx, uintN argc, jsval *vp, int nElements) +helper_nsIDOMWebGLRenderingContext_UniformMatrix_x_fv(JSContext *cx, uintN argc, jsval *vp, int nElements) { XPC_QS_ASSERT_CONTEXT_OK(cx); JSObject *obj = JS_THIS_OBJECT(cx, vp); @@ -661,7 +661,7 @@ helper_nsICanvasRenderingContextWebGL_UniformMatrix_x_fv(JSContext *cx, uintN ar nsresult rv; - nsICanvasRenderingContextWebGL *self; + nsIDOMWebGLRenderingContext *self; xpc_qsSelfRef selfref; js::AutoValueRooter tvr(cx); if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr, tvr.jsval_addr(), nsnull)) @@ -727,7 +727,7 @@ helper_nsICanvasRenderingContextWebGL_UniformMatrix_x_fv(JSContext *cx, uintN ar } static inline JSBool -helper_nsICanvasRenderingContextWebGL_VertexAttrib_x_fv(JSContext *cx, uintN argc, jsval *vp, int nElements) +helper_nsIDOMWebGLRenderingContext_VertexAttrib_x_fv(JSContext *cx, uintN argc, jsval *vp, int nElements) { XPC_QS_ASSERT_CONTEXT_OK(cx); JSObject *obj = JS_THIS_OBJECT(cx, vp); @@ -736,7 +736,7 @@ helper_nsICanvasRenderingContextWebGL_VertexAttrib_x_fv(JSContext *cx, uintN arg nsresult rv; - nsICanvasRenderingContextWebGL *self; + nsIDOMWebGLRenderingContext *self; xpc_qsSelfRef selfref; js::AutoValueRooter tvr(cx); if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr, tvr.jsval_addr(), nsnull)) @@ -796,104 +796,104 @@ helper_nsICanvasRenderingContextWebGL_VertexAttrib_x_fv(JSContext *cx, uintN arg } static JSBool -nsICanvasRenderingContextWebGL_Uniform1iv(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_Uniform1iv(JSContext *cx, uintN argc, jsval *vp) { - return helper_nsICanvasRenderingContextWebGL_Uniform_x_iv(cx, argc, vp, 1); + return helper_nsIDOMWebGLRenderingContext_Uniform_x_iv(cx, argc, vp, 1); } static JSBool -nsICanvasRenderingContextWebGL_Uniform2iv(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_Uniform2iv(JSContext *cx, uintN argc, jsval *vp) { - return helper_nsICanvasRenderingContextWebGL_Uniform_x_iv(cx, argc, vp, 2); + return helper_nsIDOMWebGLRenderingContext_Uniform_x_iv(cx, argc, vp, 2); } static JSBool -nsICanvasRenderingContextWebGL_Uniform3iv(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_Uniform3iv(JSContext *cx, uintN argc, jsval *vp) { - return helper_nsICanvasRenderingContextWebGL_Uniform_x_iv(cx, argc, vp, 3); + return helper_nsIDOMWebGLRenderingContext_Uniform_x_iv(cx, argc, vp, 3); } static JSBool -nsICanvasRenderingContextWebGL_Uniform4iv(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_Uniform4iv(JSContext *cx, uintN argc, jsval *vp) { - return helper_nsICanvasRenderingContextWebGL_Uniform_x_iv(cx, argc, vp, 4); + return helper_nsIDOMWebGLRenderingContext_Uniform_x_iv(cx, argc, vp, 4); } static JSBool -nsICanvasRenderingContextWebGL_Uniform1fv(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_Uniform1fv(JSContext *cx, uintN argc, jsval *vp) { - return helper_nsICanvasRenderingContextWebGL_Uniform_x_fv(cx, argc, vp, 1); + return helper_nsIDOMWebGLRenderingContext_Uniform_x_fv(cx, argc, vp, 1); } static JSBool -nsICanvasRenderingContextWebGL_Uniform2fv(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_Uniform2fv(JSContext *cx, uintN argc, jsval *vp) { - return helper_nsICanvasRenderingContextWebGL_Uniform_x_fv(cx, argc, vp, 2); + return helper_nsIDOMWebGLRenderingContext_Uniform_x_fv(cx, argc, vp, 2); } static JSBool -nsICanvasRenderingContextWebGL_Uniform3fv(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_Uniform3fv(JSContext *cx, uintN argc, jsval *vp) { - return helper_nsICanvasRenderingContextWebGL_Uniform_x_fv(cx, argc, vp, 3); + return helper_nsIDOMWebGLRenderingContext_Uniform_x_fv(cx, argc, vp, 3); } static JSBool -nsICanvasRenderingContextWebGL_Uniform4fv(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_Uniform4fv(JSContext *cx, uintN argc, jsval *vp) { - return helper_nsICanvasRenderingContextWebGL_Uniform_x_fv(cx, argc, vp, 4); + return helper_nsIDOMWebGLRenderingContext_Uniform_x_fv(cx, argc, vp, 4); } static JSBool -nsICanvasRenderingContextWebGL_UniformMatrix2fv(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_UniformMatrix2fv(JSContext *cx, uintN argc, jsval *vp) { - return helper_nsICanvasRenderingContextWebGL_UniformMatrix_x_fv(cx, argc, vp, 2); + return helper_nsIDOMWebGLRenderingContext_UniformMatrix_x_fv(cx, argc, vp, 2); } static JSBool -nsICanvasRenderingContextWebGL_UniformMatrix3fv(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_UniformMatrix3fv(JSContext *cx, uintN argc, jsval *vp) { - return helper_nsICanvasRenderingContextWebGL_UniformMatrix_x_fv(cx, argc, vp, 3); + return helper_nsIDOMWebGLRenderingContext_UniformMatrix_x_fv(cx, argc, vp, 3); } static JSBool -nsICanvasRenderingContextWebGL_UniformMatrix4fv(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_UniformMatrix4fv(JSContext *cx, uintN argc, jsval *vp) { - return helper_nsICanvasRenderingContextWebGL_UniformMatrix_x_fv(cx, argc, vp, 4); + return helper_nsIDOMWebGLRenderingContext_UniformMatrix_x_fv(cx, argc, vp, 4); } static JSBool -nsICanvasRenderingContextWebGL_VertexAttrib1fv(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_VertexAttrib1fv(JSContext *cx, uintN argc, jsval *vp) { - return helper_nsICanvasRenderingContextWebGL_VertexAttrib_x_fv(cx, argc, vp, 1); + return helper_nsIDOMWebGLRenderingContext_VertexAttrib_x_fv(cx, argc, vp, 1); } static JSBool -nsICanvasRenderingContextWebGL_VertexAttrib2fv(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_VertexAttrib2fv(JSContext *cx, uintN argc, jsval *vp) { - return helper_nsICanvasRenderingContextWebGL_VertexAttrib_x_fv(cx, argc, vp, 2); + return helper_nsIDOMWebGLRenderingContext_VertexAttrib_x_fv(cx, argc, vp, 2); } static JSBool -nsICanvasRenderingContextWebGL_VertexAttrib3fv(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_VertexAttrib3fv(JSContext *cx, uintN argc, jsval *vp) { - return helper_nsICanvasRenderingContextWebGL_VertexAttrib_x_fv(cx, argc, vp, 3); + return helper_nsIDOMWebGLRenderingContext_VertexAttrib_x_fv(cx, argc, vp, 3); } static JSBool -nsICanvasRenderingContextWebGL_VertexAttrib4fv(JSContext *cx, uintN argc, jsval *vp) +nsIDOMWebGLRenderingContext_VertexAttrib4fv(JSContext *cx, uintN argc, jsval *vp) { - return helper_nsICanvasRenderingContextWebGL_VertexAttrib_x_fv(cx, argc, vp, 4); + return helper_nsIDOMWebGLRenderingContext_VertexAttrib_x_fv(cx, argc, vp, 4); } #ifdef JS_TRACER static inline void FASTCALL -helper_nsICanvasRenderingContextWebGL_Uniform_x_iv_tn(JSContext *cx, JSObject *obj, JSObject *locationobj, +helper_nsIDOMWebGLRenderingContext_Uniform_x_iv_tn(JSContext *cx, JSObject *obj, JSObject *locationobj, JSObject *arg, int nElements) { XPC_QS_ASSERT_CONTEXT_OK(cx); - nsICanvasRenderingContextWebGL *self; + nsIDOMWebGLRenderingContext *self; xpc_qsSelfRef selfref; xpc_qsArgValArray<3> vp(cx); if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr, &vp.array[0], nsnull)) { @@ -902,7 +902,7 @@ helper_nsICanvasRenderingContextWebGL_Uniform_x_iv_tn(JSContext *cx, JSObject *o } if (!arg) { - xpc_qsThrowMethodFailedWithDetails(cx, NS_ERROR_FAILURE, "nsICanvasRenderingContextWebGL", "uniformNiv"); + xpc_qsThrowMethodFailedWithDetails(cx, NS_ERROR_FAILURE, "nsIDOMWebGLRenderingContext", "uniformNiv"); js_SetTraceableNativeFailed(cx); } @@ -932,7 +932,7 @@ helper_nsICanvasRenderingContextWebGL_Uniform_x_iv_tn(JSContext *cx, JSObject *o *obj_tvr.jsval_addr() = OBJECT_TO_JSVAL(nobj); wa = js::TypedArray::fromJSObject(nobj); } else { - xpc_qsThrowMethodFailedWithDetails(cx, NS_ERROR_FAILURE, "nsICanvasRenderingContextWebGL", "uniformNiv"); + xpc_qsThrowMethodFailedWithDetails(cx, NS_ERROR_FAILURE, "nsIDOMWebGLRenderingContext", "uniformNiv"); js_SetTraceableNativeFailed(cx); return; } @@ -950,18 +950,18 @@ helper_nsICanvasRenderingContextWebGL_Uniform_x_iv_tn(JSContext *cx, JSObject *o } if (NS_FAILED(rv)) { - xpc_qsThrowMethodFailedWithDetails(cx, rv, "nsICanvasRenderingContextWebGL", "uniformNiv"); + xpc_qsThrowMethodFailedWithDetails(cx, rv, "nsIDOMWebGLRenderingContext", "uniformNiv"); js_SetTraceableNativeFailed(cx); } } static inline void FASTCALL -helper_nsICanvasRenderingContextWebGL_Uniform_x_fv_tn(JSContext *cx, JSObject *obj, JSObject *locationobj, +helper_nsIDOMWebGLRenderingContext_Uniform_x_fv_tn(JSContext *cx, JSObject *obj, JSObject *locationobj, JSObject *arg, int nElements) { XPC_QS_ASSERT_CONTEXT_OK(cx); - nsICanvasRenderingContextWebGL *self; + nsIDOMWebGLRenderingContext *self; xpc_qsSelfRef selfref; xpc_qsArgValArray<3> vp(cx); if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr, &vp.array[0], nsnull)) { @@ -970,7 +970,7 @@ helper_nsICanvasRenderingContextWebGL_Uniform_x_fv_tn(JSContext *cx, JSObject *o } if (!arg) { - xpc_qsThrowMethodFailedWithDetails(cx, NS_ERROR_FAILURE, "nsICanvasRenderingContextWebGL", "uniformNfv"); + xpc_qsThrowMethodFailedWithDetails(cx, NS_ERROR_FAILURE, "nsIDOMWebGLRenderingContext", "uniformNfv"); js_SetTraceableNativeFailed(cx); } @@ -1000,7 +1000,7 @@ helper_nsICanvasRenderingContextWebGL_Uniform_x_fv_tn(JSContext *cx, JSObject *o *obj_tvr.jsval_addr() = OBJECT_TO_JSVAL(nobj); wa = js::TypedArray::fromJSObject(nobj); } else { - xpc_qsThrowMethodFailedWithDetails(cx, NS_ERROR_FAILURE, "nsICanvasRenderingContextWebGL", "uniformNfv"); + xpc_qsThrowMethodFailedWithDetails(cx, NS_ERROR_FAILURE, "nsIDOMWebGLRenderingContext", "uniformNfv"); js_SetTraceableNativeFailed(cx); return; } @@ -1018,7 +1018,7 @@ helper_nsICanvasRenderingContextWebGL_Uniform_x_fv_tn(JSContext *cx, JSObject *o } if (NS_FAILED(rv)) { - xpc_qsThrowMethodFailedWithDetails(cx, rv, "nsICanvasRenderingContextWebGL", "uniformNfv"); + xpc_qsThrowMethodFailedWithDetails(cx, rv, "nsIDOMWebGLRenderingContext", "uniformNfv"); js_SetTraceableNativeFailed(cx); } @@ -1026,12 +1026,12 @@ helper_nsICanvasRenderingContextWebGL_Uniform_x_fv_tn(JSContext *cx, JSObject *o } static inline void FASTCALL -helper_nsICanvasRenderingContextWebGL_UniformMatrix_x_fv_tn(JSContext *cx, JSObject *obj, JSObject *locationobj, +helper_nsIDOMWebGLRenderingContext_UniformMatrix_x_fv_tn(JSContext *cx, JSObject *obj, JSObject *locationobj, JSBool transpose, JSObject *arg, int nElements) { XPC_QS_ASSERT_CONTEXT_OK(cx); - nsICanvasRenderingContextWebGL *self; + nsIDOMWebGLRenderingContext *self; xpc_qsSelfRef selfref; xpc_qsArgValArray<4> vp(cx); if (!xpc_qsUnwrapThis(cx, obj, nsnull, &self, &selfref.ptr, &vp.array[0], nsnull)) { @@ -1040,7 +1040,7 @@ helper_nsICanvasRenderingContextWebGL_UniformMatrix_x_fv_tn(JSContext *cx, JSObj } if (!arg) { - xpc_qsThrowMethodFailedWithDetails(cx, NS_ERROR_FAILURE, "nsICanvasRenderingContextWebGL", "uniformMatrixNfv"); + xpc_qsThrowMethodFailedWithDetails(cx, NS_ERROR_FAILURE, "nsIDOMWebGLRenderingContext", "uniformMatrixNfv"); js_SetTraceableNativeFailed(cx); } @@ -1070,7 +1070,7 @@ helper_nsICanvasRenderingContextWebGL_UniformMatrix_x_fv_tn(JSContext *cx, JSObj *obj_tvr.jsval_addr() = OBJECT_TO_JSVAL(nobj); wa = js::TypedArray::fromJSObject(nobj); } else { - xpc_qsThrowMethodFailedWithDetails(cx, NS_ERROR_FAILURE, "nsICanvasRenderingContextWebGL", "uniformMatrixNfv"); + xpc_qsThrowMethodFailedWithDetails(cx, NS_ERROR_FAILURE, "nsIDOMWebGLRenderingContext", "uniformMatrixNfv"); js_SetTraceableNativeFailed(cx); return; } @@ -1085,7 +1085,7 @@ helper_nsICanvasRenderingContextWebGL_UniformMatrix_x_fv_tn(JSContext *cx, JSObj } if (NS_FAILED(rv)) { - xpc_qsThrowMethodFailedWithDetails(cx, rv, "nsICanvasRenderingContextWebGL", "uniformMatrixNfv"); + xpc_qsThrowMethodFailedWithDetails(cx, rv, "nsIDOMWebGLRenderingContext", "uniformMatrixNfv"); js_SetTraceableNativeFailed(cx); } } @@ -1093,133 +1093,133 @@ helper_nsICanvasRenderingContextWebGL_UniformMatrix_x_fv_tn(JSContext *cx, JSObj // FIXME This should return void, not uint32 // (waiting for https://bugzilla.mozilla.org/show_bug.cgi?id=572798) static uint32 FASTCALL -nsICanvasRenderingContextWebGL_Uniform1iv_tn(JSContext *cx, JSObject *obj, JSObject *location, JSObject *arg) +nsIDOMWebGLRenderingContext_Uniform1iv_tn(JSContext *cx, JSObject *obj, JSObject *location, JSObject *arg) { - helper_nsICanvasRenderingContextWebGL_Uniform_x_iv_tn(cx, obj, location, arg, 1); + helper_nsIDOMWebGLRenderingContext_Uniform_x_iv_tn(cx, obj, location, arg, 1); return 0; } -JS_DEFINE_TRCINFO_1(nsICanvasRenderingContextWebGL_Uniform1iv, - (4, (static, UINT32_FAIL, nsICanvasRenderingContextWebGL_Uniform1iv_tn, CONTEXT, THIS, OBJECT, OBJECT, 0, nanojit::ACCSET_STORE_ANY))) +JS_DEFINE_TRCINFO_1(nsIDOMWebGLRenderingContext_Uniform1iv, + (4, (static, UINT32_FAIL, nsIDOMWebGLRenderingContext_Uniform1iv_tn, CONTEXT, THIS, OBJECT, OBJECT, 0, nanojit::ACCSET_STORE_ANY))) // FIXME This should return void, not uint32 // (waiting for https://bugzilla.mozilla.org/show_bug.cgi?id=572798) static uint32 FASTCALL -nsICanvasRenderingContextWebGL_Uniform2iv_tn(JSContext *cx, JSObject *obj, JSObject *location, JSObject *arg) +nsIDOMWebGLRenderingContext_Uniform2iv_tn(JSContext *cx, JSObject *obj, JSObject *location, JSObject *arg) { - helper_nsICanvasRenderingContextWebGL_Uniform_x_iv_tn(cx, obj, location, arg, 2); + helper_nsIDOMWebGLRenderingContext_Uniform_x_iv_tn(cx, obj, location, arg, 2); return 0; } -JS_DEFINE_TRCINFO_1(nsICanvasRenderingContextWebGL_Uniform2iv, - (4, (static, UINT32_FAIL, nsICanvasRenderingContextWebGL_Uniform2iv_tn, CONTEXT, THIS, OBJECT, OBJECT, 0, nanojit::ACCSET_STORE_ANY))) +JS_DEFINE_TRCINFO_1(nsIDOMWebGLRenderingContext_Uniform2iv, + (4, (static, UINT32_FAIL, nsIDOMWebGLRenderingContext_Uniform2iv_tn, CONTEXT, THIS, OBJECT, OBJECT, 0, nanojit::ACCSET_STORE_ANY))) // FIXME This should return void, not uint32 // (waiting for https://bugzilla.mozilla.org/show_bug.cgi?id=572798) static uint32 FASTCALL -nsICanvasRenderingContextWebGL_Uniform3iv_tn(JSContext *cx, JSObject *obj, JSObject *location, JSObject *arg) +nsIDOMWebGLRenderingContext_Uniform3iv_tn(JSContext *cx, JSObject *obj, JSObject *location, JSObject *arg) { - helper_nsICanvasRenderingContextWebGL_Uniform_x_iv_tn(cx, obj, location, arg, 3); + helper_nsIDOMWebGLRenderingContext_Uniform_x_iv_tn(cx, obj, location, arg, 3); return 0; } -JS_DEFINE_TRCINFO_1(nsICanvasRenderingContextWebGL_Uniform3iv, - (4, (static, UINT32_FAIL, nsICanvasRenderingContextWebGL_Uniform3iv_tn, CONTEXT, THIS, OBJECT, OBJECT, 0, nanojit::ACCSET_STORE_ANY))) +JS_DEFINE_TRCINFO_1(nsIDOMWebGLRenderingContext_Uniform3iv, + (4, (static, UINT32_FAIL, nsIDOMWebGLRenderingContext_Uniform3iv_tn, CONTEXT, THIS, OBJECT, OBJECT, 0, nanojit::ACCSET_STORE_ANY))) // FIXME This should return void, not uint32 // (waiting for https://bugzilla.mozilla.org/show_bug.cgi?id=572798) static uint32 FASTCALL -nsICanvasRenderingContextWebGL_Uniform4iv_tn(JSContext *cx, JSObject *obj, JSObject *location, JSObject *arg) +nsIDOMWebGLRenderingContext_Uniform4iv_tn(JSContext *cx, JSObject *obj, JSObject *location, JSObject *arg) { - helper_nsICanvasRenderingContextWebGL_Uniform_x_iv_tn(cx, obj, location, arg, 4); + helper_nsIDOMWebGLRenderingContext_Uniform_x_iv_tn(cx, obj, location, arg, 4); return 0; } -JS_DEFINE_TRCINFO_1(nsICanvasRenderingContextWebGL_Uniform4iv, - (4, (static, UINT32_FAIL, nsICanvasRenderingContextWebGL_Uniform4iv_tn, CONTEXT, THIS, OBJECT, OBJECT, 0, nanojit::ACCSET_STORE_ANY))) +JS_DEFINE_TRCINFO_1(nsIDOMWebGLRenderingContext_Uniform4iv, + (4, (static, UINT32_FAIL, nsIDOMWebGLRenderingContext_Uniform4iv_tn, CONTEXT, THIS, OBJECT, OBJECT, 0, nanojit::ACCSET_STORE_ANY))) // FIXME This should return void, not uint32 // (waiting for https://bugzilla.mozilla.org/show_bug.cgi?id=572798) static uint32 FASTCALL -nsICanvasRenderingContextWebGL_Uniform1fv_tn(JSContext *cx, JSObject *obj, JSObject *location, JSObject *arg) +nsIDOMWebGLRenderingContext_Uniform1fv_tn(JSContext *cx, JSObject *obj, JSObject *location, JSObject *arg) { - helper_nsICanvasRenderingContextWebGL_Uniform_x_fv_tn(cx, obj, location, arg, 1); + helper_nsIDOMWebGLRenderingContext_Uniform_x_fv_tn(cx, obj, location, arg, 1); return 0; } -JS_DEFINE_TRCINFO_1(nsICanvasRenderingContextWebGL_Uniform1fv, - (4, (static, UINT32_FAIL, nsICanvasRenderingContextWebGL_Uniform1fv_tn, CONTEXT, THIS, OBJECT, OBJECT, 0, nanojit::ACCSET_STORE_ANY))) +JS_DEFINE_TRCINFO_1(nsIDOMWebGLRenderingContext_Uniform1fv, + (4, (static, UINT32_FAIL, nsIDOMWebGLRenderingContext_Uniform1fv_tn, CONTEXT, THIS, OBJECT, OBJECT, 0, nanojit::ACCSET_STORE_ANY))) // FIXME This should return void, not uint32 // (waiting for https://bugzilla.mozilla.org/show_bug.cgi?id=572798) static uint32 FASTCALL -nsICanvasRenderingContextWebGL_Uniform2fv_tn(JSContext *cx, JSObject *obj, JSObject *location, JSObject *arg) +nsIDOMWebGLRenderingContext_Uniform2fv_tn(JSContext *cx, JSObject *obj, JSObject *location, JSObject *arg) { - helper_nsICanvasRenderingContextWebGL_Uniform_x_fv_tn(cx, obj, location, arg, 2); + helper_nsIDOMWebGLRenderingContext_Uniform_x_fv_tn(cx, obj, location, arg, 2); return 0; } -JS_DEFINE_TRCINFO_1(nsICanvasRenderingContextWebGL_Uniform2fv, - (4, (static, UINT32_FAIL, nsICanvasRenderingContextWebGL_Uniform2fv_tn, CONTEXT, THIS, OBJECT, OBJECT, 0, nanojit::ACCSET_STORE_ANY))) +JS_DEFINE_TRCINFO_1(nsIDOMWebGLRenderingContext_Uniform2fv, + (4, (static, UINT32_FAIL, nsIDOMWebGLRenderingContext_Uniform2fv_tn, CONTEXT, THIS, OBJECT, OBJECT, 0, nanojit::ACCSET_STORE_ANY))) // FIXME This should return void, not uint32 // (waiting for https://bugzilla.mozilla.org/show_bug.cgi?id=572798) static uint32 FASTCALL -nsICanvasRenderingContextWebGL_Uniform3fv_tn(JSContext *cx, JSObject *obj, JSObject *location, JSObject *arg) +nsIDOMWebGLRenderingContext_Uniform3fv_tn(JSContext *cx, JSObject *obj, JSObject *location, JSObject *arg) { - helper_nsICanvasRenderingContextWebGL_Uniform_x_fv_tn(cx, obj, location, arg, 3); + helper_nsIDOMWebGLRenderingContext_Uniform_x_fv_tn(cx, obj, location, arg, 3); return 0; } -JS_DEFINE_TRCINFO_1(nsICanvasRenderingContextWebGL_Uniform3fv, - (4, (static, UINT32_FAIL, nsICanvasRenderingContextWebGL_Uniform3fv_tn, CONTEXT, THIS, OBJECT, OBJECT, 0, nanojit::ACCSET_STORE_ANY))) +JS_DEFINE_TRCINFO_1(nsIDOMWebGLRenderingContext_Uniform3fv, + (4, (static, UINT32_FAIL, nsIDOMWebGLRenderingContext_Uniform3fv_tn, CONTEXT, THIS, OBJECT, OBJECT, 0, nanojit::ACCSET_STORE_ANY))) // FIXME This should return void, not uint32 // (waiting for https://bugzilla.mozilla.org/show_bug.cgi?id=572798) static uint32 FASTCALL -nsICanvasRenderingContextWebGL_Uniform4fv_tn(JSContext *cx, JSObject *obj, JSObject *location, JSObject *arg) +nsIDOMWebGLRenderingContext_Uniform4fv_tn(JSContext *cx, JSObject *obj, JSObject *location, JSObject *arg) { - helper_nsICanvasRenderingContextWebGL_Uniform_x_fv_tn(cx, obj, location, arg, 4); + helper_nsIDOMWebGLRenderingContext_Uniform_x_fv_tn(cx, obj, location, arg, 4); return 0; } -JS_DEFINE_TRCINFO_1(nsICanvasRenderingContextWebGL_Uniform4fv, - (4, (static, UINT32_FAIL, nsICanvasRenderingContextWebGL_Uniform4fv_tn, CONTEXT, THIS, OBJECT, OBJECT, 0, nanojit::ACCSET_STORE_ANY))) +JS_DEFINE_TRCINFO_1(nsIDOMWebGLRenderingContext_Uniform4fv, + (4, (static, UINT32_FAIL, nsIDOMWebGLRenderingContext_Uniform4fv_tn, CONTEXT, THIS, OBJECT, OBJECT, 0, nanojit::ACCSET_STORE_ANY))) // FIXME This should return void, not uint32 // (waiting for https://bugzilla.mozilla.org/show_bug.cgi?id=572798) static uint32 FASTCALL -nsICanvasRenderingContextWebGL_UniformMatrix2fv_tn(JSContext *cx, JSObject *obj, JSObject *loc, JSBool transpose, JSObject *arg) +nsIDOMWebGLRenderingContext_UniformMatrix2fv_tn(JSContext *cx, JSObject *obj, JSObject *loc, JSBool transpose, JSObject *arg) { - helper_nsICanvasRenderingContextWebGL_UniformMatrix_x_fv_tn(cx, obj, loc, transpose, arg, 2); + helper_nsIDOMWebGLRenderingContext_UniformMatrix_x_fv_tn(cx, obj, loc, transpose, arg, 2); return 0; } -JS_DEFINE_TRCINFO_1(nsICanvasRenderingContextWebGL_UniformMatrix2fv, - (5, (static, UINT32_FAIL, nsICanvasRenderingContextWebGL_UniformMatrix2fv_tn, CONTEXT, THIS, OBJECT, BOOL, OBJECT, 0, nanojit::ACCSET_STORE_ANY))) +JS_DEFINE_TRCINFO_1(nsIDOMWebGLRenderingContext_UniformMatrix2fv, + (5, (static, UINT32_FAIL, nsIDOMWebGLRenderingContext_UniformMatrix2fv_tn, CONTEXT, THIS, OBJECT, BOOL, OBJECT, 0, nanojit::ACCSET_STORE_ANY))) // FIXME This should return void, not uint32 // (waiting for https://bugzilla.mozilla.org/show_bug.cgi?id=572798) static uint32 FASTCALL -nsICanvasRenderingContextWebGL_UniformMatrix3fv_tn(JSContext *cx, JSObject *obj, JSObject *loc, JSBool transpose, JSObject *arg) +nsIDOMWebGLRenderingContext_UniformMatrix3fv_tn(JSContext *cx, JSObject *obj, JSObject *loc, JSBool transpose, JSObject *arg) { - helper_nsICanvasRenderingContextWebGL_UniformMatrix_x_fv_tn(cx, obj, loc, transpose, arg, 3); + helper_nsIDOMWebGLRenderingContext_UniformMatrix_x_fv_tn(cx, obj, loc, transpose, arg, 3); return 0; } -JS_DEFINE_TRCINFO_1(nsICanvasRenderingContextWebGL_UniformMatrix3fv, - (5, (static, UINT32_FAIL, nsICanvasRenderingContextWebGL_UniformMatrix3fv_tn, CONTEXT, THIS, OBJECT, BOOL, OBJECT, 0, nanojit::ACCSET_STORE_ANY))) +JS_DEFINE_TRCINFO_1(nsIDOMWebGLRenderingContext_UniformMatrix3fv, + (5, (static, UINT32_FAIL, nsIDOMWebGLRenderingContext_UniformMatrix3fv_tn, CONTEXT, THIS, OBJECT, BOOL, OBJECT, 0, nanojit::ACCSET_STORE_ANY))) // FIXME This should return void, not uint32 // (waiting for https://bugzilla.mozilla.org/show_bug.cgi?id=572798) static uint32 FASTCALL -nsICanvasRenderingContextWebGL_UniformMatrix4fv_tn(JSContext *cx, JSObject *obj, JSObject *loc, JSBool transpose, JSObject *arg) +nsIDOMWebGLRenderingContext_UniformMatrix4fv_tn(JSContext *cx, JSObject *obj, JSObject *loc, JSBool transpose, JSObject *arg) { - helper_nsICanvasRenderingContextWebGL_UniformMatrix_x_fv_tn(cx, obj, loc, transpose, arg, 4); + helper_nsIDOMWebGLRenderingContext_UniformMatrix_x_fv_tn(cx, obj, loc, transpose, arg, 4); return 0; } -JS_DEFINE_TRCINFO_1(nsICanvasRenderingContextWebGL_UniformMatrix4fv, - (5, (static, UINT32_FAIL, nsICanvasRenderingContextWebGL_UniformMatrix4fv_tn, CONTEXT, THIS, OBJECT, BOOL, OBJECT, 0, nanojit::ACCSET_STORE_ANY))) +JS_DEFINE_TRCINFO_1(nsIDOMWebGLRenderingContext_UniformMatrix4fv, + (5, (static, UINT32_FAIL, nsIDOMWebGLRenderingContext_UniformMatrix4fv_tn, CONTEXT, THIS, OBJECT, BOOL, OBJECT, 0, nanojit::ACCSET_STORE_ANY))) #endif /* JS_TRACER */ diff --git a/content/canvas/src/WebGLContext.cpp b/content/canvas/src/WebGLContext.cpp index 174152123907..334fe4781a74 100644 --- a/content/canvas/src/WebGLContext.cpp +++ b/content/canvas/src/WebGLContext.cpp @@ -48,6 +48,11 @@ #include "nsDOMError.h" #include "nsIGfxInfo.h" +#include "nsIPropertyBag.h" +#include "nsIVariant.h" + +#include "imgIEncoder.h" + #include "gfxContext.h" #include "gfxPattern.h" #include "gfxUtils.h" @@ -66,12 +71,12 @@ using namespace mozilla; using namespace mozilla::gl; using namespace mozilla::layers; -nsresult NS_NewCanvasRenderingContextWebGL(nsICanvasRenderingContextWebGL** aResult); +nsresult NS_NewCanvasRenderingContextWebGL(nsIDOMWebGLRenderingContext** aResult); nsresult -NS_NewCanvasRenderingContextWebGL(nsICanvasRenderingContextWebGL** aResult) +NS_NewCanvasRenderingContextWebGL(nsIDOMWebGLRenderingContext** aResult) { - nsICanvasRenderingContextWebGL* ctx = new WebGLContext(); + nsIDOMWebGLRenderingContext* ctx = new WebGLContext(); if (!ctx) return NS_ERROR_OUT_OF_MEMORY; @@ -88,6 +93,7 @@ WebGLContext::WebGLContext() mInvalidated = PR_FALSE; mResetLayer = PR_TRUE; mVerbose = PR_FALSE; + mOptionsFrozen = PR_FALSE; mActiveTexture = 0; mSynthesizedGLError = LOCAL_GL_NO_ERROR; @@ -266,6 +272,67 @@ WebGLContext::SetCanvasElement(nsHTMLCanvasElement* aParentCanvas) return NS_OK; } +static bool +GetBoolFromPropertyBag(nsIPropertyBag *bag, const char *propName, bool *boolResult) +{ + nsCOMPtr vv; + PRBool bv; + + nsresult rv = bag->GetProperty(NS_ConvertASCIItoUTF16(propName), getter_AddRefs(vv)); + if (NS_FAILED(rv) || !vv) + return false; + + rv = vv->GetAsBool(&bv); + if (NS_FAILED(rv)) + return false; + + *boolResult = bv ? true : false; + return true; +} + +NS_IMETHODIMP +WebGLContext::SetContextOptions(nsIPropertyBag *aOptions) +{ + if (!aOptions) + return NS_OK; + + WebGLContextOptions newOpts; + + // defaults are: yes: depth, alpha, premultipliedAlpha; no: stencil + if (!GetBoolFromPropertyBag(aOptions, "stencil", &newOpts.stencil)) + newOpts.stencil = false; + + if (!GetBoolFromPropertyBag(aOptions, "depth", &newOpts.depth)) + newOpts.depth = true; + + if (!GetBoolFromPropertyBag(aOptions, "alpha", &newOpts.alpha)) + newOpts.alpha = true; + + if (!GetBoolFromPropertyBag(aOptions, "premultipliedAlpha", &newOpts.premultipliedAlpha)) + newOpts.premultipliedAlpha = true; + + GetBoolFromPropertyBag(aOptions, "antialiasHint", &newOpts.antialiasHint); + + // enforce that if stencil is specified, we also give back depth + newOpts.depth |= newOpts.stencil; + + LogMessage("aaHint: %d stencil: %d depth: %d alpha: %d premult: %d\n", + newOpts.antialiasHint ? 1 : 0, + newOpts.stencil ? 1 : 0, + newOpts.depth ? 1 : 0, + newOpts.alpha ? 1 : 0, + newOpts.premultipliedAlpha ? 1 : 0); + + if (mOptionsFrozen && newOpts != mOptions) { + // Error if the options are already frozen, and the ones that were asked for + // aren't the same as what they were originally. + return NS_ERROR_FAILURE; + } + + mOptions = newOpts; + return NS_OK; +} + NS_IMETHODIMP WebGLContext::SetDimensions(PRInt32 width, PRInt32 height) { @@ -300,8 +367,26 @@ WebGLContext::SetDimensions(PRInt32 width, PRInt32 height) DestroyResourcesAndContext(); gl::ContextFormat format(gl::ContextFormat::BasicRGBA32); - format.depth = 16; - format.minDepth = 1; + if (mOptions.depth) { + format.depth = 24; + format.minDepth = 16; + } + + if (mOptions.stencil) { + format.stencil = 8; + format.minStencil = 8; + } + + if (!mOptions.alpha) { + // Select 565; we won't/shouldn't hit this on the desktop, + // but let mobile know we're ok with it. + format.red = 5; + format.green = 6; + format.blue = 5; + + format.alpha = 0; + format.minAlpha = 0; + } nsCOMPtr prefService = do_GetService(NS_PREFSERVICE_CONTRACTID); NS_ENSURE_TRUE(prefService != nsnull, NS_ERROR_FAILURE); @@ -421,6 +506,7 @@ WebGLContext::SetDimensions(PRInt32 width, PRInt32 height) mWidth = width; mHeight = height; mResetLayer = PR_TRUE; + mOptionsFrozen = PR_TRUE; // increment the generation number ++mGeneration; @@ -482,17 +568,20 @@ WebGLContext::GetInputStream(const char* aMimeType, const PRUnichar* aEncoderOptions, nsIInputStream **aStream) { - return NS_ERROR_FAILURE; - - // XXX fix this -#if 0 - if (!mGLPbuffer || - !mGLPbuffer->ThebesSurface()) + NS_ASSERTION(gl, "GetInputStream on invalid context?"); + if (!gl) return NS_ERROR_FAILURE; + nsRefPtr surf = new gfxImageSurface(gfxIntSize(mWidth, mHeight), + gfxASurface::ImageFormatARGB32); + if (surf->CairoStatus() != 0) + return NS_ERROR_FAILURE; + + gl->ReadPixelsIntoImageSurface(0, 0, mWidth, mHeight, surf); + nsresult rv; const char encoderPrefix[] = "@mozilla.org/image/encoder;2?type="; - nsAutoArrayPtr conid(new (std::nothrow) char[strlen(encoderPrefix) + strlen(aMimeType) + 1]); + nsAutoArrayPtr conid(new char[strlen(encoderPrefix) + strlen(aMimeType) + 1]); if (!conid) return NS_ERROR_OUT_OF_MEMORY; @@ -504,45 +593,15 @@ WebGLContext::GetInputStream(const char* aMimeType, if (!encoder) return NS_ERROR_FAILURE; - nsAutoArrayPtr imageBuffer(new (std::nothrow) PRUint8[mWidth * mHeight * 4]); - if (!imageBuffer) - return NS_ERROR_OUT_OF_MEMORY; - - nsRefPtr imgsurf = new gfxImageSurface(imageBuffer.get(), - gfxIntSize(mWidth, mHeight), - mWidth * 4, - gfxASurface::ImageFormatARGB32); - - if (!imgsurf || imgsurf->CairoStatus()) - return NS_ERROR_FAILURE; - - nsRefPtr ctx = new gfxContext(imgsurf); - - if (!ctx || ctx->HasError()) - return NS_ERROR_FAILURE; - - nsRefPtr surf = mGLPbuffer->ThebesSurface(); - nsRefPtr pat = CanvasGLThebes::CreatePattern(surf); - gfxMatrix m; - m.Translate(gfxPoint(0.0, mGLPbuffer->Height())); - m.Scale(1.0, -1.0); - pat->SetMatrix(m); - - // XXX I don't want to use PixelSnapped here, but layout doesn't guarantee - // pixel alignment for this stuff! - ctx->NewPath(); - ctx->PixelSnappedRectangleAndSetPattern(gfxRect(0, 0, mWidth, mHeight), pat); - ctx->SetOperator(gfxContext::OPERATOR_SOURCE); - ctx->Fill(); - - rv = encoder->InitFromData(imageBuffer.get(), - mWidth * mHeight * 4, mWidth, mHeight, mWidth * 4, + rv = encoder->InitFromData(surf->Data(), + mWidth * mHeight * 4, + mWidth, mHeight, + surf->Stride(), imgIEncoder::INPUT_FORMAT_HOSTARGB, nsDependentString(aEncoderOptions)); NS_ENSURE_SUCCESS(rv, rv); return CallQueryInterface(encoder, aStream); -#endif } NS_IMETHODIMP @@ -589,7 +648,7 @@ WebGLContext::GetCanvasLayer(CanvasLayer *aOldLayer, } data.mSize = nsIntSize(mWidth, mHeight); - data.mGLBufferIsPremultiplied = PR_FALSE; + data.mGLBufferIsPremultiplied = mOptions.premultipliedAlpha ? PR_TRUE : PR_FALSE; canvasLayer->Initialize(data); PRUint32 flags = gl->CreationFormat().alpha == 0 ? Layer::CONTENT_OPAQUE : 0; @@ -602,12 +661,46 @@ WebGLContext::GetCanvasLayer(CanvasLayer *aOldLayer, return canvasLayer.forget().get(); } +NS_IMETHODIMP +WebGLContext::GetContextAttributes(jsval *aResult) +{ + JSContext *cx = nsContentUtils::GetCurrentJSContext(); + if (!cx) + return NS_ERROR_FAILURE; + + JSObject *obj = JS_NewObject(cx, NULL, NULL, NULL); + if (!obj) + return NS_ERROR_FAILURE; + + *aResult = OBJECT_TO_JSVAL(obj); + + gl::ContextFormat cf = gl->ActualFormat(); + + if (!JS_DefineProperty(cx, obj, "alpha", cf.alpha > 0 ? JSVAL_TRUE : JSVAL_FALSE, + NULL, NULL, JSPROP_ENUMERATE) || + !JS_DefineProperty(cx, obj, "depth", cf.depth > 0 ? JSVAL_TRUE : JSVAL_FALSE, + NULL, NULL, JSPROP_ENUMERATE) || + !JS_DefineProperty(cx, obj, "stencil", cf.stencil > 0 ? JSVAL_TRUE : JSVAL_FALSE, + NULL, NULL, JSPROP_ENUMERATE) || + !JS_DefineProperty(cx, obj, "antialias", JSVAL_FALSE, + NULL, NULL, JSPROP_ENUMERATE) || + !JS_DefineProperty(cx, obj, "premultipliedAlpha", + mOptions.premultipliedAlpha ? JSVAL_TRUE : JSVAL_FALSE, + NULL, NULL, JSPROP_ENUMERATE)) + { + *aResult = JSVAL_VOID; + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + // // XPCOM goop // -NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(WebGLContext, nsICanvasRenderingContextWebGL) -NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(WebGLContext, nsICanvasRenderingContextWebGL) +NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(WebGLContext, nsIDOMWebGLRenderingContext) +NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(WebGLContext, nsIDOMWebGLRenderingContext) NS_IMPL_CYCLE_COLLECTION_CLASS(WebGLContext) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WebGLContext) @@ -620,10 +713,10 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END DOMCI_DATA(CanvasRenderingContextWebGL, WebGLContext) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebGLContext) - NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextWebGL) + NS_INTERFACE_MAP_ENTRY(nsIDOMWebGLRenderingContext) NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal) NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) - NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsICanvasRenderingContextWebGL) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMWebGLRenderingContext) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CanvasRenderingContextWebGL) NS_INTERFACE_MAP_END diff --git a/content/canvas/src/WebGLContext.h b/content/canvas/src/WebGLContext.h index 79167cdab039..a9062f799687 100644 --- a/content/canvas/src/WebGLContext.h +++ b/content/canvas/src/WebGLContext.h @@ -50,7 +50,7 @@ #include "nsIDocShell.h" -#include "nsICanvasRenderingContextWebGL.h" +#include "nsIDOMWebGLRenderingContext.h" #include "nsICanvasRenderingContextInternal.h" #include "nsHTMLCanvasElement.h" #include "nsWeakReference.h" @@ -67,6 +67,7 @@ #define CONTEXT_LOST_WEBGL 0x9242 class nsIDocShell; +class nsIPropertyBag; namespace mozilla { @@ -235,8 +236,10 @@ private: class WebGLBuffer; struct WebGLVertexAttribData { + // note that these initial values are what GL initializes vertex attribs to WebGLVertexAttribData() - : buf(0), stride(0), size(0), byteOffset(0), type(0), enabled(PR_FALSE), normalized(PR_FALSE) + : buf(0), stride(0), size(4), byteOffset(0), + type(LOCAL_GL_FLOAT), enabled(PR_FALSE), normalized(PR_FALSE) { } WebGLObjectRefPtr buf; @@ -277,8 +280,41 @@ struct WebGLVertexAttribData { } }; +struct WebGLContextOptions { + // these are defaults + WebGLContextOptions() + : alpha(true), depth(true), stencil(false), + premultipliedAlpha(true), antialiasHint(false) + { } + + bool operator==(const WebGLContextOptions& other) const { + return + alpha == other.alpha && + depth == other.depth && + stencil == other.stencil && + premultipliedAlpha == other.premultipliedAlpha && + antialiasHint == other.antialiasHint; + } + + bool operator!=(const WebGLContextOptions& other) const { + return + alpha != other.alpha || + depth != other.depth || + stencil != other.stencil || + premultipliedAlpha != other.premultipliedAlpha || + antialiasHint != other.antialiasHint; + } + + bool alpha; + bool depth; + bool stencil; + + bool premultipliedAlpha; + bool antialiasHint; +}; + class WebGLContext : - public nsICanvasRenderingContextWebGL, + public nsIDOMWebGLRenderingContext, public nsICanvasRenderingContextInternal, public nsSupportsWeakReference { @@ -288,9 +324,9 @@ public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(WebGLContext, nsICanvasRenderingContextWebGL) + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(WebGLContext, nsIDOMWebGLRenderingContext) - NS_DECL_NSICANVASRENDERINGCONTEXTWEBGL + NS_DECL_NSIDOMWEBGLRENDERINGCONTEXT // nsICanvasRenderingContextInternal NS_IMETHOD SetCanvasElement(nsHTMLCanvasElement* aParentCanvas); @@ -305,6 +341,8 @@ public: nsIInputStream **aStream); NS_IMETHOD GetThebesSurface(gfxASurface **surface); NS_IMETHOD SetIsOpaque(PRBool b) { return NS_OK; }; + NS_IMETHOD SetContextOptions(nsIPropertyBag *aOptions); + NS_IMETHOD SetIsIPC(PRBool b) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHOD Redraw(const gfxRect&) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHOD Swap(mozilla::ipc::Shmem& aBack, @@ -360,9 +398,12 @@ protected: PRInt32 mWidth, mHeight; CheckedUint32 mGeneration; + WebGLContextOptions mOptions; + PRPackedBool mInvalidated; PRPackedBool mResetLayer; PRPackedBool mVerbose; + PRPackedBool mOptionsFrozen; WebGLuint mActiveTexture; WebGLenum mSynthesizedGLError; @@ -729,13 +770,14 @@ public: NS_DECL_NSIWEBGLTEXTURE protected: + friend class WebGLContext; + friend class WebGLFramebuffer; + PRBool mDeleted; WebGLuint mName; -//////////////////////////////////////////////////////////////////////////////////////////////////// -/////// everything below that point is only used for the texture completeness/npot business -/////// (sections 3.7.10 and 3.8.2 in GL ES 2.0.24 spec) -//////////////////////////////////////////////////////////////////////////////////////////////////// + // we store information about the various images that are part of + // this texture (cubemap faces, mipmap levels) struct ImageInfo { ImageInfo() : mWidth(0), mHeight(0), mFormat(0), mType(0), mIsDefined(PR_FALSE) {} @@ -1211,21 +1253,21 @@ public: return mAttachedShaders.RemoveElement(shader); } - PRBool HasBothShaderTypesAttached() { - PRBool haveVertex = PR_FALSE; - PRBool haveFrag = PR_FALSE; + PRBool HasAttachedShaderOfType(GLenum shaderType) { for (PRUint32 i = 0; i < mAttachedShaders.Length(); ++i) { - if (mAttachedShaders[i]->ShaderType() == LOCAL_GL_FRAGMENT_SHADER) - haveFrag = PR_TRUE; - else if (mAttachedShaders[i]->ShaderType() == LOCAL_GL_VERTEX_SHADER) - haveVertex = PR_TRUE; - if (haveFrag && haveVertex) + if (mAttachedShaders[i]->ShaderType() == shaderType) { return PR_TRUE; + } } - return PR_FALSE; } + PRBool HasBothShaderTypesAttached() { + return + HasAttachedShaderOfType(LOCAL_GL_VERTEX_SHADER) && + HasAttachedShaderOfType(LOCAL_GL_FRAGMENT_SHADER); + } + PRBool NextGeneration() { if (!(mGeneration+1).valid()) @@ -1328,6 +1370,7 @@ public: WebGLFramebuffer(WebGLContext *context, WebGLuint name) : WebGLContextBoundObject(context), mName(name), mDeleted(PR_FALSE), + mColorAttachment0HasAlpha(PR_FALSE), mHasDepthAttachment(PR_FALSE), mHasStencilAttachment(PR_FALSE), mHasDepthStencilAttachment(PR_FALSE) @@ -1342,6 +1385,8 @@ public: PRBool Deleted() { return mDeleted; } WebGLuint GLName() { return mName; } + PRBool ColorAttachment0HasAlpha() { return mColorAttachment0HasAlpha; } + nsresult FramebufferRenderbuffer(WebGLenum target, WebGLenum attachment, WebGLenum rbtarget, @@ -1405,15 +1450,20 @@ public: { return mContext->ErrorInvalidOperation(badAttachmentFormatMsg); } + + // ReadPixels needs alpha and size information, but only + // for COLOR_ATTACHMENT0 + if (attachment == LOCAL_GL_COLOR_ATTACHMENT0) { + setDimensions(wrb); + mColorAttachment0HasAlpha = InternalFormatHasAlpha(wrb->mInternalFormat); + } else { + mColorAttachment0HasAlpha = PR_FALSE; + } } mColorRenderbufferAttachment = wrb; break; } - // dimensions are kept for readPixels primarily, function only uses COLOR_ATTACHMENT0 - if (attachment == LOCAL_GL_COLOR_ATTACHMENT0) - setDimensions(wrb); - mContext->MakeContextCurrent(); mContext->gl->fFramebufferRenderbuffer(target, attachment, rbtarget, renderbuffername); @@ -1460,14 +1510,26 @@ public: { return mContext->ErrorInvalidEnumInfo("framebufferTexture2D: attachment", attachment); } - // nothing to do for color buffers. all textures have a color-renderable format. + + // keep data for readPixels, function only uses COLOR_ATTACHMENT0 + if (attachment == LOCAL_GL_COLOR_ATTACHMENT0) { + setDimensions(wtex); + + if (wtex) { + const WebGLTexture::ImageInfo& ia = wtex->ImageInfoAt + (level, textarget == LOCAL_GL_TEXTURE_2D + ? 0 + : textarget - LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X); + mColorAttachment0HasAlpha = InternalFormatHasAlpha(ia.mFormat); + } else { + mColorAttachment0HasAlpha = PR_FALSE; + } + } + + // nothing else to do for color buffers. all textures have a color-renderable format. break; } - // dimensions are kept for readPixels primarily, function only uses COLOR_ATTACHMENT0 - if (attachment == LOCAL_GL_COLOR_ATTACHMENT0) - setDimensions(wtex); - mContext->MakeContextCurrent(); mContext->gl->fFramebufferTexture2D(target, attachment, textarget, texturename, level); @@ -1501,6 +1563,14 @@ public: int(mHasDepthStencilAttachment) > 1; } + static PRBool InternalFormatHasAlpha(WebGLenum aInternalFormat) { + return + aInternalFormat == LOCAL_GL_RGBA || + aInternalFormat == LOCAL_GL_ALPHA || + aInternalFormat == LOCAL_GL_RGBA4 || + aInternalFormat == LOCAL_GL_RGB5_A1; + } + protected: // protected because WebGLContext should only call InitializeRenderbuffers @@ -1605,7 +1675,8 @@ protected: } WebGLuint mName; - PRBool mDeleted; + PRPackedBool mDeleted; + PRPackedBool mColorAttachment0HasAlpha; // we only store pointers to attached renderbuffers, not to attached textures, because // we will only need to initialize renderbuffers. Textures are already initialized. diff --git a/content/canvas/src/WebGLContextGL.cpp b/content/canvas/src/WebGLContextGL.cpp index ec972657a4d4..af6267340b44 100644 --- a/content/canvas/src/WebGLContextGL.cpp +++ b/content/canvas/src/WebGLContextGL.cpp @@ -123,18 +123,17 @@ WebGLProgram::GetUniformLocationObject(GLint glLocation) return loc.forget(); } +static PRBool +InternalFormatHasAlpha(WebGLenum aInternalFormat) { + return aInternalFormat == LOCAL_GL_RGBA4 || + aInternalFormat == LOCAL_GL_RGB5_A1; +} + // // WebGL API // -/* void present (); */ -NS_IMETHODIMP -WebGLContext::Present() -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - /* void GlActiveTexture (in GLenum texture); */ NS_IMETHODIMP WebGLContext::ActiveTexture(WebGLenum texture) @@ -163,6 +162,12 @@ WebGLContext::AttachShader(nsIWebGLProgram *pobj, nsIWebGLShader *shobj) !GetConcreteObjectAndGLName("attachShader: shader", shobj, &shader, &shadername)) return NS_OK; + // Per GLSL ES 2.0, we can only have one of each type of shader + // attached. This renders the next test somewhat moot, but we'll + // leave it for when we support more than one shader of each type. + if (program->HasAttachedShaderOfType(shader->ShaderType())) + return ErrorInvalidOperation("AttachShader: only one of each type of shader may be attached to a program"); + if (!program->AttachShader(shader)) return ErrorInvalidOperation("AttachShader: shader is already attached"); @@ -540,6 +545,11 @@ WebGLContext::Clear(PRUint32 mask) if (mBoundFramebuffer && !mBoundFramebuffer->CheckAndInitializeRenderbuffers()) return NS_OK; + PRUint32 m = mask & (LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT | LOCAL_GL_STENCIL_BUFFER_BIT); + if (mask != m) { + return ErrorInvalidValue("clear: invalid mask bits"); + } + gl->fClear(mask); Invalidate(); @@ -1433,7 +1443,6 @@ WebGLContext::GetParameter(PRUint32 pname, nsIVariant **retval) case LOCAL_GL_CULL_FACE_MODE: case LOCAL_GL_FRONT_FACE: case LOCAL_GL_ACTIVE_TEXTURE: - case LOCAL_GL_DEPTH_CLEAR_VALUE: case LOCAL_GL_STENCIL_CLEAR_VALUE: case LOCAL_GL_STENCIL_FUNC: case LOCAL_GL_STENCIL_REF: @@ -1521,6 +1530,7 @@ WebGLContext::GetParameter(PRUint32 pname, nsIVariant **retval) break; // float + case LOCAL_GL_DEPTH_CLEAR_VALUE: case LOCAL_GL_LINE_WIDTH: case LOCAL_GL_POLYGON_OFFSET_FACTOR: case LOCAL_GL_POLYGON_OFFSET_UNITS: @@ -2470,30 +2480,31 @@ WebGLContext::ReadPixels_base(WebGLint x, WebGLint y, WebGLsizei width, WebGLsiz WebGLsizei boundHeight = mBoundFramebuffer ? mBoundFramebuffer->height() : mHeight; PRUint32 size = 0; + bool badFormat = false, badType = false; switch (format) { - case LOCAL_GL_ALPHA: - size = 1; - break; - case LOCAL_GL_RGB: - size = 3; - break; - case LOCAL_GL_RGBA: + case LOCAL_GL_RGBA: size = 4; break; - default: - return ErrorInvalidEnumInfo("readPixels: format", format); + default: + badFormat = true; + break; } switch (type) { -// case LOCAL_GL_UNSIGNED_SHORT_4_4_4_4: -// case LOCAL_GL_UNSIGNED_SHORT_5_5_5_1: -// case LOCAL_GL_UNSIGNED_SHORT_5_6_5: - case LOCAL_GL_UNSIGNED_BYTE: + case LOCAL_GL_UNSIGNED_BYTE: + break; + default: + badType = true; break; - default: - return ErrorInvalidEnumInfo("ReadPixels: type", type); } + if (badFormat && badType) + return ErrorInvalidOperation("readPixels: bad format and type"); + if (badFormat) + return ErrorInvalidEnumInfo("readPixels: format", format); + if (badType) + return ErrorInvalidEnumInfo("ReadPixels: type", type); + CheckedUint32 checked_plainRowSize = CheckedUint32(width) * size; PRUint32 packAlignment = mPixelStorePackAlignment; @@ -2579,6 +2590,51 @@ WebGLContext::ReadPixels_base(WebGLint x, WebGLint y, WebGLsizei width, WebGLsiz } delete [] subrect_data; } + + // if we're reading alpha, we may need to do fixup + if (format == LOCAL_GL_ALPHA || + format == LOCAL_GL_RGBA) + { + PRBool needAlphaFixup; + if (mBoundFramebuffer) { + needAlphaFixup = !mBoundFramebuffer->ColorAttachment0HasAlpha(); + } else { + needAlphaFixup = gl->ActualFormat().alpha == 0; + } + + if (needAlphaFixup) { + if (format == LOCAL_GL_ALPHA && type == LOCAL_GL_UNSIGNED_BYTE) { + // this is easy; it's an 0xff memset per row + PRUint8 *row = (PRUint8*)data; + for (GLint j = 0; j < height; ++j) { + memset(row, 0xff, checked_plainRowSize.value()); + row += checked_alignedRowSize.value(); + } + } else if (format == LOCAL_GL_RGBA && type == LOCAL_GL_UNSIGNED_BYTE) { + // this is harder, we need to just set the alpha byte here + PRUint8 *row = (PRUint8*)data; + for (GLint j = 0; j < height; ++j) { + PRUint8 *rowp = row; +#ifdef IS_LITTLE_ENDIAN + // offset to get the alpha byte; we're always going to + // move by 4 bytes + rowp += 3; +#endif + PRUint8 *endrowp = rowp + 4 * width; + while (rowp != endrowp) { + *rowp = 0xff; + rowp += 4; + } + + row += checked_alignedRowSize.value(); + } + } else { + NS_WARNING("Unhandled case, how'd we get here?"); + return NS_ERROR_FAILURE; + } + } + } + return NS_OK; } @@ -3767,7 +3823,7 @@ WebGLContext::TexSubImage2D_base(WebGLenum target, WebGLint level, PRUint32 bytesNeeded = checked_neededByteLength.value(); if (byteLength < bytesNeeded) - return ErrorInvalidValue("texSubImage2D: not enough data for operation (need %d, have %d)", bytesNeeded, byteLength); + return ErrorInvalidOperation("texSubImage2D: not enough data for operation (need %d, have %d)", bytesNeeded, byteLength); WebGLTexture *tex = activeBoundTextureForTarget(target); @@ -3880,129 +3936,6 @@ WebGLContext::TexSubImage2D_dom(WebGLenum target, WebGLint level, srcFormat, PR_TRUE); } -#if 0 -// ImageData getImageData (in float x, in float y, in float width, in float height); -NS_IMETHODIMP -WebGLContext::GetImageData(PRUint32 x, PRUint32 y, PRUint32 w, PRUint32 h) -{ - // disabled due to win32 linkage issues with thebes symbols and NS_RELEASE - return NS_ERROR_FAILURE; - -#if 0 - NativeJSContext js; - if (NS_FAILED(js.error)) - return js.error; - - if (js.argc != 4) return NS_ERROR_INVALID_ARG; - - if (!mGLPbuffer || - !mGLPbuffer->ThebesSurface()) - return NS_ERROR_FAILURE; - - if (!mCanvasElement) - return NS_ERROR_FAILURE; - - if (HTMLCanvasElement()->IsWriteOnly() && !IsCallerTrustedForRead()) { - // XXX ERRMSG we need to report an error to developers here! (bug 329026) - return NS_ERROR_DOM_SECURITY_ERR; - } - - JSContext *ctx = js.ctx; - - if (!CanvasUtils::CheckSaneSubrectSize (x, y, w, h, mWidth, mHeight)) - return NS_ERROR_DOM_SYNTAX_ERR; - - nsAutoArrayPtr surfaceData (new (std::nothrow) PRUint8[w * h * 4]); - int surfaceDataStride = w*4; - int surfaceDataOffset = 0; - - if (!surfaceData) - return NS_ERROR_OUT_OF_MEMORY; - - nsRefPtr tmpsurf = new gfxImageSurface(surfaceData, - gfxIntSize(w, h), - w * 4, - gfxASurface::ImageFormatARGB32); - if (!tmpsurf || tmpsurf->CairoStatus()) - return NS_ERROR_FAILURE; - - nsRefPtr tmpctx = new gfxContext(tmpsurf); - - if (!tmpctx || tmpctx->HasError()) - return NS_ERROR_FAILURE; - - nsRefPtr surf = mGLPbuffer->ThebesSurface(); - nsRefPtr pat = CanvasGLThebes::CreatePattern(surf); - gfxMatrix m; - m.Translate(gfxPoint(x, mGLPbuffer->Height()-y)); - m.Scale(1.0, -1.0); - pat->SetMatrix(m); - - // XXX I don't want to use PixelSnapped here, but layout doesn't guarantee - // pixel alignment for this stuff! - tmpctx->NewPath(); - tmpctx->PixelSnappedRectangleAndSetPattern(gfxRect(0, 0, w, h), pat); - tmpctx->SetOperator(gfxContext::OPERATOR_SOURCE); - tmpctx->Fill(); - - tmpctx = nsnull; - tmpsurf = nsnull; - - PRUint32 len = w * h * 4; - if (len > (((PRUint32)0xfff00000)/sizeof(jsval))) - return NS_ERROR_INVALID_ARG; - - nsAutoArrayPtr jsvector(new (std::nothrow) jsval[w * h * 4]); - if (!jsvector) - return NS_ERROR_OUT_OF_MEMORY; - jsval *dest = jsvector.get(); - PRUint8 *row; - for (PRUint32 j = 0; j < h; j++) { - row = surfaceData + surfaceDataOffset + (surfaceDataStride * j); - for (PRUint32 i = 0; i < w; i++) { - // XXX Is there some useful swizzle MMX we can use here? - // I guess we have to INT_TO_JSVAL still -#ifdef IS_LITTLE_ENDIAN - PRUint8 b = *row++; - PRUint8 g = *row++; - PRUint8 r = *row++; - PRUint8 a = *row++; -#else - PRUint8 a = *row++; - PRUint8 r = *row++; - PRUint8 g = *row++; - PRUint8 b = *row++; -#endif - // Convert to non-premultiplied color - if (a != 0) { - r = (r * 255) / a; - g = (g * 255) / a; - b = (b * 255) / a; - } - - *dest++ = INT_TO_JSVAL(r); - *dest++ = INT_TO_JSVAL(g); - *dest++ = INT_TO_JSVAL(b); - *dest++ = INT_TO_JSVAL(a); - } - } - - JSObject *dataArray = JS_NewArrayObject(ctx, w*h*4, jsvector); - if (!dataArray) - return NS_ERROR_OUT_OF_MEMORY; - - JSObjectHelper retobj(&js); - retobj.DefineProperty("width", w); - retobj.DefineProperty("height", h); - retobj.DefineProperty("data", dataArray); - - js.SetRetVal(retobj); - - return NS_OK; -#endif -} -#endif - PRBool BaseTypeAndSizeFromUniformType(WebGLenum uType, WebGLenum *baseType, WebGLint *unitSize) { diff --git a/content/canvas/src/WebGLContextNotSupported.cpp b/content/canvas/src/WebGLContextNotSupported.cpp index 7e034c3c3f0d..0e4c16bc4d2e 100644 --- a/content/canvas/src/WebGLContextNotSupported.cpp +++ b/content/canvas/src/WebGLContextNotSupported.cpp @@ -36,12 +36,12 @@ * * ***** END LICENSE BLOCK ***** */ -#include "nsICanvasRenderingContextWebGL.h" +#include "nsIDOMWebGLRenderingContext.h" #include "nsDOMClassInfoID.h" #define DUMMY(func,rtype) nsresult func (rtype ** aResult) { return NS_ERROR_FAILURE; } -DUMMY(NS_NewCanvasRenderingContextWebGL, nsICanvasRenderingContextWebGL) +DUMMY(NS_NewCanvasRenderingContextWebGL, nsIDOMWebGLRenderingContext) DOMCI_DATA(CanvasRenderingContextWebGL, void) DOMCI_DATA(WebGLBuffer, void) diff --git a/content/canvas/test/webgl/README.mozilla b/content/canvas/test/webgl/README.mozilla index 1d5433e513f9..13b83daa16c4 100644 --- a/content/canvas/test/webgl/README.mozilla +++ b/content/canvas/test/webgl/README.mozilla @@ -1,6 +1,6 @@ This is a local copy of the WebGL conformance suite. - SVN revision: 11955 + SVN revision: 12905 The canonical location for this testsuite is: diff --git a/content/canvas/test/webgl/conformance/00_testFIXME_list.txt b/content/canvas/test/webgl/conformance/00_testFIXME_list.txt index f98f751dfbbb..7912f6548aed 100644 --- a/content/canvas/test/webgl/conformance/00_testFIXME_list.txt +++ b/content/canvas/test/webgl/conformance/00_testFIXME_list.txt @@ -7,7 +7,6 @@ buffer-data-array-buffer.html canvas-test.html constants.html context-attributes-alpha-depth-stencil-antialias.html -context-attributes.html context-type-test.html copy-tex-image-and-sub-image-2d.html draw-arrays-out-of-bounds.html @@ -17,11 +16,13 @@ framebuffer-object-attachment.html framebuffer-test.html get-active-test.html gl-bind-attrib-location-test.html +gl-clear.html gl-drawelements.html gl-enable-enum-test.html gl-enable-vertex-attrib.html gl-enum-tests.html gl-get-active-attribute.html +gl-get-active-uniform.html gl-get-calls.html gl-getstring.html gl-object-get-calls.html @@ -45,10 +46,13 @@ index-validation-with-resized-buffer.html index-validation.html invalid-UTF-16.html invalid-passed-params.html +is-object.html methods.html more-than-65536-points.html null-object-behaviour.html null-uniform-location.html +object-deletion-behaviour.html +origin-clean-conformance.html point-size.html program-test.html read-pixels-pack-alignment.html @@ -72,7 +76,9 @@ texture-formats-test.html texture-npot.html texture-transparent-pixels-initialized.html triangle.html -#uniform-location.html +type-conversion-test.html +uniform-location.html uniform-samplers-test.html uninitialized-test.html viewport-unchanged-upon-resize.html +webgl-specific.html diff --git a/content/canvas/test/webgl/conformance/00_test_list.txt b/content/canvas/test/webgl/conformance/00_test_list.txt deleted file mode 100644 index 640d55244c93..000000000000 --- a/content/canvas/test/webgl/conformance/00_test_list.txt +++ /dev/null @@ -1,82 +0,0 @@ -array-buffer-crash.html -array-buffer-view-crash.html -array-unit-tests.html -bad-arguments-test.html -buffer-bind-test.html -buffer-data-array-buffer.html -canvas-test.html -constants.html -context-attributes-alpha-depth-stencil-antialias.html -context-attributes.html -context-type-test.html -copy-tex-image-and-sub-image-2d.html -draw-arrays-out-of-bounds.html -draw-elements-out-of-bounds.html -error-reporting.html -framebuffer-object-attachment.html -framebuffer-test.html -get-active-test.html -gl-bind-attrib-location-test.html -gl-clear.html -gl-drawelements.html -gl-enable-enum-test.html -gl-enable-vertex-attrib.html -gl-enum-tests.html -gl-get-active-attribute.html -gl-get-active-uniform.html -gl-get-calls.html -gl-getstring.html -gl-object-get-calls.html -gl-pixelstorei.html -gl-scissor-test.html -gl-shader-test.html -gl-teximage.html -gl-uniform-arrays.html -gl-uniform-bool.html -gl-uniformmatrix4fv.html -gl-unknown-uniform.html -gl-vertex-attrib.html -gl-vertexattribpointer.html -glsl-2types-of-textures-on-same-unit.html -glsl-conformance.html -incorrect-context-object-behaviour.html -index-validation-copies-indices.html -index-validation-crash-with-buffer-sub-data.html -index-validation-verifies-too-many-indices.html -index-validation-with-resized-buffer.html -index-validation.html -invalid-UTF-16.html -invalid-passed-params.html -methods.html -more-than-65536-points.html -null-object-behaviour.html -null-uniform-location.html -object-deletion-behaviour.html -origin-clean-conformance.html -point-size.html -program-test.html -read-pixels-pack-alignment.html -read-pixels-test.html -renderbuffer-initialization.html -resource-sharing-test.html -tex-image-and-sub-image-2d-with-array-buffer-view.html -tex-image-and-sub-image-2d-with-image-data.html -tex-image-and-sub-image-2d-with-image.html -tex-image-and-sub-image-2d-with-video.html -tex-image-and-uniform-binding-bugs.html -tex-image-with-format-and-type.html -tex-image-with-invalid-data.html -tex-input-validation.html -tex-sub-image-2d.html -texparameter-test.html -texture-active-bind-2.html -texture-active-bind.html -texture-complete.html -texture-formats-test.html -texture-npot.html -texture-transparent-pixels-initialized.html -triangle.html -uniform-location.html -uniform-samplers-test.html -uninitialized-test.html -viewport-unchanged-upon-resize.html diff --git a/content/canvas/test/webgl/conformance/array-unit-tests.html b/content/canvas/test/webgl/conformance/array-unit-tests.html index d5c72a1d490a..54c0f8e87ca2 100644 --- a/content/canvas/test/webgl/conformance/array-unit-tests.html +++ b/content/canvas/test/webgl/conformance/array-unit-tests.html @@ -438,6 +438,15 @@ function testConstructionWithUnalignedOffset(type, name, elementSizeInBytes) { } } +function testConstructionWithUnalignedLength(type, name, elementSizeInBytes) { + if (elementSizeInBytes > 1) { + shouldThrowIndexSizeErr(function() { + var buffer = new ArrayBuffer(elementSizeInBytes + 1); + var array = new type(buffer, 0); + }, "Construction of " + name + " with unaligned length"); + } +} + function testConstructionOfHugeArray(type, name, sz) { if (sz == 1) return; @@ -671,6 +680,7 @@ function runTests() { testConstructionWithOutOfRangeValues(type, name); testConstructionWithNegativeOutOfRangeValues(type, name); testConstructionWithUnalignedOffset(type, name, testCase.elementSizeInBytes); + testConstructionWithUnalignedLength(type, name, testCase.elementSizeInBytes); testConstructionOfHugeArray(type, name, testCase.elementSizeInBytes); testConstructionWithBothArrayBufferAndLength(type, name, testCase.elementSizeInBytes); testSlicingWithOutOfRangeValues(type, name, testCase.elementSizeInBytes); diff --git a/content/canvas/test/webgl/conformance/bad-arguments-test.html b/content/canvas/test/webgl/conformance/bad-arguments-test.html index f2a9b5fa1aa1..3b38115952fc 100644 --- a/content/canvas/test/webgl/conformance/bad-arguments-test.html +++ b/content/canvas/test/webgl/conformance/bad-arguments-test.html @@ -79,7 +79,6 @@ for (var i = 0; i < arguments.length; ++i) { argument = arguments[i].value; func("context.compileShader(argument)"); func("context.linkProgram(argument)"); - func("context.attachShader(argument)"); func("context.attachShader(program, argument)"); func("context.attachShader(argument, shader)"); func("context.detachShader(program, argument)"); diff --git a/content/canvas/test/webgl/conformance/buffer-data-array-buffer.html b/content/canvas/test/webgl/conformance/buffer-data-array-buffer.html index eb85d7c9b363..0be24dc7fb59 100644 --- a/content/canvas/test/webgl/conformance/buffer-data-array-buffer.html +++ b/content/canvas/test/webgl/conformance/buffer-data-array-buffer.html @@ -33,6 +33,10 @@ glErrorShouldBe(gl, gl.INVALID_OPERATION); gl.bindBuffer(gl.ARRAY_BUFFER, buf); glErrorShouldBe(gl, gl.NO_ERROR); +// This should not crash, but the selection of the overload is ambiguous per Web IDL. +gl.bufferData(gl.ARRAY_BUFFER, null, gl.STATIC_DRAW); +gl.getError(); + gl.bufferData(gl.ARRAY_BUFFER, array, gl.STATIC_DRAW); glErrorShouldBe(gl, gl.NO_ERROR); @@ -41,6 +45,9 @@ array = new ArrayBuffer(64); gl.bufferSubData(gl.ARRAY_BUFFER, 10, array); glErrorShouldBe(gl, gl.NO_ERROR); +gl.bufferSubData(gl.ARRAY_BUFFER, 10, null); +glErrorShouldBe(gl, gl.NO_ERROR); + successfullyParsed = true; diff --git a/content/canvas/test/webgl/conformance/constants.html b/content/canvas/test/webgl/conformance/constants.html index 1dff6df0f73a..380092b9abda 100644 --- a/content/canvas/test/webgl/conformance/constants.html +++ b/content/canvas/test/webgl/conformance/constants.html @@ -375,10 +375,6 @@ VERTEX_ATTRIB_ARRAY_NORMALIZED : 0x886A, VERTEX_ATTRIB_ARRAY_POINTER : 0x8645, VERTEX_ATTRIB_ARRAY_BUFFER_BINDING : 0x889F, - /* Read Format */ -IMPLEMENTATION_COLOR_READ_TYPE : 0x8B9A, -IMPLEMENTATION_COLOR_READ_FORMAT : 0x8B9B, - /* Shader Source */ COMPILE_STATUS : 0x8B81, INFO_LOG_LENGTH : 0x8B84, @@ -403,6 +399,7 @@ RGB565 : 0x8D62, DEPTH_COMPONENT16 : 0x81A5, STENCIL_INDEX : 0x1901, STENCIL_INDEX8 : 0x8D48, +DEPTH_STENCIL : 0x84F9, RENDERBUFFER_WIDTH : 0x8D42, RENDERBUFFER_HEIGHT : 0x8D43, @@ -422,6 +419,7 @@ FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE : 0x8CD3, COLOR_ATTACHMENT0 : 0x8CE0, DEPTH_ATTACHMENT : 0x8D00, STENCIL_ATTACHMENT : 0x8D20, +DEPTH_STENCIL_ATTACHMENT : 0x821A, NONE : 0, @@ -435,7 +433,14 @@ FRAMEBUFFER_BINDING : 0x8CA6, RENDERBUFFER_BINDING : 0x8CA7, MAX_RENDERBUFFER_SIZE : 0x84E8, -INVALID_FRAMEBUFFER_OPERATION : 0x0506 +INVALID_FRAMEBUFFER_OPERATION : 0x0506, + +/* WebGL-specific enums */ +UNPACK_FLIP_Y_WEBGL : 0x9240, +UNPACK_PREMULTIPLY_ALPHA_WEBGL : 0x9241, +CONTEXT_LOST_WEBGL : 0x9242, +UNPACK_COLORSPACE_CONVERSION_WEBGL : 0x9243, +BROWSER_DEFAULT_WEBGL : 0x9244 }; function assertProperty(v, p) { diff --git a/content/canvas/test/webgl/conformance/context-attributes-alpha-depth-stencil-antialias.html b/content/canvas/test/webgl/conformance/context-attributes-alpha-depth-stencil-antialias.html index edbdc4ad764a..e97025f9ab4d 100644 --- a/content/canvas/test/webgl/conformance/context-attributes-alpha-depth-stencil-antialias.html +++ b/content/canvas/test/webgl/conformance/context-attributes-alpha-depth-stencil-antialias.html @@ -54,66 +54,14 @@ function init() function getWebGL(canvasName, contextAttribs, clearColor, clearDepth, clearStencil) { - var canvas = document.getElementById(canvasName); - var gl = canvas.getContext("experimental-webgl", contextAttribs); - if (!gl) { - alert("No WebGL context found"); - return null; + var context = initWebGL(canvasName, "vshader", "fshader", ["pos", "colorIn"], clearColor, clearDepth, contextAttribs); + if (context) { + context.clearStencil(clearStencil); + context.enable(context.STENCIL_TEST); + context.disable(context.BLEND); + context.clear(context.COLOR_BUFFER_BIT | context.DEPTH_BUFFER_BIT | context.STENCIL_BUFFER_BIT); } - var actualContextAttribs = gl.getContextAttributes(); - - // Add a console - gl.console = ("console" in window) ? window.console : { log: function() { } }; - - // create our shaders - var vertexShader = loadShader(gl, "vshader"); - var fragmentShader = loadShader(gl, "fshader"); - - if (!vertexShader || !fragmentShader) - return null; - - // Create the program object - gl.program = gl.createProgram(); - - if (!gl.program) - return null; - - // Attach our two shaders to the program - gl.attachShader(gl.program, vertexShader); - gl.attachShader(gl.program, fragmentShader); - - // Bind attributes - var attribs = [ "pos", "colorIn" ]; - for (var i in attribs) - gl.bindAttribLocation(gl.program, i, attribs[i]); - - // Link the program - gl.linkProgram(gl.program); - - // Check the link status - var linked = gl.getProgramParameter(gl.program, gl.LINK_STATUS); - if (!linked) { - // something went wrong with the link - var error = gl.getProgramInfoLog (gl.program); - gl.console.log("Error in program linking:"+error); - - gl.deleteProgram(gl.program); - gl.deleteProgram(fragmentShader); - gl.deleteProgram(vertexShader); - - return null; - } - - gl.useProgram(gl.program); - - gl.clearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); - gl.clearDepth(clearDepth); - gl.clearStencil(clearStencil); - gl.enable(gl.DEPTH_TEST); - gl.enable(gl.STENCIL_TEST); - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); - - return gl; + return context; } function drawAndReadPixel(gl, vertices, colors, x, y) @@ -146,11 +94,6 @@ function testAlpha(alpha) else shouldBeNonNull("webGL = getWebGL('alphaOff', { alpha: false, depth: false, stencil: false, antialias: false }, [ 0, 0, 0, 0 ], 1, 0)"); shouldBeNonNull("contextAttribs = webGL.getContextAttributes()"); - shouldBe("contextAttribs.alpha", (alpha ? "true" : "false")); - shouldBe("contextAttribs.depth", "false"); - shouldBe("contextAttribs.stencil", "false"); - shouldBe("contextAttribs.antialias", "false"); - shouldBe("contextAttribs.premultipliedAlpha", "true"); var buf = new Uint8Array(1 * 1 * 4); webGL.readPixels(0, 0, 1, 1, webGL.RGBA, webGL.UNSIGNED_BYTE, buf); @@ -158,7 +101,7 @@ function testAlpha(alpha) pixel[1] = buf[1]; pixel[2] = buf[2]; pixel[3] = buf[3]; - correctColor = (alpha ? [0, 0, 0, 0] : [0, 0, 0, 255]); + correctColor = (contextAttribs.alpha ? [0, 0, 0, 0] : [0, 0, 0, 255]); shouldBe("pixel", "correctColor"); } @@ -170,11 +113,6 @@ function testDepth(depth) else shouldBeNonNull("webGL = getWebGL('depthOff', { depth: false, stencil: false, antialias: false }, [ 0, 0, 0, 1 ], 1, 0)"); shouldBeNonNull("contextAttribs = webGL.getContextAttributes()"); - shouldBe("contextAttribs.depth", (depth ? "true" : "false")); - shouldBe("contextAttribs.alpha", "true"); - shouldBe("contextAttribs.stencil", "false"); - shouldBe("contextAttribs.antialias", "false"); - shouldBe("contextAttribs.premultipliedAlpha", "true"); webGL.depthFunc(webGL.NEVER); @@ -198,7 +136,7 @@ function testDepth(depth) pixel[1] = buf[1]; pixel[2] = buf[2]; pixel[3] = buf[3]; - correctColor = (depth ? [0, 0, 0, 255] : [255, 0, 0, 255]); + correctColor = (contextAttribs.depth ? [0, 0, 0, 255] : [255, 0, 0, 255]); shouldBe("pixel", "correctColor"); } @@ -210,11 +148,6 @@ function testStencil(stencil) else shouldBeNonNull("webGL = getWebGL('stencilOff', { depth: false, stencil: false, antialias: false }, [ 0, 0, 0, 1 ], 1, 0)"); shouldBeNonNull("contextAttribs = webGL.getContextAttributes()"); - // If EXT_packed_depth_stencil is supported, both depth & stencil will be true; otherwise, both will be false. - shouldBe("contextAttribs.depth == contextAttribs.stencil", "true"); - shouldBe("contextAttribs.alpha", "true"); - shouldBe("contextAttribs.antialias", "false"); - shouldBe("contextAttribs.premultipliedAlpha", "true"); webGL.depthFunc(webGL.ALWAYS); @@ -241,10 +174,7 @@ function testStencil(stencil) pixel[1] = buf[1]; pixel[2] = buf[2]; pixel[3] = buf[3]; - correctColor = (stencil ? [0, 0, 0, 255] : [255, 0, 0, 255]); - // If stencil is requested but not supported, we fake the effect. - if (stencil && !contextAttribs.stencil) - pixel[0] = 0; + correctColor = (contextAttribs.stencil ? [0, 0, 0, 255] : [255, 0, 0, 255]); shouldBe("pixel", "correctColor"); } @@ -256,11 +186,6 @@ function testAntialias(antialias) else shouldBeNonNull("webGL = getWebGL('antialiasOff', { depth: false, stencil: false, alpha: false, antialias: false }, [ 0, 0, 0, 1 ], 1, 0)"); shouldBeNonNull("contextAttribs = webGL.getContextAttributes()"); - shouldBe("contextAttribs.depth", "false"); - shouldBe("contextAttribs.stencil", "false"); - shouldBe("contextAttribs.alpha", "false"); - shouldBe("contextAttribs.antialias == true || contextAttribs.antialias == false", "true"); - shouldBe("contextAttribs.premultipliedAlpha", "true"); var vertices = new Float32Array([ 1.0, 1.0, 0.0, @@ -272,10 +197,7 @@ function testAntialias(antialias) 255, 0, 0, 255]); var buf = drawAndReadPixel(webGL, vertices, colors, 0, 0); pixel[0] = buf[0]; - // If antialias is requested but not supported, we fake the effect. - if (antialias && !contextAttribs.antialias) - pixel[0] = 127; - shouldBe("pixel[0] == 255 || pixel[0] == 0", (antialias ? "false" : "true")); + shouldBe("pixel[0] != 255 && pixel[0] != 0", "contextAttribs.antialias"); } function runTest() diff --git a/content/canvas/test/webgl/conformance/context-attributes.html b/content/canvas/test/webgl/conformance/context-attributes.html deleted file mode 100644 index ec8827710d73..000000000000 --- a/content/canvas/test/webgl/conformance/context-attributes.html +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - -
-
- - - - - - diff --git a/content/canvas/test/webgl/conformance/copy-tex-image-and-sub-image-2d.html b/content/canvas/test/webgl/conformance/copy-tex-image-and-sub-image-2d.html index 141d4e8bb441..fb0dfa7c3cb7 100644 --- a/content/canvas/test/webgl/conformance/copy-tex-image-and-sub-image-2d.html +++ b/content/canvas/test/webgl/conformance/copy-tex-image-and-sub-image-2d.html @@ -88,7 +88,7 @@ function runTestIteration(antialias) gl.enableVertexAttribArray(0); gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(1); - gl.vertexAttribPointer(1, 2, gl.FLOAT, gl.FALSE, 0, texCoordOffset); + gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 0, texCoordOffset); gl.colorMask(1, 1, 1, 0); gl.disable(gl.BLEND); diff --git a/content/canvas/test/webgl/conformance/draw-arrays-out-of-bounds.html b/content/canvas/test/webgl/conformance/draw-arrays-out-of-bounds.html index e2ae5c7a796f..5474d79ab6e0 100644 --- a/content/canvas/test/webgl/conformance/draw-arrays-out-of-bounds.html +++ b/content/canvas/test/webgl/conformance/draw-arrays-out-of-bounds.html @@ -50,10 +50,10 @@ shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(co shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 0, 10000)"); shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 0, 10000000000000)"); shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, 0, -1)"); -shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 1, 0)"); +shouldGenerateGLError(context, context.NO_ERROR, "context.drawArrays(context.TRIANGLES, 1, 0)"); shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, -1, 0)"); shouldGenerateGLError(context, context.NO_ERROR, "context.drawArrays(context.TRIANGLES, 0, 0)"); -shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 100, 0)"); +shouldGenerateGLError(context, context.NO_ERROR, "context.drawArrays(context.TRIANGLES, 100, 0)"); shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, 1, -1)"); shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, -1, 1)"); @@ -69,7 +69,7 @@ shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(co shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, 0, -1)"); shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, -1, 0)"); shouldGenerateGLError(context, context.NO_ERROR, "context.drawArrays(context.TRIANGLES, 0, 0)"); -shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawArrays(context.TRIANGLES, 100, 0)"); +shouldGenerateGLError(context, context.NO_ERROR, "context.drawArrays(context.TRIANGLES, 100, 0)"); shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, 1, -1)"); shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawArrays(context.TRIANGLES, -1, 1)"); diff --git a/content/canvas/test/webgl/conformance/draw-elements-out-of-bounds.html b/content/canvas/test/webgl/conformance/draw-elements-out-of-bounds.html index 763c4a528a46..5957a8f47a75 100644 --- a/content/canvas/test/webgl/conformance/draw-elements-out-of-bounds.html +++ b/content/canvas/test/webgl/conformance/draw-elements-out-of-bounds.html @@ -74,7 +74,7 @@ shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawElements(cont shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawElements(context.TRIANGLES, 0, context.UNSIGNED_BYTE, -1)"); shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawElements(context.TRIANGLES, -1, context.UNSIGNED_BYTE, 1)"); shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawElements(context.TRIANGLES, 1, context.UNSIGNED_BYTE, -1)"); -shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 0, context.UNSIGNED_BYTE, 4)"); +shouldGenerateGLError(context, context.NO_ERROR, "context.drawElements(context.TRIANGLES, 0, context.UNSIGNED_BYTE, 4)"); shouldGenerateGLError(context, context.INVALID_VALUE, "context.drawElements(context.TRIANGLES, 0xffffffff, context.UNSIGNED_BYTE, 0)"); shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 0x7fffffff, context.UNSIGNED_BYTE, 0)"); shouldGenerateGLError(context, context.INVALID_OPERATION, "context.drawElements(context.TRIANGLES, 0x7fffffff, context.UNSIGNED_BYTE, 0x7fffffff)"); diff --git a/content/canvas/test/webgl/conformance/framebuffer-object-attachment.html b/content/canvas/test/webgl/conformance/framebuffer-object-attachment.html index 20d2518b11f8..e5caa22f4777 100644 --- a/content/canvas/test/webgl/conformance/framebuffer-object-attachment.html +++ b/content/canvas/test/webgl/conformance/framebuffer-object-attachment.html @@ -28,25 +28,29 @@ function testAttachment(attachment, buffer, isConflicted) { shouldBeNonNull("fbo = gl.createFramebuffer()"); gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorBuffer); gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment, gl.RENDERBUFFER, buffer); - glErrorShouldBe(gl, isConflicted ? gl.INVALID_OPERATION : gl.NO_ERROR); + glErrorShouldBe(gl, gl.NO_ERROR); + if (isConflicted) { + shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_UNSUPPORTED"); + gl.clear(gl.COLOR_BUFFER_BIT); + glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION); + gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(width * height * 4)); + glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION); + } } function testAttachments(attachment0, buffer0, attachment1, buffer1, isConflicted) { shouldBeNonNull("fbo = gl.createFramebuffer()"); gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorBuffer); gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment0, gl.RENDERBUFFER, buffer0); glErrorShouldBe(gl, gl.NO_ERROR); gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment1, gl.RENDERBUFFER, buffer1); - glErrorShouldBe(gl, isConflicted ? gl.INVALID_OPERATION : gl.NO_ERROR); - if (isConflicted) { - // Detach buffer0 first using a null object, then attaching buffer1 should succeed. - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment0, gl.RENDERBUFFER, null); - glErrorShouldBe(gl, gl.NO_ERROR); - gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment1, gl.RENDERBUFFER, buffer1); - glErrorShouldBe(gl, gl.NO_ERROR); - } + glErrorShouldBe(gl, gl.NO_ERROR); + if (isConflicted) + shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_UNSUPPORTED"); } function testColorRenderbuffer(internalformat) @@ -58,10 +62,33 @@ function testColorRenderbuffer(internalformat) testAttachment(gl.COLOR_ATTACHMENT0, colorBuffer, false); } +function testDepthStencilRenderbuffer() +{ + shouldBeNonNull("depthStencilBuffer = gl.createRenderbuffer()"); + gl.bindRenderbuffer(gl.RENDERBUFFER, depthStencilBuffer); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width, height); + glErrorShouldBe(gl, gl.NO_ERROR); + shouldBe("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_WIDTH)", "width"); + shouldBe("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_HEIGHT)", "height"); + shouldBe("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_INTERNAL_FORMAT)", "gl.DEPTH_STENCIL"); + shouldBe("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_RED_SIZE)", "0"); + shouldBe("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_GREEN_SIZE)", "0"); + shouldBe("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_BLUE_SIZE)", "0"); + shouldBe("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_ALPHA_SIZE)", "0"); + shouldBeTrue("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_DEPTH_SIZE) > 0"); + shouldBeTrue("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_STENCIL_SIZE) > 0"); + glErrorShouldBe(gl, gl.NO_ERROR); + testAttachment(gl.DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer, false); +} + description("Test framebuffer object attachment behaviors"); debug("Create renderbuffers"); shouldBeNonNull("gl = create3DContext()"); +shouldBeNonNull("colorBuffer = gl.createRenderbuffer()"); +gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer); +gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, width, height); +glErrorShouldBe(gl, gl.NO_ERROR); shouldBeNonNull("depthBuffer = gl.createRenderbuffer()"); gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer); gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height); @@ -116,6 +143,9 @@ testColorRenderbuffer(gl.RGB5_A1); debug("Attach color renderbuffer with internalformat == RGB565"); testColorRenderbuffer(gl.RGB565); +debug("Create and attach depthStencil renderbuffer"); +testDepthStencilRenderbuffer(); + successfullyParsed = true; diff --git a/content/canvas/test/webgl/conformance/gl-object-get-calls.html b/content/canvas/test/webgl/conformance/gl-object-get-calls.html index 2ee92a9b32b6..da08bdc42d19 100644 --- a/content/canvas/test/webgl/conformance/gl-object-get-calls.html +++ b/content/canvas/test/webgl/conformance/gl-object-get-calls.html @@ -64,7 +64,7 @@ var shaders = gl.getAttachedShaders(standardProgram); shouldBe('shaders.length', '2'); shouldBeTrue('shaders[0] == standardVert && shaders[1] == standardFrag || shaders[1] == standardVert && shaders[0] == standardFrag'); glErrorShouldBe(gl, gl.NO_ERROR); -shouldBeUndefined('gl.getAttachedShaders(null)'); +shouldBeNull('gl.getAttachedShaders(null)'); glErrorShouldBe(gl, gl.INVALID_VALUE); shouldThrow('gl.getAttachedShaders(standardVert)'); glErrorShouldBe(gl, gl.NO_ERROR); @@ -174,9 +174,9 @@ gl.uniform3i(bval3Loc, 1, 0, 1); gl.uniform4i(bval4Loc, 1, 0, 1, 0); glErrorShouldBe(gl, gl.NO_ERROR); shouldBe('gl.getUniform(boolProgram, bvalLoc)', 'true'); -shouldBe('gl.getUniform(boolProgram, bval2Loc)', '[1, 0]'); -shouldBe('gl.getUniform(boolProgram, bval3Loc)', '[1, 0, 1]'); -shouldBe('gl.getUniform(boolProgram, bval4Loc)', '[1, 0, 1, 0]'); +shouldBe('gl.getUniform(boolProgram, bval2Loc)', '[true, false]'); +shouldBe('gl.getUniform(boolProgram, bval3Loc)', '[true, false, true]'); +shouldBe('gl.getUniform(boolProgram, bval4Loc)', '[true, false, true, false]'); // Integer uniform variables var intProgram = loadProgram(gl, "resources/intUniformShader.vert", "resources/noopUniformShader.frag"); shouldBe('gl.getProgramParameter(intProgram, gl.LINK_STATUS)', 'true'); diff --git a/content/canvas/test/webgl/conformance/gl-teximage.html b/content/canvas/test/webgl/conformance/gl-teximage.html index 218fb8a55b84..80160eec93ab 100755 --- a/content/canvas/test/webgl/conformance/gl-teximage.html +++ b/content/canvas/test/webgl/conformance/gl-teximage.html @@ -38,7 +38,8 @@ var imgURLs = [ 'resources/gray-ramp.png', 'resources/zero-alpha.png', 'resources/3x3.png', - 'resources/blue-1x1.jpg']; + 'resources/blue-1x1.jpg', + 'resources/green-2x2-16bit.png']; wtu.loadImagesAsync(imgURLs, runTests); @@ -167,6 +168,7 @@ function runTests(imgs) { debug(""); debug("Check that gamma settings don't effect 8bit pngs"); + gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, imgs['resources/gray-ramp-default-gamma.png']); glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup."); @@ -289,6 +291,19 @@ function runTests(imgs) { glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors"); + debug(""); + debug("check uploading of 16-bit images"); + gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, + imgs['resources/green-2x2-16bit.png']); + glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup"); + wtu.drawQuad(gl); + gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf); + + checkPixelRange(buf, middle, center, [ 15, 121, 0, 255], 10); + + glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors"); debug(""); successfullyParsed = true; shouldBeTrue("successfullyParsed"); diff --git a/content/canvas/test/webgl/conformance/gl-vertexattribpointer.html b/content/canvas/test/webgl/conformance/gl-vertexattribpointer.html index 4210db601b3b..9822485b16b8 100755 --- a/content/canvas/test/webgl/conformance/gl-vertexattribpointer.html +++ b/content/canvas/test/webgl/conformance/gl-vertexattribpointer.html @@ -19,7 +19,7 @@ found in the LICENSE file.
+ + + + + + + + +
+
+ + + + + diff --git a/content/canvas/test/webgl/conformance/read-pixels-pack-alignment.html b/content/canvas/test/webgl/conformance/read-pixels-pack-alignment.html index 01efa0b60393..2035894492ed 100644 --- a/content/canvas/test/webgl/conformance/read-pixels-pack-alignment.html +++ b/content/canvas/test/webgl/conformance/read-pixels-pack-alignment.html @@ -44,8 +44,6 @@ var gl = null; var array = null; var pixel = [ 0, 0, 0, 0 ]; var expectedColor = [ 0, 0, 0, 0 ]; -var pixelFormat = 0; -var pixelType = 0; function calculatePixelBytes(format, type) { @@ -168,38 +166,26 @@ function runTestIteration(format, type, packAlignment, width, height) var bytesPerPixel = calculatePixelBytes(format, type); var padding = calculatePaddingBytes(bytesPerPixel, packAlignment, width); var size = bytesPerPixel * width * height + padding * (height - 1); - var isShort = false; - switch (type) { - case gl.UNSIGNED_SHORT_5_6_5: - case gl.UNSIGNED_SHORT_4_4_4_4: - case gl.UNSIGNED_SHORT_5_5_5_1: - isShort = true; + if (type != gl.UNSIGNED_BYTE) { + throw "test error: only UNSIGNED_BYTE is valid to ReadPixels"; } - if (isShort) - size /= 2; if (size < 0) size = 0; - if (type == gl.UNSIGNED_BYTE) - array = new Uint8Array(size); - else - array = new Uint16Array(size); + array = new Uint8Array(size); gl.readPixels(0, 0, width, height, format, type, array); if (width < 0 || height < 0) { glErrorShouldBe(gl, gl.INVALID_VALUE); return; - } else { - glErrorShouldBe(gl, gl.NO_ERROR); - if (!array.length) - return; } + + glErrorShouldBe(gl, gl.NO_ERROR); + if (!array.length) + return; + // Check the last pixel of the last row. var bytesPerRow = width * bytesPerPixel + padding; var pos = bytesPerRow * (height - 1) + (width - 1) * bytesPerPixel; var numComponents = bytesPerPixel; - if (isShort) { - pos /= 2; - numComponents /= 2; - } for (var i = 0; i < numComponents; ++i) pixel[i] = array[pos + i]; for (var i = numComponents; i < 4; ++i) @@ -209,75 +195,44 @@ function runTestIteration(format, type, packAlignment, width, height) shouldBe("pixel", "expectedColor"); } -function checkSupportedPixelFormatAndType() -{ - debug("Check supported pixel format/type besides RGBA/UNSIGNED_BYTE"); - glErrorShouldBe(gl, gl.NO_ERROR); - pixelFormat = gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_FORMAT); - pixelType = gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_TYPE); - glErrorShouldBe(gl, gl.NO_ERROR); - shouldBeFalse("pixelFormat == gl.RGBA && pixelType == gl.UNSIGNED_BYTE"); -} - description('Verify readPixels() works fine with various PACK_ALIGNMENT values.'); shouldBeNonNull("gl = initWebGL('example', 'vshader', 'fshader', [ 'pos', 'colorIn' ], [ 0, 0, 0, 1 ], 1)"); gl.disable(gl.BLEND); -checkSupportedPixelFormatAndType(); +var formats = [ gl.RGBA ]; +var formatNames = [ "RGBA" ]; -debug("Testing format = RGBA and type = UNSIGNED_BYTE"); -runTestIteration(gl.RGBA, gl.UNSIGNED_BYTE, 1, 1, 2); -runTestIteration(gl.RGBA, gl.UNSIGNED_BYTE, 2, 1, 2); -runTestIteration(gl.RGBA, gl.UNSIGNED_BYTE, 4, 1, 2); -runTestIteration(gl.RGBA, gl.UNSIGNED_BYTE, 8, 1, 2); -runTestIteration(gl.RGBA, gl.UNSIGNED_BYTE, 4, 2, 2); -runTestIteration(gl.RGBA, gl.UNSIGNED_BYTE, 8, 2, 2); -runTestIteration(gl.RGBA, gl.UNSIGNED_BYTE, 4, 3, 2); -runTestIteration(gl.RGBA, gl.UNSIGNED_BYTE, 8, 3, 2); -runTestIteration(gl.RGBA, gl.UNSIGNED_BYTE, 4, 4, 2); -runTestIteration(gl.RGBA, gl.UNSIGNED_BYTE, 8, 4, 2); -runTestIteration(gl.RGBA, gl.UNSIGNED_BYTE, 8, 5, 1); -runTestIteration(gl.RGBA, gl.UNSIGNED_BYTE, 4, 5, 2); -runTestIteration(gl.RGBA, gl.UNSIGNED_BYTE, 8, 5, 2); -runTestIteration(gl.RGBA, gl.UNSIGNED_BYTE, 8, 6, 2); -runTestIteration(gl.RGBA, gl.UNSIGNED_BYTE, 8, 7, 2); -runTestIteration(gl.RGBA, gl.UNSIGNED_BYTE, 8, 8, 2); -runTestIteration(gl.RGBA, gl.UNSIGNED_BYTE, 1, 0, 0); -runTestIteration(gl.RGBA, gl.UNSIGNED_BYTE, 2, 0, 0); -runTestIteration(gl.RGBA, gl.UNSIGNED_BYTE, 4, 0, 0); -runTestIteration(gl.RGBA, gl.UNSIGNED_BYTE, 8, 0, 0); -runTestIteration(gl.RGBA, gl.UNSIGNED_BYTE, 1, -1, 1); -runTestIteration(gl.RGBA, gl.UNSIGNED_BYTE, 2, 1, -1); -runTestIteration(gl.RGBA, gl.UNSIGNED_BYTE, 4, 0, -1); -runTestIteration(gl.RGBA, gl.UNSIGNED_BYTE, 8, -1, -1); - -debug("Testing the other supported format/type combination"); -runTestIteration(pixelFormat, pixelType, 1, 1, 2); -runTestIteration(pixelFormat, pixelType, 2, 1, 2); -runTestIteration(pixelFormat, pixelType, 4, 1, 2); -runTestIteration(pixelFormat, pixelType, 8, 1, 2); -runTestIteration(pixelFormat, pixelType, 4, 2, 2); -runTestIteration(pixelFormat, pixelType, 8, 2, 2); -runTestIteration(pixelFormat, pixelType, 4, 3, 2); -runTestIteration(pixelFormat, pixelType, 8, 3, 2); -runTestIteration(pixelFormat, pixelType, 4, 4, 2); -runTestIteration(pixelFormat, pixelType, 8, 4, 2); -runTestIteration(pixelFormat, pixelType, 8, 5, 1); -runTestIteration(pixelFormat, pixelType, 4, 5, 2); -runTestIteration(pixelFormat, pixelType, 8, 5, 2); -runTestIteration(pixelFormat, pixelType, 8, 6, 2); -runTestIteration(pixelFormat, pixelType, 8, 7, 2); -runTestIteration(pixelFormat, pixelType, 8, 8, 2); -runTestIteration(pixelFormat, pixelType, 1, 0, 0); -runTestIteration(pixelFormat, pixelType, 2, 0, 0); -runTestIteration(pixelFormat, pixelType, 4, 0, 0); -runTestIteration(pixelFormat, pixelType, 8, 0, 0); -runTestIteration(pixelFormat, pixelType, 1, -1, 1); -runTestIteration(pixelFormat, pixelType, 2, 1, -1); -runTestIteration(pixelFormat, pixelType, 4, 0, -1); -runTestIteration(pixelFormat, pixelType, 8, -1, -1); +for (var i = 0; i < formats.length; ++i) { + var format = formats[i]; + debug("Testing format = " + formatNames[i] + " and type = UNSIGNED_BYTE"); + runTestIteration(format, gl.UNSIGNED_BYTE, 1, 1, 2); + runTestIteration(format, gl.UNSIGNED_BYTE, 2, 1, 2); + runTestIteration(format, gl.UNSIGNED_BYTE, 4, 1, 2); + runTestIteration(format, gl.UNSIGNED_BYTE, 8, 1, 2); + runTestIteration(format, gl.UNSIGNED_BYTE, 4, 2, 2); + runTestIteration(format, gl.UNSIGNED_BYTE, 8, 2, 2); + runTestIteration(format, gl.UNSIGNED_BYTE, 4, 3, 2); + runTestIteration(format, gl.UNSIGNED_BYTE, 8, 3, 2); + runTestIteration(format, gl.UNSIGNED_BYTE, 4, 4, 2); + runTestIteration(format, gl.UNSIGNED_BYTE, 8, 4, 2); + runTestIteration(format, gl.UNSIGNED_BYTE, 8, 5, 1); + runTestIteration(format, gl.UNSIGNED_BYTE, 4, 5, 2); + runTestIteration(format, gl.UNSIGNED_BYTE, 8, 5, 2); + runTestIteration(format, gl.UNSIGNED_BYTE, 8, 6, 2); + runTestIteration(format, gl.UNSIGNED_BYTE, 8, 7, 2); + runTestIteration(format, gl.UNSIGNED_BYTE, 8, 8, 2); + runTestIteration(format, gl.UNSIGNED_BYTE, 1, 0, 0); + runTestIteration(format, gl.UNSIGNED_BYTE, 2, 0, 0); + runTestIteration(format, gl.UNSIGNED_BYTE, 4, 0, 0); + runTestIteration(format, gl.UNSIGNED_BYTE, 8, 0, 0); + runTestIteration(format, gl.UNSIGNED_BYTE, 1, -1, 1); + runTestIteration(format, gl.UNSIGNED_BYTE, 2, 1, -1); + runTestIteration(format, gl.UNSIGNED_BYTE, 4, 0, -1); + runTestIteration(format, gl.UNSIGNED_BYTE, 8, -1, -1); +} + successfullyParsed = true; diff --git a/content/canvas/test/webgl/conformance/read-pixels-test.html b/content/canvas/test/webgl/conformance/read-pixels-test.html index 79bccc003712..35b64d4db028 100644 --- a/content/canvas/test/webgl/conformance/read-pixels-test.html +++ b/content/canvas/test/webgl/conformance/read-pixels-test.html @@ -11,6 +11,7 @@ found in the LICENSE file. + @@ -19,6 +20,7 @@ found in the LICENSE file. diff --git a/content/canvas/test/webgl/conformance/renderbuffer-initialization.html b/content/canvas/test/webgl/conformance/renderbuffer-initialization.html index 3f243c298043..45b9ed6ef565 100644 --- a/content/canvas/test/webgl/conformance/renderbuffer-initialization.html +++ b/content/canvas/test/webgl/conformance/renderbuffer-initialization.html @@ -12,7 +12,7 @@ found in the LICENSE file. function runTest() { var canvas = document.getElementById("testbed"); - var gl = canvas.getContext("experimental-webgl"); + var gl = create3DContext(canvas); if (!gl) { testFailed('canvas.getContext() failed'); return false; diff --git a/content/canvas/test/webgl/conformance/tex-image-and-sub-image-2d-with-array-buffer-view.html b/content/canvas/test/webgl/conformance/tex-image-and-sub-image-2d-with-array-buffer-view.html index 17adb0b27b30..5c3c5f021d43 100644 --- a/content/canvas/test/webgl/conformance/tex-image-and-sub-image-2d-with-array-buffer-view.html +++ b/content/canvas/test/webgl/conformance/tex-image-and-sub-image-2d-with-array-buffer-view.html @@ -9,46 +9,18 @@ found in the LICENSE file. + + + +
+
- - - - -
-
+ diff --git a/content/canvas/test/webgl/conformance/tex-image-with-invalid-data.html b/content/canvas/test/webgl/conformance/tex-image-with-invalid-data.html index 04552a822e01..ebe1c7c317e5 100644 --- a/content/canvas/test/webgl/conformance/tex-image-with-invalid-data.html +++ b/content/canvas/test/webgl/conformance/tex-image-with-invalid-data.html @@ -6,13 +6,12 @@ -
- + diff --git a/content/canvas/test/webgl/conformance/texture-transparent-pixels-initialized.html b/content/canvas/test/webgl/conformance/texture-transparent-pixels-initialized.html index ef659468fbc7..c4d3962eb118 100644 --- a/content/canvas/test/webgl/conformance/texture-transparent-pixels-initialized.html +++ b/content/canvas/test/webgl/conformance/texture-transparent-pixels-initialized.html @@ -51,7 +51,7 @@ function init() var canvas = document.getElementById("example"); gl = wtu.create3DContext(canvas); var program = wtu.setupTexturedQuad(gl); - gl.clearColor(0,0,0,1); + gl.clearColor(0.5,0.5,0.5,1); gl.clearDepth(1); textureLoc = gl.getUniformLocation(program, "tex"); @@ -70,7 +70,7 @@ function init() gl.bindBuffer(gl.ARRAY_BUFFER, vbo); gl.bufferData(gl.ARRAY_BUFFER, texCoords, gl.STATIC_DRAW); gl.enableVertexAttribArray(1); - gl.vertexAttribPointer(1, 2, gl.FLOAT, gl.FALSE, 0, 0); + gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 0, 0); texture = wtu.loadTexture(gl, "resources/bug-32888-texture.png", runTest); } @@ -82,16 +82,18 @@ var idx = 0; function runTest() { gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + gl.enable(gl.BLEND); + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); // Bind the texture to texture unit 0 gl.bindTexture(gl.TEXTURE_2D, texture); // Point the uniform sampler to texture unit 0 gl.uniform1i(textureLoc, 0); // Draw the triangles - wtu.drawQuad(gl, [100, 100, 100, 255]); + wtu.drawQuad(gl, [0, 0, 0, 255]); // Spot check a couple of 2x2 regions in the upper and lower left // corners; they should be the rgb values in the texture. - color = [255, 255, 255] + color = [0, 0, 0]; debug("Checking lower left corner"); wtu.checkCanvasRect(gl, 1, gl.canvas.height - 3, 2, 2, color, "shouldBe " + color); diff --git a/content/canvas/test/webgl/conformance/type-conversion-test.html b/content/canvas/test/webgl/conformance/type-conversion-test.html new file mode 100644 index 000000000000..f692c4180b7c --- /dev/null +++ b/content/canvas/test/webgl/conformance/type-conversion-test.html @@ -0,0 +1,168 @@ + + + + + + + + + +
+
+ + + + + + + diff --git a/content/canvas/test/webgl/conformance/uninitialized-test.html b/content/canvas/test/webgl/conformance/uninitialized-test.html index 6ba85252d341..a6443a7d0524 100644 --- a/content/canvas/test/webgl/conformance/uninitialized-test.html +++ b/content/canvas/test/webgl/conformance/uninitialized-test.html @@ -6,13 +6,12 @@ -
- + diff --git a/content/canvas/test/webgl/conformance/webgl-specific.html b/content/canvas/test/webgl/conformance/webgl-specific.html new file mode 100644 index 000000000000..29bc2c8c7f7a --- /dev/null +++ b/content/canvas/test/webgl/conformance/webgl-specific.html @@ -0,0 +1,120 @@ + + + + +WebGL GLES2 difference test. + + + + + + +
+
+ + + + + + diff --git a/content/events/src/nsDOMEvent.cpp b/content/events/src/nsDOMEvent.cpp index 929b15309aa5..50fcd7655404 100644 --- a/content/events/src/nsDOMEvent.cpp +++ b/content/events/src/nsDOMEvent.cpp @@ -93,6 +93,7 @@ static const char* const sEventNames[] = { #endif // MOZ_MEDIA "MozAfterPaint", "MozBeforePaint", + "MozBeforeResize", "MozSwipeGesture", "MozMagnifyGestureStart", "MozMagnifyGestureUpdate", @@ -1316,6 +1317,8 @@ const char* nsDOMEvent::GetEventName(PRUint32 aEventType) return sEventNames[eDOMEvents_afterpaint]; case NS_BEFOREPAINT: return sEventNames[eDOMEvents_beforepaint]; + case NS_BEFORERESIZE_EVENT: + return sEventNames[eDOMEvents_beforeresize]; case NS_SIMPLE_GESTURE_SWIPE: return sEventNames[eDOMEvents_MozSwipeGesture]; case NS_SIMPLE_GESTURE_MAGNIFY_START: diff --git a/content/events/src/nsDOMEvent.h b/content/events/src/nsDOMEvent.h index 70f1759a2eae..39621e8c86a6 100644 --- a/content/events/src/nsDOMEvent.h +++ b/content/events/src/nsDOMEvent.h @@ -175,6 +175,7 @@ public: #endif eDOMEvents_afterpaint, eDOMEvents_beforepaint, + eDOMEvents_beforeresize, eDOMEvents_MozSwipeGesture, eDOMEvents_MozMagnifyGestureStart, eDOMEvents_MozMagnifyGestureUpdate, diff --git a/content/events/test/Makefile.in b/content/events/test/Makefile.in index 382397ecb49f..65b8652a0388 100644 --- a/content/events/test/Makefile.in +++ b/content/events/test/Makefile.in @@ -110,6 +110,8 @@ _CHROME_FILES = \ test_bug415498.xul \ bug415498-doc1.html \ bug415498-doc2.html \ + bug602962.xul \ + test_bug602962.xul \ $(NULL) libs:: $(_TEST_FILES) diff --git a/content/events/test/bug602962.xul b/content/events/test/bug602962.xul new file mode 100644 index 000000000000..0d54b7ad5260 --- /dev/null +++ b/content/events/test/bug602962.xul @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/content/events/test/test_bug602962.xul b/content/events/test/test_bug602962.xul new file mode 100644 index 000000000000..b74c6ad0f957 --- /dev/null +++ b/content/events/test/test_bug602962.xul @@ -0,0 +1,94 @@ + + + + + + + Test for Bug 602962 + + + diff --git a/content/html/content/public/nsHTMLCanvasElement.h b/content/html/content/public/nsHTMLCanvasElement.h index bce11f45e8fa..1e7e503cd8e4 100644 --- a/content/html/content/public/nsHTMLCanvasElement.h +++ b/content/html/content/public/nsHTMLCanvasElement.h @@ -169,7 +169,7 @@ public: protected: nsIntSize GetWidthHeight(); - nsresult UpdateContext(); + nsresult UpdateContext(nsIPropertyBag *aNewContextOptions = nsnull); nsresult ExtractData(const nsAString& aType, const nsAString& aOptions, char*& aData, diff --git a/content/html/content/public/nsHTMLMediaElement.h b/content/html/content/public/nsHTMLMediaElement.h index c7a4526f5029..0331d95eff43 100644 --- a/content/html/content/public/nsHTMLMediaElement.h +++ b/content/html/content/public/nsHTMLMediaElement.h @@ -598,7 +598,7 @@ protected: nsCOMPtr mSourceLoadCandidate; // An audio stream for writing audio directly from JS. - nsAutoPtr mAudioStream; + nsRefPtr mAudioStream; // PR_TRUE if MozAudioAvailable events can be safely dispatched, based on // a media and element same-origin check. diff --git a/content/html/content/public/nsISelectElement.idl b/content/html/content/public/nsISelectElement.idl index dbabe9299ef0..b6282670cef6 100644 --- a/content/html/content/public/nsISelectElement.idl +++ b/content/html/content/public/nsISelectElement.idl @@ -48,7 +48,7 @@ interface nsIDOMHTMLOptionElement; * OPTIONs within a SELECT element. */ -[scriptable, uuid(35bd8ed5-5f34-4126-8c4f-38ba01681836)] +[scriptable, uuid(aa73a61a-8ef2-402d-b86c-3a5c5f2a6027)] interface nsISelectElement : nsISupports { @@ -64,7 +64,8 @@ interface nsISelectElement : nsISupports */ [noscript] void willAddOptions(in nsIContent aOptions, in nsIContent aParent, - in long aContentIndex); + in long aContentIndex, + in boolean aNotify); /** * To be called when stuff is removed under a child of the select--but @@ -75,7 +76,8 @@ interface nsISelectElement : nsISupports * parent is an optgroup, the index within the optgroup) */ [noscript] void willRemoveOptions(in nsIContent aParent, - in long aContentIndex); + in long aContentIndex, + in boolean aNotify); /** * Checks whether an option is disabled (even if it's part of an optgroup) diff --git a/content/html/content/src/nsHTMLAudioElement.cpp b/content/html/content/src/nsHTMLAudioElement.cpp index c20246729fe2..250354ec1e81 100644 --- a/content/html/content/src/nsHTMLAudioElement.cpp +++ b/content/html/content/src/nsHTMLAudioElement.cpp @@ -168,7 +168,7 @@ nsHTMLAudioElement::MozSetup(PRUint32 aChannels, PRUint32 aRate) mAudioStream->Shutdown(); } - mAudioStream = new nsAudioStream(); + mAudioStream = nsAudioStream::AllocateStream(); nsresult rv = mAudioStream->Init(aChannels, aRate, nsAudioStream::FORMAT_FLOAT32); if (NS_FAILED(rv)) { diff --git a/content/html/content/src/nsHTMLCanvasElement.cpp b/content/html/content/src/nsHTMLCanvasElement.cpp index 420d6d58a9cf..2df9f42f3118 100644 --- a/content/html/content/src/nsHTMLCanvasElement.cpp +++ b/content/html/content/src/nsHTMLCanvasElement.cpp @@ -51,6 +51,9 @@ #include "nsDisplayList.h" #include "ImageLayers.h" #include "BasicLayers.h" +#include "imgIEncoder.h" + +#include "nsIWritablePropertyBag2.h" #define DEFAULT_CANVAS_WIDTH 300 #define DEFAULT_CANVAS_HEIGHT 150 @@ -151,7 +154,7 @@ nsHTMLCanvasElement::CopyInnerTo(nsGenericElement* aDest) const if (aDest->GetOwnerDoc()->IsStaticDocument()) { nsHTMLCanvasElement* dest = static_cast(aDest); nsCOMPtr cxt; - dest->GetContext(NS_LITERAL_STRING("2d"), getter_AddRefs(cxt)); + dest->GetContext(NS_LITERAL_STRING("2d"), JSVAL_VOID, getter_AddRefs(cxt)); nsCOMPtr context2d = do_QueryInterface(cxt); if (context2d) { context2d->DrawImage(const_cast(this), @@ -230,33 +233,60 @@ nsHTMLCanvasElement::ExtractData(const nsAString& aType, PRUint32& aSize, bool& aFellBackToPNG) { - // We get an input stream from the context. If more than one context type - // is supported in the future, this will have to be changed to do the right - // thing. For now, just assume that the 2D context has all the goods. - nsCOMPtr context; - nsresult rv = GetContext(NS_LITERAL_STRING("2d"), getter_AddRefs(context)); - NS_ENSURE_SUCCESS(rv, rv); - if (!context) { - // XXX bug 578349 - return NS_ERROR_NOT_IMPLEMENTED; + // note that if we don't have a current context, the spec says we're + // supposed to just return transparent black pixels of the canvas + // dimensions. + nsRefPtr emptyCanvas; + nsIntSize size = GetWidthHeight(); + if (!mCurrentContext) { + emptyCanvas = new gfxImageSurface(gfxIntSize(size.width, size.height), gfxASurface::ImageFormatARGB32); } + nsresult rv; + // get image bytes nsCOMPtr imgStream; - NS_ConvertUTF16toUTF8 aMimeType8(aType); - rv = context->GetInputStream(nsPromiseFlatCString(aMimeType8).get(), - nsPromiseFlatString(aOptions).get(), - getter_AddRefs(imgStream)); - if (NS_FAILED(rv)) { - // Use image/png instead. + nsCAutoString encoderType; + encoderType.Assign(NS_ConvertUTF16toUTF8(aType)); + + try_again: + if (mCurrentContext) { + rv = mCurrentContext->GetInputStream(nsPromiseFlatCString(encoderType).get(), + nsPromiseFlatString(aOptions).get(), + getter_AddRefs(imgStream)); + } else { + // no context, so we have to encode the empty image we created above + nsCString enccid("@mozilla.org/image/encoder;2?type="); + enccid += encoderType; + + nsCOMPtr encoder = do_CreateInstance(nsPromiseFlatCString(enccid).get(), &rv); + if (NS_SUCCEEDED(rv) && encoder) { + rv = encoder->InitFromData(emptyCanvas->Data(), + size.width * size.height * 4, + size.width, + size.height, + size.width * 4, + imgIEncoder::INPUT_FORMAT_HOSTARGB, + aOptions); + if (NS_SUCCEEDED(rv)) { + imgStream = do_QueryInterface(encoder); + } + } else { + rv = NS_ERROR_FAILURE; + } + } + + if (NS_FAILED(rv) && !aFellBackToPNG) { + // Try image/png instead. // XXX ERRMSG we need to report an error to developers here! (bug 329026) aFellBackToPNG = true; - rv = context->GetInputStream("image/png", - nsPromiseFlatString(aOptions).get(), - getter_AddRefs(imgStream)); - NS_ENSURE_SUCCESS(rv, rv); + encoderType.AssignLiteral("image/png"); + goto try_again; } + // at this point, we either need to succeed or bail. + NS_ENSURE_SUCCESS(rv, rv); + // Generally, there will be only one chunk of data, and it will be available // for us to read right away, so optimize this case. PRUint32 bufSize; @@ -301,6 +331,7 @@ nsHTMLCanvasElement::ToDataURLImpl(const nsAString& aMimeType, nsAString& aDataURL) { bool fallbackToPNG = false; + PRUint32 imgSize = 0; char* imgData; @@ -417,6 +448,7 @@ nsHTMLCanvasElement::GetContextHelper(const nsAString& aContextId, NS_IMETHODIMP nsHTMLCanvasElement::GetContext(const nsAString& aContextId, + const jsval& aContextOptions, nsISupports **aContext) { nsresult rv; @@ -443,7 +475,52 @@ nsHTMLCanvasElement::GetContext(const nsAString& aContextId, return rv; } - rv = UpdateContext(); + nsCOMPtr contextProps; + if (!JSVAL_IS_NULL(aContextOptions) && + !JSVAL_IS_VOID(aContextOptions)) + { + JSContext *cx = nsContentUtils::GetCurrentJSContext(); + + nsCOMPtr newProps; + + // note: if any contexts end up supporting something other + // than objects, e.g. plain strings, then we'll need to expand + // this to know how to create nsISupportsStrings etc. + if (JSVAL_IS_OBJECT(aContextOptions)) { + newProps = do_CreateInstance("@mozilla.org/hash-property-bag;1"); + + JSObject *opts = JSVAL_TO_OBJECT(aContextOptions); + JSIdArray *props = JS_Enumerate(cx, opts); + for (int i = 0; props && i < props->length; ++i) { + jsid propid = props->vector[i]; + jsval propname, propval; + if (!JS_IdToValue(cx, propid, &propname) || + !JS_GetPropertyById(cx, opts, propid, &propval)) + { + continue; + } + + JSString *propnameString = JS_ValueToString(cx, propname); + + nsDependentString pstr(JS_GetStringChars(propnameString), JS_GetStringLength(propnameString)); + + if (JSVAL_IS_BOOLEAN(propval)) { + newProps->SetPropertyAsBool(pstr, propval == JSVAL_TRUE ? PR_TRUE : PR_FALSE); + } else if (JSVAL_IS_INT(propval)) { + newProps->SetPropertyAsInt32(pstr, JSVAL_TO_INT(propval)); + } else if (JSVAL_IS_DOUBLE(propval)) { + newProps->SetPropertyAsDouble(pstr, JSVAL_TO_DOUBLE(propval)); + } else if (JSVAL_IS_STRING(propval)) { + newProps->SetPropertyAsAString(pstr, nsDependentString(JS_GetStringChars(JS_ValueToString(cx, propval)), + JS_GetStringLength(JS_ValueToString(cx, propval)))); + } + } + } + + contextProps = newProps; + } + + rv = UpdateContext(contextProps); if (NS_FAILED(rv)) { mCurrentContext = nsnull; return rv; @@ -504,14 +581,25 @@ nsHTMLCanvasElement::MozGetIPCContext(const nsAString& aContextId, } nsresult -nsHTMLCanvasElement::UpdateContext() +nsHTMLCanvasElement::UpdateContext(nsIPropertyBag *aNewContextOptions) { + if (!mCurrentContext) + return NS_OK; + nsresult rv = NS_OK; - if (mCurrentContext) { - nsIntSize sz = GetWidthHeight(); - rv = mCurrentContext->SetIsOpaque(GetIsOpaque()); - rv = mCurrentContext->SetDimensions(sz.width, sz.height); - } + + rv = mCurrentContext->SetIsOpaque(GetIsOpaque()); + if (NS_FAILED(rv)) + return rv; + + rv = mCurrentContext->SetContextOptions(aNewContextOptions); + if (NS_FAILED(rv)) + return rv; + + nsIntSize sz = GetWidthHeight(); + rv = mCurrentContext->SetDimensions(sz.width, sz.height); + if (NS_FAILED(rv)) + return rv; return rv; } diff --git a/content/html/content/src/nsHTMLOptGroupElement.cpp b/content/html/content/src/nsHTMLOptGroupElement.cpp index 9e7d68bf31d7..0d815f7d0a50 100644 --- a/content/html/content/src/nsHTMLOptGroupElement.cpp +++ b/content/html/content/src/nsHTMLOptGroupElement.cpp @@ -175,7 +175,7 @@ nsHTMLOptGroupElement::InsertChildAt(nsIContent* aKid, PRUint32 aIndex, PRBool aNotify) { - nsSafeOptionListMutation safeMutation(GetSelect(), this, aKid, aIndex); + nsSafeOptionListMutation safeMutation(GetSelect(), this, aKid, aIndex, aNotify); nsresult rv = nsGenericHTMLElement::InsertChildAt(aKid, aIndex, aNotify); if (NS_FAILED(rv)) { safeMutation.MutationFailed(); @@ -186,7 +186,7 @@ nsHTMLOptGroupElement::InsertChildAt(nsIContent* aKid, nsresult nsHTMLOptGroupElement::RemoveChildAt(PRUint32 aIndex, PRBool aNotify, PRBool aMutationEvent) { - nsSafeOptionListMutation safeMutation(GetSelect(), this, nsnull, aIndex); + nsSafeOptionListMutation safeMutation(GetSelect(), this, nsnull, aIndex, aNotify); nsresult rv = nsGenericHTMLElement::RemoveChildAt(aIndex, aNotify, aMutationEvent); if (NS_FAILED(rv)) { safeMutation.MutationFailed(); diff --git a/content/html/content/src/nsHTMLOutputElement.cpp b/content/html/content/src/nsHTMLOutputElement.cpp index 39442d1d8328..5918a7b5e4f4 100644 --- a/content/html/content/src/nsHTMLOutputElement.cpp +++ b/content/html/content/src/nsHTMLOutputElement.cpp @@ -41,6 +41,8 @@ #include "nsDOMSettableTokenList.h" #include "nsStubMutationObserver.h" #include "nsIConstraintValidation.h" +#include "nsIEventStateManager.h" +#include "mozAutoDocUpdate.h" class nsHTMLOutputElement : public nsGenericHTMLFormElement, @@ -81,6 +83,8 @@ public: PRBool ParseAttribute(PRInt32 aNamespaceID, nsIAtom* aAttribute, const nsAString& aValue, nsAttrValue& aResult); + nsEventStates IntrinsicState() const; + // This function is called when a callback function from nsIMutationObserver // has to be used to update the defaultValue attribute. void DescendantsChanged(); @@ -115,8 +119,6 @@ nsHTMLOutputElement::nsHTMLOutputElement(already_AddRefed aNodeInfo , mValueModeFlag(eModeDefault) { AddMutationObserver(this); - // is always barred from constraint validation. - SetBarredFromConstraintValidation(PR_TRUE); } nsHTMLOutputElement::~nsHTMLOutputElement() @@ -147,7 +149,22 @@ NS_IMPL_ELEMENT_CLONE(nsHTMLOutputElement) NS_IMPL_STRING_ATTR(nsHTMLOutputElement, Name, name) // nsIConstraintValidation -NS_IMPL_NSICONSTRAINTVALIDATION(nsHTMLOutputElement) +NS_IMPL_NSICONSTRAINTVALIDATION_EXCEPT_SETCUSTOMVALIDITY(nsHTMLOutputElement) + +NS_IMETHODIMP +nsHTMLOutputElement::SetCustomValidity(const nsAString& aError) +{ + nsIConstraintValidation::SetCustomValidity(aError); + + nsIDocument* doc = GetCurrentDoc(); + if (doc) { + MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE); + doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_INVALID | + NS_EVENT_STATE_VALID); + } + + return NS_OK; +} NS_IMETHODIMP nsHTMLOutputElement::Reset() @@ -180,6 +197,18 @@ nsHTMLOutputElement::ParseAttribute(PRInt32 aNamespaceID, nsIAtom* aAttribute, aValue, aResult); } +nsEventStates +nsHTMLOutputElement::IntrinsicState() const +{ + nsEventStates states = nsGenericHTMLFormElement::IntrinsicState(); + + // We don't have to call IsCandidateForConstraintValidation() + // because can't be barred from constraint validation. + states |= IsValid() ? NS_EVENT_STATE_VALID : NS_EVENT_STATE_INVALID; + + return states; +} + NS_IMETHODIMP nsHTMLOutputElement::GetForm(nsIDOMHTMLFormElement** aForm) { diff --git a/content/html/content/src/nsHTMLScriptElement.cpp b/content/html/content/src/nsHTMLScriptElement.cpp index 469c3d5c790d..295582c51ff1 100644 --- a/content/html/content/src/nsHTMLScriptElement.cpp +++ b/content/html/content/src/nsHTMLScriptElement.cpp @@ -556,6 +556,8 @@ nsHTMLScriptElement::FreezeUriAsyncDefer() nsAutoString src; GetSrc(src); NS_NewURI(getter_AddRefs(mUri), src); + // At this point mUri will be null for invalid URLs. + mExternal = PR_TRUE; PRBool defer, async; GetAsync(&async); @@ -571,7 +573,7 @@ nsHTMLScriptElement::FreezeUriAsyncDefer() PRBool nsHTMLScriptElement::HasScriptContent() { - return (mFrozen ? !!mUri : HasAttr(kNameSpaceID_None, nsGkAtoms::src)) || + return (mFrozen ? mExternal : HasAttr(kNameSpaceID_None, nsGkAtoms::src)) || nsContentUtils::HasNonEmptyTextContent(this); } diff --git a/content/html/content/src/nsHTMLSelectElement.cpp b/content/html/content/src/nsHTMLSelectElement.cpp index 5009842dcba7..1dd6ac025adc 100644 --- a/content/html/content/src/nsHTMLSelectElement.cpp +++ b/content/html/content/src/nsHTMLSelectElement.cpp @@ -86,7 +86,8 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsSelectState, NS_SELECT_STATE_IID) nsSafeOptionListMutation::nsSafeOptionListMutation(nsIContent* aSelect, nsIContent* aParent, nsIContent* aKid, - PRUint32 aIndex) + PRUint32 aIndex, + PRBool aNotify) : mSelect(do_QueryInterface(aSelect)), mTopLevelMutation(PR_FALSE), mNeedsRebuild(PR_FALSE) { @@ -104,9 +105,9 @@ nsSafeOptionListMutation::nsSafeOptionListMutation(nsIContent* aSelect, } nsresult rv; if (aKid) { - rv = mSelect->WillAddOptions(aKid, aParent, aIndex); + rv = mSelect->WillAddOptions(aKid, aParent, aIndex, aNotify); } else { - rv = mSelect->WillRemoveOptions(aParent, aIndex); + rv = mSelect->WillRemoveOptions(aParent, aIndex, aNotify); } mNeedsRebuild = NS_FAILED(rv); } @@ -182,8 +183,9 @@ DOMCI_NODE_DATA(HTMLSelectElement, nsHTMLSelectElement) // QueryInterface implementation for nsHTMLSelectElement NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsHTMLSelectElement) - NS_HTML_CONTENT_INTERFACE_TABLE3(nsHTMLSelectElement, + NS_HTML_CONTENT_INTERFACE_TABLE4(nsHTMLSelectElement, nsIDOMHTMLSelectElement, + nsIDOMHTMLSelectElement_Mozilla_2_0_Branch, nsISelectElement, nsIConstraintValidation) NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE(nsHTMLSelectElement, @@ -225,7 +227,7 @@ nsHTMLSelectElement::InsertChildAt(nsIContent* aKid, PRUint32 aIndex, PRBool aNotify) { - nsSafeOptionListMutation safeMutation(this, this, aKid, aIndex); + nsSafeOptionListMutation safeMutation(this, this, aKid, aIndex, aNotify); nsresult rv = nsGenericHTMLFormElement::InsertChildAt(aKid, aIndex, aNotify); if (NS_FAILED(rv)) { safeMutation.MutationFailed(); @@ -237,7 +239,7 @@ nsresult nsHTMLSelectElement::RemoveChildAt(PRUint32 aIndex, PRBool aNotify, PRBool aMutationEvent) { NS_ASSERTION(aMutationEvent, "Someone tried to inhibit mutations on select child removal."); - nsSafeOptionListMutation safeMutation(this, this, nsnull, aIndex); + nsSafeOptionListMutation safeMutation(this, this, nsnull, aIndex, aNotify); nsresult rv = nsGenericHTMLFormElement::RemoveChildAt(aIndex, aNotify, aMutationEvent); if (NS_FAILED(rv)) { safeMutation.MutationFailed(); @@ -251,7 +253,8 @@ nsHTMLSelectElement::RemoveChildAt(PRUint32 aIndex, PRBool aNotify, PRBool aMuta nsresult nsHTMLSelectElement::InsertOptionsIntoList(nsIContent* aOptions, PRInt32 aListIndex, - PRInt32 aDepth) + PRInt32 aDepth, + PRBool aNotify) { PRInt32 insertIndex = aListIndex; nsresult rv = InsertOptionsIntoListRecurse(aOptions, &insertIndex, aDepth); @@ -305,7 +308,7 @@ nsHTMLSelectElement::InsertOptionsIntoList(nsIContent* aOptions, } } - CheckSelectSomething(); + CheckSelectSomething(aNotify); } return NS_OK; @@ -314,7 +317,8 @@ nsHTMLSelectElement::InsertOptionsIntoList(nsIContent* aOptions, nsresult nsHTMLSelectElement::RemoveOptionsFromList(nsIContent* aOptions, PRInt32 aListIndex, - PRInt32 aDepth) + PRInt32 aDepth, + PRBool aNotify) { PRInt32 numRemoved = 0; nsresult rv = RemoveOptionsFromListRecurse(aOptions, aListIndex, &numRemoved, @@ -346,7 +350,20 @@ nsHTMLSelectElement::RemoveOptionsFromList(nsIContent* aOptions, // Select something in case we removed the selected option on a // single select - CheckSelectSomething(); + if (!CheckSelectSomething(aNotify) && mSelectedIndex == -1) { + // Update the validity state in case of we've just removed the last + // option. + UpdateValueMissingValidityState(); + + if (aNotify) { + nsIDocument* doc = GetCurrentDoc(); + if (doc) { + MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE); + doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_VALID | + NS_EVENT_STATE_INVALID); + } + } + } } return NS_OK; @@ -454,7 +471,8 @@ nsHTMLSelectElement::RemoveOptionsFromListRecurse(nsIContent* aOptions, NS_IMETHODIMP nsHTMLSelectElement::WillAddOptions(nsIContent* aOptions, nsIContent* aParent, - PRInt32 aContentIndex) + PRInt32 aContentIndex, + PRBool aNotify) { PRInt32 level = GetContentDepth(aParent); if (level == -1) { @@ -489,12 +507,13 @@ nsHTMLSelectElement::WillAddOptions(nsIContent* aOptions, } } - return InsertOptionsIntoList(aOptions, ind, level); + return InsertOptionsIntoList(aOptions, ind, level, aNotify); } NS_IMETHODIMP nsHTMLSelectElement::WillRemoveOptions(nsIContent* aParent, - PRInt32 aContentIndex) + PRInt32 aContentIndex, + PRBool aNotify) { PRInt32 level = GetContentDepth(aParent); NS_ASSERTION(level >= 0, "getting notified by unexpected content"); @@ -515,7 +534,7 @@ nsHTMLSelectElement::WillRemoveOptions(nsIContent* aParent, ind = GetFirstOptionIndex(currentKid); } if (ind != -1) { - nsresult rv = RemoveOptionsFromList(currentKid, ind, level); + nsresult rv = RemoveOptionsFromList(currentKid, ind, level, aNotify); NS_ENSURE_SUCCESS(rv, rv); } } @@ -856,6 +875,16 @@ nsHTMLSelectElement::OnOptionSelected(nsISelectControlFrame* aSelectFrame, if (aSelectFrame) { aSelectFrame->OnOptionSelected(aIndex, aSelected); } + + UpdateValueMissingValidityState(); + if (aNotify) { + nsIDocument* doc = GetCurrentDoc(); + if (doc) { + MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE); + doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_VALID | + NS_EVENT_STATE_INVALID); + } + } } void @@ -1082,7 +1111,7 @@ nsHTMLSelectElement::SetOptionsSelectedByIndex(PRInt32 aStartIndex, // Make sure something is selected unless we were set to -1 (none) if (optionsDeselected && aStartIndex != -1) { - optionsSelected = CheckSelectSomething() || optionsSelected; + optionsSelected = CheckSelectSomething(aNotify) || optionsSelected; } // Let the caller know whether anything was changed @@ -1217,6 +1246,7 @@ NS_IMPL_BOOL_ATTR(nsHTMLSelectElement, Autofocus, autofocus) NS_IMPL_BOOL_ATTR(nsHTMLSelectElement, Disabled, disabled) NS_IMPL_BOOL_ATTR(nsHTMLSelectElement, Multiple, multiple) NS_IMPL_STRING_ATTR(nsHTMLSelectElement, Name, name) +NS_IMPL_BOOL_ATTR(nsHTMLSelectElement, Required, required) NS_IMPL_POSITIVE_INT_ATTR_DEFAULT_VALUE(nsHTMLSelectElement, Size, size, 0) NS_IMPL_INT_ATTR(nsHTMLSelectElement, TabIndex, tabindex) @@ -1259,18 +1289,18 @@ nsHTMLSelectElement::NamedItem(const nsAString& aName, } PRBool -nsHTMLSelectElement::CheckSelectSomething() +nsHTMLSelectElement::CheckSelectSomething(PRBool aNotify) { if (mIsDoneAddingChildren) { if (mSelectedIndex < 0 && IsCombobox()) { - return SelectSomething(); + return SelectSomething(aNotify); } } return PR_FALSE; } PRBool -nsHTMLSelectElement::SelectSomething() +nsHTMLSelectElement::SelectSomething(PRBool aNotify) { // If we're not done building the select, don't play with this yet. if (!mIsDoneAddingChildren) { @@ -1286,6 +1316,17 @@ nsHTMLSelectElement::SelectSomething() if (NS_FAILED(rv) || !disabled) { rv = SetSelectedIndex(i); NS_ENSURE_SUCCESS(rv, PR_FALSE); + + UpdateValueMissingValidityState(); + if (aNotify) { + nsIDocument* doc = GetCurrentDoc(); + if (doc) { + MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE); + doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_VALID | + NS_EVENT_STATE_INVALID); + } + } + return PR_TRUE; } } @@ -1327,15 +1368,23 @@ nsresult nsHTMLSelectElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, const nsAString* aValue, PRBool aNotify) { - if (aName == nsGkAtoms::disabled && aNameSpaceID == kNameSpaceID_None) { - UpdateBarredFromConstraintValidation(); - if (aNotify) { - nsIDocument* doc = GetCurrentDoc(); - if (doc) { - MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE); - doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_VALID | - NS_EVENT_STATE_INVALID); - } + nsEventStates states; + + if (aNameSpaceID == kNameSpaceID_None) { + if (aName == nsGkAtoms::disabled) { + UpdateBarredFromConstraintValidation(); + states |= NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID; + } else if (aName == nsGkAtoms::required) { + UpdateValueMissingValidityState(); + states |= NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID; + } + } + + if (aNotify && !states.IsEmpty()) { + nsIDocument* doc = GetCurrentDoc(); + if (doc) { + MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE); + doc->ContentStatesChanged(this, nsnull, states); } } @@ -1368,7 +1417,7 @@ nsHTMLSelectElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute, aAttribute == nsGkAtoms::multiple) { // We might have become a combobox; make sure _something_ gets // selected in that case - CheckSelectSomething(); + CheckSelectSomething(aNotify); } return rv; @@ -1406,7 +1455,12 @@ nsHTMLSelectElement::DoneAddingChildren(PRBool aHaveNotified) // Now that we're done, select something (if it's a single select something // must be selected) - CheckSelectSomething(); + if (!CheckSelectSomething(PR_FALSE)) { + // If an option has @selected set, it will be selected during parsing but + // with an empty value. We have to make sure the select element updates it's + // validity state to take this into account. + UpdateValueMissingValidityState(); + } return NS_OK; } @@ -1498,7 +1552,13 @@ nsHTMLSelectElement::IntrinsicState() const state |= IsValid() ? NS_EVENT_STATE_VALID : NS_EVENT_STATE_INVALID; } - return state | NS_EVENT_STATE_OPTIONAL; + if (HasAttr(kNameSpaceID_None, nsGkAtoms::required)) { + state |= NS_EVENT_STATE_REQUIRED; + } else { + state |= NS_EVENT_STATE_OPTIONAL; + } + + return state; } // nsIFormControl @@ -1631,7 +1691,7 @@ nsHTMLSelectElement::Reset() // If nothing was selected and it's not multiple, select something // if (numSelected == 0 && IsCombobox()) { - SelectSomething(); + SelectSomething(PR_TRUE); } // @@ -1768,6 +1828,75 @@ nsHTMLSelectElement::RebuildOptionsArray() FindSelectedIndex(0); } +bool +nsHTMLSelectElement::IsValueMissing() +{ + if (!HasAttr(kNameSpaceID_None, nsGkAtoms::required)) { + return false; + } + + PRUint32 length; + nsIDOMHTMLOptionElement* option = nsnull; + PRBool disabled; + PRBool selected; + + mOptions->GetLength(&length); + + for (PRUint32 i=0; iItemAsOption(i); + NS_ENSURE_SUCCESS(option->GetSelected(&selected), false); + + if (!selected) { + continue; + } + + IsOptionDisabled(i, &disabled); + if (disabled) { + continue; + } + + nsAutoString value; + NS_ENSURE_SUCCESS(option->GetValue(value), false); + if (!value.IsEmpty()) { + return false; + } + } + + return true; +} + +void +nsHTMLSelectElement::UpdateValueMissingValidityState() +{ + SetValidityState(VALIDITY_STATE_VALUE_MISSING, IsValueMissing()); +} + +nsresult +nsHTMLSelectElement::GetValidationMessage(nsAString& aValidationMessage, + ValidityStateType aType) +{ + nsresult rv = NS_OK; + + switch (aType) + { + case VALIDITY_STATE_VALUE_MISSING: + { + nsXPIDLString message; + + rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES, + "FormValidationSelectMissing", + message); + + aValidationMessage = message; + } + break; + default: + rv = nsIConstraintValidation::GetValidationMessage(aValidationMessage, aType); + } + + return rv; +} + #ifdef DEBUG static void diff --git a/content/html/content/src/nsHTMLSelectElement.h b/content/html/content/src/nsHTMLSelectElement.h index 79f6905288dd..628653b367e0 100644 --- a/content/html/content/src/nsHTMLSelectElement.h +++ b/content/html/content/src/nsHTMLSelectElement.h @@ -215,7 +215,7 @@ public: * @param aIndex The index of the content object in the parent. */ nsSafeOptionListMutation(nsIContent* aSelect, nsIContent* aParent, - nsIContent* aKid, PRUint32 aIndex); + nsIContent* aKid, PRUint32 aIndex, PRBool aNotify); ~nsSafeOptionListMutation(); void MutationFailed() { mNeedsRebuild = PR_TRUE; } private: @@ -236,7 +236,7 @@ private: * Implementation of <select> */ class nsHTMLSelectElement : public nsGenericHTMLFormElement, - public nsIDOMHTMLSelectElement, + public nsIDOMHTMLSelectElement_Mozilla_2_0_Branch, public nsISelectElement, public nsIConstraintValidation { @@ -262,6 +262,9 @@ public: // nsIDOMHTMLSelectElement NS_DECL_NSIDOMHTMLSELECTELEMENT + // nsIDOMHTMLSelectElement_Mozilla_2_0_Branch + NS_DECL_NSIDOMHTMLSELECTELEMENT_MOZILLA_2_0_BRANCH + // nsIContent virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor); @@ -327,7 +330,8 @@ public: virtual nsXPCClassInfo* GetClassInfo(); // nsIConstraintValidation - void UpdateBarredFromConstraintValidation(); + nsresult GetValidationMessage(nsAString& aValidationMessage, + ValidityStateType aType); protected: friend class nsSafeOptionListMutation; @@ -349,13 +353,13 @@ protected: * Select some option if possible (generally the first non-disabled option). * @return true if something was selected, false otherwise */ - PRBool SelectSomething(); + PRBool SelectSomething(PRBool aNotify); /** * Call SelectSomething(), but only if nothing is selected * @see SelectSomething() * @return true if something was selected, false otherwise */ - PRBool CheckSelectSomething(); + PRBool CheckSelectSomething(PRBool aNotify); /** * Called to trigger notifications of frames and fixing selected index * @@ -387,7 +391,8 @@ protected: */ nsresult InsertOptionsIntoList(nsIContent* aOptions, PRInt32 aListIndex, - PRInt32 aDepth); + PRInt32 aDepth, + PRBool aNotify); /** * Remove option(s) from the options[] array * @param aOptions the option or optgroup being added @@ -396,7 +401,8 @@ protected: */ nsresult RemoveOptionsFromList(nsIContent* aOptions, PRInt32 aListIndex, - PRInt32 aDepth); + PRInt32 aDepth, + PRBool aNotify); /** * Insert option(s) into the options[] array (called by InsertOptionsIntoList) * @param aOptions the option or optgroup being added @@ -417,6 +423,12 @@ protected: PRInt32 aRemoveIndex, PRInt32* aNumRemoved, PRInt32 aDepth); + + // nsIConstraintValidation + void UpdateBarredFromConstraintValidation(); + bool IsValueMissing(); + void UpdateValueMissingValidityState(); + /** * Find out how deep this content is from the select (1=direct child) * @param aContent the content to check diff --git a/content/html/content/test/Makefile.in b/content/html/content/test/Makefile.in index 1b0100d6dbb1..5932661d060c 100644 --- a/content/html/content/test/Makefile.in +++ b/content/html/content/test/Makefile.in @@ -240,6 +240,7 @@ _TEST_FILES = \ file_bug297761.html \ test_bug607145.html \ test_bug601061.html \ + test_bug596511.html \ reflect.js \ $(NULL) diff --git a/content/html/content/test/test_bug345624-1.html b/content/html/content/test/test_bug345624-1.html index 2fc6da217fc6..b16575917b08 100644 --- a/content/html/content/test/test_bug345624-1.html +++ b/content/html/content/test/test_bug345624-1.html @@ -23,8 +23,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=345624 + -
@@ -94,8 +94,8 @@ function checkDefaultPseudoClass()
      "Nor :valid and :invalid should apply");
 
   is(window.getComputedStyle(document.getElementById('o'), null)
-       .getPropertyValue('background-color'), "rgb(0, 0, 0)",
-     "Nor :valid and :invalid should apply");
+       .getPropertyValue('background-color'), "rgb(0, 255, 0)",
+     ":valid should apply");
 
   is(window.getComputedStyle(document.getElementById('obj'), null)
        .getPropertyValue('background-color'), "rgb(0, 0, 0)",
@@ -126,9 +126,9 @@ function checkSpecificWillValidate()
 {
   // fieldset, output, object, keygen (TODO) and select elements
   ok(!document.getElementById('f').willValidate, "Fielset element should be barred from constraint validation");
-  ok(!document.getElementById('o').willValidate, "Output element should be barred from constraint validation");
   ok(!document.getElementById('obj').willValidate, "Object element should be barred from constraint validation");
   todo(!document.getElementById('k').willValidate, "Keygen element should be barred from constraint validation");
+  ok(document.getElementById('o').willValidate, "Output element should not be barred from constraint validation");
   ok(document.getElementById('s').willValidate, "Select element should not be barred from constraint validation");
 
   // input element
@@ -189,13 +189,16 @@ function checkCommonWillValidate(element)
 {
   // Not checking the default value because it has been checked previously.
 
-  element.disabled = true;
-  ok(!element.willValidate, "Disabled element should be barred from constraint validation");
+  // Not checking output elements because they can't be disabled.
+  if (element.tagName != 'OUTPUT') {
+    element.disabled = true;
+    ok(!element.willValidate, "Disabled element should be barred from constraint validation");
 
-  is(window.getComputedStyle(element, null).getPropertyValue('background-color'),
-     "rgb(0, 0, 0)", "Nor :valid and :invalid should apply");
+    is(window.getComputedStyle(element, null).getPropertyValue('background-color'),
+       "rgb(0, 0, 0)", "Nor :valid and :invalid should apply");
 
-  element.removeAttribute('disabled');
+    element.removeAttribute('disabled');
+  }
 
   // TODO: If an element has a datalist element ancestor, it is barred from constraint validation.
 }
@@ -293,28 +296,30 @@ checkDefaultPseudoClass();
 
 checkSpecificWillValidate();
 
-// Not checking fieldset, output, object and keygen
+// Not checking fieldset, object and keygen
 // because they are always barred from constraint validation.
 checkCommonWillValidate(document.getElementById('i'));
 checkCommonWillValidate(document.getElementById('b'));
 checkCommonWillValidate(document.getElementById('s'));
 checkCommonWillValidate(document.getElementById('t'));
+checkCommonWillValidate(document.getElementById('o'));
 
 /* TODO: add "keygen" element */
 checkCustomError(document.getElementById('i'), false);
 checkCustomError(document.getElementById('b'), false);
 checkCustomError(document.getElementById('s'), false);
 checkCustomError(document.getElementById('t'), false);
+checkCustomError(document.getElementById('o'), false);
 checkCustomError(document.getElementById('f'), true);
-checkCustomError(document.getElementById('o'), true);
 checkCustomError(document.getElementById('obj'), true);
 
-// Not checking fieldset, output, object and keygen
+// Not checking fieldset, object and keygen
 // because they are always barred from constraint validation.
 checkCheckValidity(document.getElementById('i'));
 checkCheckValidity(document.getElementById('b'));
 checkCheckValidity(document.getElementById('s'));
 checkCheckValidity(document.getElementById('t'));
+checkCheckValidity(document.getElementById('o'));
 
 /* TODO: add "keygen" element */
 checkValidityStateObjectAliveWithoutElement("fieldset");
diff --git a/content/html/content/test/test_bug389797.html b/content/html/content/test/test_bug389797.html
index f6730204cea1..a34f995c5091 100644
--- a/content/html/content/test/test_bug389797.html
+++ b/content/html/content/test/test_bug389797.html
@@ -219,7 +219,7 @@ HTML_TAG("s", ""); // HTMLElement
 HTML_TAG("samp", ""); // HTMLElement
 HTML_TAG("script", "Script", [], [ "nsIScriptLoaderObserver" ]);
 HTML_TAG("section", "") // HTMLElement
-HTML_TAG("select", "Select");
+HTML_TAG("select", "Select", ["nsIDOMHTMLSelectElement_Mozilla_2_0_Branch"]);
 HTML_TAG("small", ""); // HTMLElement
 HTML_TAG("span", "Span");
 HTML_TAG("strike", ""); // HTMLElement
diff --git a/content/html/content/test/test_bug596511.html b/content/html/content/test/test_bug596511.html
new file mode 100644
index 000000000000..3af194b185fb
--- /dev/null
+++ b/content/html/content/test/test_bug596511.html
@@ -0,0 +1,230 @@
+
+
+
+
+  Test for Bug 596511
+  
+  
+  
+  
+
+
+Mozilla Bug 596511
+

+ +
+
+
+ + diff --git a/content/media/Makefile.in b/content/media/Makefile.in index 4953bfd07432..e4e4563fc8b4 100644 --- a/content/media/Makefile.in +++ b/content/media/Makefile.in @@ -98,6 +98,8 @@ endif FORCE_STATIC_LIB = 1 +include $(topsrcdir)/config/config.mk +include $(topsrcdir)/ipc/chromium/chromium-config.mk include $(topsrcdir)/config/rules.mk INCLUDES += \ diff --git a/content/media/nsAudioStream.cpp b/content/media/nsAudioStream.cpp index 5915713d177b..739956409087 100644 --- a/content/media/nsAudioStream.cpp +++ b/content/media/nsAudioStream.cpp @@ -35,6 +35,15 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ + +#ifdef MOZ_IPC +#include "mozilla/dom/ContentChild.h" +#include "mozilla/dom/PAudioChild.h" +#include "mozilla/dom/AudioChild.h" +#include "nsXULAppAPI.h" +using namespace mozilla::dom; +#endif + #include #include #include "prlog.h" @@ -46,6 +55,7 @@ extern "C" { #include "sydneyaudio/sydney_audio.h" } #include "mozilla/TimeStamp.h" +#include "nsThreadUtils.h" #if defined(XP_MACOSX) #define SA_PER_STREAM_VOLUME 1 @@ -57,21 +67,282 @@ using mozilla::TimeStamp; PRLogModuleInfo* gAudioStreamLog = nsnull; #endif +#ifdef MOZ_IPC +static nsIThread *gAudioPlaybackThread = nsnull; +#endif + #define FAKE_BUFFER_SIZE 176400 #define MILLISECONDS_PER_SECOND 1000 +class nsAudioStreamLocal : public nsAudioStream +{ + public: + NS_DECL_ISUPPORTS + + ~nsAudioStreamLocal(); + nsAudioStreamLocal(); + + nsresult Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat aFormat); + void Shutdown(); + nsresult Write(const void* aBuf, PRUint32 aCount, PRBool aBlocking); + PRUint32 Available(); + void SetVolume(float aVolume); + void Drain(); + void Pause(); + void Resume(); + PRInt64 GetPosition(); + PRInt64 GetSampleOffset(); + PRBool IsPaused(); + + private: + + double mVolume; + void* mAudioHandle; + int mRate; + int mChannels; + + SampleFormat mFormat; + + // When a Write() request is made, and the number of samples + // requested to be written exceeds the buffer size of the audio + // backend, the remaining samples are stored in this variable. They + // will be written on the next Write() request. + nsTArray mBufferOverflow; + + // PR_TRUE if this audio stream is paused. + PRPackedBool mPaused; + + // PR_TRUE if this stream has encountered an error. + PRPackedBool mInError; + +}; + +#ifdef MOZ_IPC +class nsAudioStreamRemote : public nsAudioStream +{ + public: + NS_DECL_ISUPPORTS + + nsAudioStreamRemote(); + ~nsAudioStreamRemote(); + + nsresult Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat aFormat); + void Shutdown(); + nsresult Write(const void* aBuf, PRUint32 aCount, PRBool aBlocking); + PRUint32 Available(); + void SetVolume(float aVolume); + void Drain(); + void Pause(); + void Resume(); + PRInt64 GetPosition(); + PRInt64 GetSampleOffset(); + PRBool IsPaused(); + + AudioChild* mAudioChild; + + SampleFormat mFormat; + int mRate; + int mChannels; + // PR_TRUE if this audio stream is paused. + PRPackedBool mPaused; + + PRInt32 mBytesPerSample; + + friend class AudioInitEvent; + friend class AudioShutdownEvent; + friend class AudioWriteEvent; + friend class AudioSetVolumeEvent; + friend class AudioPauseEvent; + friend class AudioDrainEvent; + friend class AudioGetSampleEvent; +}; + +class AudioInitEvent : public nsRunnable +{ + public: + AudioInitEvent(nsAudioStreamRemote* owner) + { + mOwner = owner; + } + + NS_IMETHOD Run() + { + ContentChild * cpc = ContentChild::GetSingleton(); + NS_ASSERTION(cpc, "Content Protocol is NULL!"); + mOwner->mAudioChild = static_cast (cpc->SendPAudioConstructor(mOwner->mChannels, + mOwner->mRate, + mOwner->mFormat)); + return NS_OK; + } + + nsRefPtr mOwner; +}; + +class AudioShutdownEvent : public nsRunnable +{ + public: + AudioShutdownEvent(nsAudioStreamRemote* owner) + { + mOwner = owner; + } + + NS_IMETHOD Run() + { + if (mOwner->mAudioChild) { + PAudioChild::Send__delete__(mOwner->mAudioChild); + mOwner->mAudioChild = nsnull; + } + mOwner = nsnull; + return NS_OK; + } + nsRefPtr mOwner; +}; + +class AudioWriteEvent : public nsRunnable +{ + public: + AudioWriteEvent(nsAudioStreamRemote* owner, + const void* aBuf, + PRUint32 aNumberOfSamples, + PRUint32 aBytesPerSample) + { + mOwner = owner; + mBytesPerSample = aBytesPerSample; + mBuffer.Assign((const char*)aBuf, aNumberOfSamples*aBytesPerSample); + } + + NS_IMETHOD Run() + { + if (!mOwner->mAudioChild) + return NS_OK; + + mOwner->mAudioChild->SendWrite(mBuffer, + mBuffer.Length() / mBytesPerSample); + return NS_OK; + } + + nsRefPtr mOwner; + nsCString mBuffer; + PRUint32 mBytesPerSample; +}; + +class AudioSetVolumeEvent : public nsRunnable +{ + public: + AudioSetVolumeEvent(nsAudioStreamRemote* owner, float volume) + { + mOwner = owner; + mVolume = volume; + } + + NS_IMETHOD Run() + { + if (!mOwner->mAudioChild) + return NS_OK; + + mOwner->mAudioChild->SendSetVolume(mVolume); + return NS_OK; + } + + nsRefPtr mOwner; + float mVolume; +}; + +class AudioDrainEvent : public nsRunnable +{ + public: + AudioDrainEvent(nsAudioStreamRemote* owner) + { + mOwner = owner; + } + + NS_IMETHOD Run() + { + if (!mOwner->mAudioChild) + return NS_OK; + + mOwner->mAudioChild->SendDrain(); + return NS_OK; + } + + nsRefPtr mOwner; +}; + + +class AudioPauseEvent : public nsRunnable +{ + public: + AudioPauseEvent(nsAudioStreamRemote* owner, PRBool pause) + { + mOwner = owner; + mPause = pause; + } + + NS_IMETHOD Run() + { + if (!mOwner->mAudioChild) + return NS_OK; + + if (mPause) + mOwner->mAudioChild->SendPause(); + else + mOwner->mAudioChild->SendResume(); + + return NS_OK; + } + + nsRefPtr mOwner; + PRBool mPause; +}; + + +#endif // MOZ_IPC + + void nsAudioStream::InitLibrary() { #ifdef PR_LOGGING gAudioStreamLog = PR_NewLogModule("nsAudioStream"); #endif + +#ifdef MOZ_IPC + // We only need this thread in the main process. + if (XRE_GetProcessType() == GeckoProcessType_Default) { + NS_NewThread(&gAudioPlaybackThread); + } +#endif } void nsAudioStream::ShutdownLibrary() { +#ifdef MOZ_IPC + NS_IF_RELEASE(gAudioPlaybackThread); +#endif } -nsAudioStream::nsAudioStream() : + +nsIThread * +nsAudioStream::GetGlobalThread() +{ +#ifdef MOZ_IPC + NS_IF_ADDREF(gAudioPlaybackThread); + return gAudioPlaybackThread; +#else + return nsnull; +#endif +} + +nsAudioStream* nsAudioStream::AllocateStream() +{ +#ifdef MOZ_IPC + if (XRE_GetProcessType() == GeckoProcessType_Content) { + return new nsAudioStreamRemote(); + } +#endif + return new nsAudioStreamLocal(); +} + +nsAudioStreamLocal::nsAudioStreamLocal() : mVolume(1.0), mAudioHandle(0), mRate(0), @@ -82,16 +353,16 @@ nsAudioStream::nsAudioStream() : { } -nsAudioStream::~nsAudioStream() -{ - Shutdown(); -} +nsAudioStreamLocal::~nsAudioStreamLocal(){} -nsresult nsAudioStream::Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat aFormat) +NS_IMPL_THREADSAFE_ISUPPORTS0(nsAudioStreamLocal) + +nsresult nsAudioStreamLocal::Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat aFormat) { mRate = aRate; mChannels = aNumChannels; mFormat = aFormat; + if (sa_stream_create_pcm(reinterpret_cast(&mAudioHandle), NULL, SA_MODE_WRONLY, @@ -100,7 +371,7 @@ nsresult nsAudioStream::Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat a aNumChannels) != SA_SUCCESS) { mAudioHandle = nsnull; mInError = PR_TRUE; - PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStream: sa_stream_create_pcm error")); + PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStreamLocal: sa_stream_create_pcm error")); return NS_ERROR_FAILURE; } @@ -108,16 +379,17 @@ nsresult nsAudioStream::Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat a sa_stream_destroy(static_cast(mAudioHandle)); mAudioHandle = nsnull; mInError = PR_TRUE; - PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStream: sa_stream_open error")); + PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStreamLocal: sa_stream_open error")); return NS_ERROR_FAILURE; } mInError = PR_FALSE; + return NS_OK; } -void nsAudioStream::Shutdown() +void nsAudioStreamLocal::Shutdown() { - if (!mAudioHandle) + if (!mAudioHandle) return; sa_stream_destroy(static_cast(mAudioHandle)); @@ -125,7 +397,7 @@ void nsAudioStream::Shutdown() mInError = PR_TRUE; } -nsresult nsAudioStream::Write(const void* aBuf, PRUint32 aCount, PRBool aBlocking) +nsresult nsAudioStreamLocal::Write(const void* aBuf, PRUint32 aCount, PRBool aBlocking) { NS_ABORT_IF_FALSE(aCount % mChannels == 0, "Buffer size must be divisible by channel count"); @@ -199,7 +471,7 @@ nsresult nsAudioStream::Write(const void* aBuf, PRUint32 aCount, PRBool aBlockin s_data.get(), count * sizeof(short)) != SA_SUCCESS) { - PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStream: sa_stream_write error")); + PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStreamLocal: sa_stream_write error")); mInError = PR_TRUE; return NS_ERROR_FAILURE; } @@ -207,7 +479,7 @@ nsresult nsAudioStream::Write(const void* aBuf, PRUint32 aCount, PRBool aBlockin return NS_OK; } -PRUint32 nsAudioStream::Available() +PRUint32 nsAudioStreamLocal::Available() { // If the audio backend failed to open, lie and say we'll accept some // data. @@ -221,12 +493,12 @@ PRUint32 nsAudioStream::Available() return s / sizeof(short); } -void nsAudioStream::SetVolume(float aVolume) +void nsAudioStreamLocal::SetVolume(float aVolume) { NS_ASSERTION(aVolume >= 0.0 && aVolume <= 1.0, "Invalid volume"); #if defined(SA_PER_STREAM_VOLUME) if (sa_stream_set_volume_abs(static_cast(mAudioHandle), aVolume) != SA_SUCCESS) { - PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStream: sa_stream_set_volume_abs error")); + PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStreamLocal: sa_stream_set_volume_abs error")); mInError = PR_TRUE; } #else @@ -234,7 +506,7 @@ void nsAudioStream::SetVolume(float aVolume) #endif } -void nsAudioStream::Drain() +void nsAudioStreamLocal::Drain() { if (mInError) return; @@ -244,19 +516,19 @@ void nsAudioStream::Drain() if (sa_stream_write(static_cast(mAudioHandle), mBufferOverflow.Elements(), mBufferOverflow.Length() * sizeof(short)) != SA_SUCCESS) - PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStream: sa_stream_write error")); + PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStreamLocal: sa_stream_write error")); mInError = PR_TRUE; return; } int r = sa_stream_drain(static_cast(mAudioHandle)); if (r != SA_SUCCESS && r != SA_ERROR_INVALID) { - PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStream: sa_stream_drain error")); + PR_LOG(gAudioStreamLog, PR_LOG_ERROR, ("nsAudioStreamLocal: sa_stream_drain error")); mInError = PR_TRUE; } } -void nsAudioStream::Pause() +void nsAudioStreamLocal::Pause() { if (mInError) return; @@ -264,7 +536,7 @@ void nsAudioStream::Pause() sa_stream_pause(static_cast(mAudioHandle)); } -void nsAudioStream::Resume() +void nsAudioStreamLocal::Resume() { if (mInError) return; @@ -272,17 +544,16 @@ void nsAudioStream::Resume() sa_stream_resume(static_cast(mAudioHandle)); } -PRInt64 nsAudioStream::GetPosition() +PRInt64 nsAudioStreamLocal::GetPosition() { PRInt64 sampleOffset = GetSampleOffset(); if (sampleOffset >= 0) { return ((MILLISECONDS_PER_SECOND * sampleOffset) / mRate / mChannels); } - return -1; } -PRInt64 nsAudioStream::GetSampleOffset() +PRInt64 nsAudioStreamLocal::GetSampleOffset() { if (mInError) { return -1; @@ -300,3 +571,141 @@ PRInt64 nsAudioStream::GetSampleOffset() return -1; } + +PRBool nsAudioStreamLocal::IsPaused() +{ + return mPaused; +} + +#ifdef MOZ_IPC + +nsAudioStreamRemote::nsAudioStreamRemote() + : mAudioChild(NULL), + mFormat(FORMAT_S16_LE), + mRate(0), + mChannels(0), + mPaused(PR_FALSE), + mBytesPerSample(1) +{} + +nsAudioStreamRemote::~nsAudioStreamRemote() +{} + +NS_IMPL_THREADSAFE_ISUPPORTS0(nsAudioStreamRemote) + +nsresult +nsAudioStreamRemote::Init(PRInt32 aNumChannels, + PRInt32 aRate, + SampleFormat aFormat) +{ + mRate = aRate; + mChannels = aNumChannels; + mFormat = aFormat; + + switch (mFormat) { + case FORMAT_U8: { + mBytesPerSample = sizeof(PRUint8); + break; + } + case FORMAT_S16_LE: { + mBytesPerSample = sizeof(short); + break; + } + case FORMAT_FLOAT32: { + mBytesPerSample = sizeof(float); + } + } + + nsCOMPtr event = new AudioInitEvent(this); + NS_DispatchToMainThread(event); + return NS_OK; +} + +void +nsAudioStreamRemote::Shutdown() +{ + nsCOMPtr event = new AudioShutdownEvent(this); + NS_DispatchToMainThread(event); +} + +nsresult +nsAudioStreamRemote::Write(const void* aBuf, + PRUint32 aCount, + PRBool aBlocking) +{ + nsCOMPtr event = new AudioWriteEvent(this, + aBuf, + aCount, + mBytesPerSample); + NS_DispatchToMainThread(event); + return NS_OK; +} + +PRUint32 +nsAudioStreamRemote::Available() +{ + return FAKE_BUFFER_SIZE; +} + +void +nsAudioStreamRemote::SetVolume(float aVolume) +{ + nsCOMPtr event = new AudioSetVolumeEvent(this, aVolume); + NS_DispatchToMainThread(event); +} + +void +nsAudioStreamRemote::Drain() +{ + nsCOMPtr event = new AudioDrainEvent(this); + NS_DispatchToMainThread(event, NS_DISPATCH_SYNC); +} + +void +nsAudioStreamRemote::Pause() +{ + mPaused = PR_TRUE; + nsCOMPtr event = new AudioPauseEvent(this, PR_TRUE); + NS_DispatchToMainThread(event); +} + +void +nsAudioStreamRemote::Resume() +{ + mPaused = PR_FALSE; + nsCOMPtr event = new AudioPauseEvent(this, PR_FALSE); + NS_DispatchToMainThread(event); +} + +PRInt64 nsAudioStreamRemote::GetPosition() +{ + PRInt64 sampleOffset = GetSampleOffset(); + if (sampleOffset >= 0) { + return ((MILLISECONDS_PER_SECOND * sampleOffset) / mRate / mChannels); + } + return 0; +} + +PRInt64 +nsAudioStreamRemote::GetSampleOffset() +{ + if(!mAudioChild) + return 0; + + PRInt64 offset = mAudioChild->GetLastKnownSampleOffset(); + if (offset == -1) + return 0; + + PRInt64 time = mAudioChild->GetLastKnownSampleOffsetTime(); + PRInt64 result = offset + (mRate * mChannels * (PR_IntervalNow() - time) / MILLISECONDS_PER_SECOND); + + return result; +} + +PRBool +nsAudioStreamRemote::IsPaused() +{ + return mPaused; +} + +#endif // MOZ_IPC diff --git a/content/media/nsAudioStream.h b/content/media/nsAudioStream.h index c89724bcb5d7..dbc75837e956 100644 --- a/content/media/nsAudioStream.h +++ b/content/media/nsAudioStream.h @@ -39,14 +39,13 @@ #define nsAudioStream_h_ #include "nscore.h" -#include "prlog.h" -#include "nsTArray.h" +#include "nsISupportsImpl.h" +#include "nsIThread.h" -extern PRLogModuleInfo* gAudioStreamLog; - -class nsAudioStream +class nsAudioStream : public nsISupports { - public: +public: + enum SampleFormat { FORMAT_U8, @@ -62,16 +61,23 @@ class nsAudioStream // library after using it. static void ShutdownLibrary(); - nsAudioStream(); - ~nsAudioStream(); + // Thread, usually for MOZ_IPC handling, that is shared between audio streams. + // This may return null in the child process + static nsIThread *GetGlobalThread(); + + // AllocateStream will return either a local stream or a remoted stream + // depending on where you call it from. If MOZ_IPC is enabled, and you + // call this from a child process, you may recieve an implementation which + // forwards to a compositing process. + static nsAudioStream* AllocateStream(); // Initialize the audio stream. aNumChannels is the number of audio channels // (1 for mono, 2 for stereo, etc) and aRate is the frequency of the sound // samples (22050, 44100, etc). - nsresult Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat aFormat); + virtual nsresult Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat aFormat) = 0; // Closes the stream. All future use of the stream is an error. - void Shutdown(); + virtual void Shutdown() = 0; // Write sound data to the audio hardware. aBuf is an array of samples in // the format specified by mFormat of length aCount. aCount should be @@ -79,54 +85,35 @@ class nsAudioStream // When aBlocking is PR_TRUE, we'll block until the write has completed, // otherwise we'll buffer any data we can't write immediately, and write // it in a later call. - nsresult Write(const void* aBuf, PRUint32 aCount, PRBool aBlocking); + virtual nsresult Write(const void* aBuf, PRUint32 aCount, PRBool aBlocking) = 0; // Return the number of sound samples that can be written to the audio device // without blocking. - PRUint32 Available(); + virtual PRUint32 Available() = 0; // Set the current volume of the audio playback. This is a value from // 0 (meaning muted) to 1 (meaning full volume). - void SetVolume(float aVolume); + virtual void SetVolume(float aVolume) = 0; // Block until buffered audio data has been consumed. - void Drain(); + virtual void Drain() = 0; // Pause audio playback - void Pause(); + virtual void Pause() = 0; // Resume audio playback - void Resume(); + virtual void Resume() = 0; // Return the position in milliseconds of the sample being played by the // audio hardware. - PRInt64 GetPosition(); + virtual PRInt64 GetPosition() = 0; // Return the position, measured in samples played since the start, by // the audio hardware. - PRInt64 GetSampleOffset(); + virtual PRInt64 GetSampleOffset() = 0; // Returns PR_TRUE when the audio stream is paused. - PRBool IsPaused() { return mPaused; } - - private: - double mVolume; - void* mAudioHandle; - int mRate; - int mChannels; - - SampleFormat mFormat; - - // When a Write() request is made, and the number of samples - // requested to be written exceeds the buffer size of the audio - // backend, the remaining samples are stored in this variable. They - // will be written on the next Write() request. - nsTArray mBufferOverflow; - - // PR_TRUE if this audio stream is paused. - PRPackedBool mPaused; - - // PR_TRUE if this stream has encountered an error. - PRPackedBool mInError; + virtual PRBool IsPaused() = 0; }; + #endif diff --git a/content/media/nsBuiltinDecoderStateMachine.cpp b/content/media/nsBuiltinDecoderStateMachine.cpp index 4f7ce39cb54f..944d06550539 100644 --- a/content/media/nsBuiltinDecoderStateMachine.cpp +++ b/content/media/nsBuiltinDecoderStateMachine.cpp @@ -161,6 +161,12 @@ nsBuiltinDecoderStateMachine::nsBuiltinDecoderStateMachine(nsBuiltinDecoder* aDe nsBuiltinDecoderStateMachine::~nsBuiltinDecoderStateMachine() { MOZ_COUNT_DTOR(nsBuiltinDecoderStateMachine); + + if (mAudioStream) { + MonitorAutoEnter mon(mDecoder->GetMonitor()); + mAudioStream->Shutdown(); + mAudioStream = nsnull; + } } PRBool nsBuiltinDecoderStateMachine::HasFutureAudio() const { @@ -611,7 +617,7 @@ void nsBuiltinDecoderStateMachine::StartPlayback() } else { // No audiostream, create one. const nsVideoInfo& info = mReader->GetInfo(); - mAudioStream = new nsAudioStream(); + mAudioStream = nsAudioStream::AllocateStream(); mAudioStream->Init(info.mAudioChannels, info.mAudioRate, MOZ_SOUND_DATA_FORMAT); diff --git a/content/media/nsBuiltinDecoderStateMachine.h b/content/media/nsBuiltinDecoderStateMachine.h index c5aa9b8887cb..925e2b0b4202 100644 --- a/content/media/nsBuiltinDecoderStateMachine.h +++ b/content/media/nsBuiltinDecoderStateMachine.h @@ -426,7 +426,7 @@ protected: // The audio stream resource. Used on the state machine, audio, and main // threads. You must hold the mAudioMonitor, and must NOT hold the decoder // monitor when using the audio stream! - nsAutoPtr mAudioStream; + nsRefPtr mAudioStream; // The reader, don't call its methods with the decoder monitor held. // This is created in the play state machine's constructor, and destroyed diff --git a/content/media/wave/nsWaveDecoder.cpp b/content/media/wave/nsWaveDecoder.cpp index 9efb1822a9fd..a4d935dfc1a7 100644 --- a/content/media/wave/nsWaveDecoder.cpp +++ b/content/media/wave/nsWaveDecoder.cpp @@ -295,7 +295,7 @@ private: // Our audio stream. Created on demand when entering playback state. It // is destroyed when seeking begins and will not be reinitialized until // playback resumes, so it is possible for this to be null. - nsAutoPtr mAudioStream; + nsRefPtr mAudioStream; // Maximum time to spend waiting for data during buffering. TimeDuration mBufferingWait; @@ -906,7 +906,7 @@ nsWaveStateMachine::ChangeState(State aState) void nsWaveStateMachine::OpenAudioStream() { - mAudioStream = new nsAudioStream(); + mAudioStream = nsAudioStream::AllocateStream(); if (!mAudioStream) { LOG(PR_LOG_ERROR, ("Could not create audio stream")); } else { diff --git a/content/svg/content/src/nsSVGLength2.cpp b/content/svg/content/src/nsSVGLength2.cpp index 55a841d0d28b..1f1f318133a1 100644 --- a/content/svg/content/src/nsSVGLength2.cpp +++ b/content/svg/content/src/nsSVGLength2.cpp @@ -307,6 +307,7 @@ nsSVGLength2::SetBaseValueInSpecifiedUnits(float aValue, nsSVGElement *aSVGElement) { mBaseVal = aValue; + mIsBaseSet = PR_TRUE; if (!mIsAnimated) { mAnimVal = mBaseVal; } @@ -344,6 +345,7 @@ nsSVGLength2::NewValueSpecifiedUnits(PRUint16 unitType, return NS_ERROR_DOM_NOT_SUPPORTED_ERR; mBaseVal = valueInSpecifiedUnits; + mIsBaseSet = PR_TRUE; mSpecifiedUnitType = PRUint8(unitType); if (!mIsAnimated) { mAnimVal = mBaseVal; @@ -413,6 +415,7 @@ nsSVGLength2::SetBaseValueString(const nsAString &aValueAsString, } mBaseVal = value; + mIsBaseSet = PR_TRUE; mSpecifiedUnitType = PRUint8(unitType); if (!mIsAnimated) { mAnimVal = mBaseVal; @@ -445,6 +448,7 @@ void nsSVGLength2::SetBaseValue(float aValue, nsSVGElement *aSVGElement) { mBaseVal = aValue * GetUnitScaleFactor(aSVGElement, mSpecifiedUnitType); + mIsBaseSet = PR_TRUE; if (!mIsAnimated) { mAnimVal = mBaseVal; } diff --git a/content/svg/content/src/nsSVGLength2.h b/content/svg/content/src/nsSVGLength2.h index b1d7e8729109..2c43d10c3800 100644 --- a/content/svg/content/src/nsSVGLength2.h +++ b/content/svg/content/src/nsSVGLength2.h @@ -64,6 +64,7 @@ public: mAttrEnum = aAttrEnum; mCtxType = aCtxType; mIsAnimated = PR_FALSE; + mIsBaseSet = PR_FALSE; } nsresult SetBaseValueString(const nsAString& aValue, @@ -90,6 +91,14 @@ public: { return mBaseVal / GetUnitScaleFactor(aCtx, mSpecifiedUnitType); } float GetAnimValue(nsSVGSVGElement* aCtx) const { return mAnimVal / GetUnitScaleFactor(aCtx, mSpecifiedUnitType); } + + // Returns PR_TRUE if the animated value of this length has been explicitly + // set (either by animation, or by taking on the base value which has been + // explicitly set by markup or a DOM call), PR_FALSE otherwise. + // If this returns PR_FALSE, the animated value is still valid, that is, + // useable, and represents the default base value of the attribute. + PRBool IsAnimValSet() const + { return mIsAnimated || mIsBaseSet; } nsresult ToDOMAnimatedLength(nsIDOMSVGAnimatedLength **aResult, nsSVGElement* aSVGElement); @@ -105,7 +114,8 @@ private: PRUint8 mSpecifiedUnitType; PRUint8 mAttrEnum; // element specified tracking for attribute PRUint8 mCtxType; // X, Y or Unspecified - PRPackedBool mIsAnimated; + PRPackedBool mIsAnimated:1; + PRPackedBool mIsBaseSet:1; static float GetMMPerPixel() { return MM_PER_INCH_FLOAT / 96; } float GetAxisLength(nsIFrame *aNonSVGFrame) const; diff --git a/content/svg/content/src/nsSVGRectElement.cpp b/content/svg/content/src/nsSVGRectElement.cpp index 8c08d892f75a..6fb3c8e94ada 100644 --- a/content/svg/content/src/nsSVGRectElement.cpp +++ b/content/svg/content/src/nsSVGRectElement.cpp @@ -190,10 +190,10 @@ nsSVGRectElement::ConstructPath(gfxContext *aCtx) return; } - /* If either the 'rx' or the 'ry' attribute isn't set in the markup, then we + /* If either the 'rx' or the 'ry' attribute isn't set, then we have to set it to the value of the other. */ - PRBool hasRx = HasAttr(kNameSpaceID_None, nsGkAtoms::rx); - PRBool hasRy = HasAttr(kNameSpaceID_None, nsGkAtoms::ry); + PRBool hasRx = mLengthAttributes[RX].IsAnimValSet(); + PRBool hasRy = mLengthAttributes[RY].IsAnimValSet(); if (hasRx && !hasRy) ry = rx; else if (hasRy && !hasRx) diff --git a/content/svg/content/src/nsSVGSVGElement.cpp b/content/svg/content/src/nsSVGSVGElement.cpp index 7c0e46b75fae..2bbe374a2ccf 100644 --- a/content/svg/content/src/nsSVGSVGElement.cpp +++ b/content/svg/content/src/nsSVGSVGElement.cpp @@ -60,6 +60,7 @@ #include "nsGUIEvent.h" #include "nsSVGUtils.h" #include "nsSVGSVGElement.h" +#include "nsSVGEffects.h" // For nsSVGEffects::RemoveAllRenderingObservers #ifdef MOZ_SMIL #include "nsEventDispatcher.h" @@ -1229,3 +1230,12 @@ nsSVGSVGElement::GetPreserveAspectRatio() { return &mPreserveAspectRatio; } + +#ifndef MOZ_ENABLE_LIBXUL +// XXXdholbert HACK -- see comment w/ this method's declaration in header file. +void +nsSVGSVGElement::RemoveAllRenderingObservers() +{ + nsSVGEffects::RemoveAllRenderingObservers(this); +} +#endif // !MOZ_LIBXUL diff --git a/content/svg/content/src/nsSVGSVGElement.h b/content/svg/content/src/nsSVGSVGElement.h index bebeaa1f4c35..6811bca2fca0 100644 --- a/content/svg/content/src/nsSVGSVGElement.h +++ b/content/svg/content/src/nsSVGSVGElement.h @@ -221,6 +221,14 @@ public: } virtual nsXPCClassInfo* GetClassInfo(); + +#ifndef MOZ_ENABLE_LIBXUL + // XXXdholbert HACK to call static method + // nsSVGEffects::RemoveAllRenderingObservers() on myself, on behalf + // of imagelib in non-libxul builds. + virtual void RemoveAllRenderingObservers(); +#endif // !MOZ_LIBXUL + protected: // nsSVGElement overrides PRBool IsEventName(nsIAtom* aName); diff --git a/content/svg/content/src/nsSVGScriptElement.cpp b/content/svg/content/src/nsSVGScriptElement.cpp index 3a523bf3aa71..fc3ef730ea0d 100644 --- a/content/svg/content/src/nsSVGScriptElement.cpp +++ b/content/svg/content/src/nsSVGScriptElement.cpp @@ -231,6 +231,8 @@ nsSVGScriptElement::FreezeUriAsyncDefer() if (!src.IsEmpty()) { nsCOMPtr baseURI = GetBaseURI(); NS_NewURI(getter_AddRefs(mUri), src, nsnull, baseURI); + // At this point mUri will be null for invalid URLs. + mExternal = PR_TRUE; } mFrozen = PR_TRUE; @@ -245,7 +247,7 @@ nsSVGScriptElement::HasScriptContent() nsAutoString src; mStringAttributes[HREF].GetAnimValue(src, this); // preserving bug 528444 here due to being unsure how to fix correctly - return (mFrozen ? !!mUri : !src.IsEmpty()) || + return (mFrozen ? mExternal : !src.IsEmpty()) || nsContentUtils::HasNonEmptyTextContent(this); } diff --git a/content/xul/templates/tests/chrome/templates_shared.js b/content/xul/templates/tests/chrome/templates_shared.js index b50bbb44cc27..941d05c7bcc5 100644 --- a/content/xul/templates/tests/chrome/templates_shared.js +++ b/content/xul/templates/tests/chrome/templates_shared.js @@ -75,8 +75,13 @@ function test_template() var src = window.location.href.replace(/test_tmpl.*xul/, "animals.rdf"); ds = RDF.GetDataSourceBlocking(src); - if (root.datasources == "rdf:null") - root.datasources = "animals.rdf"; + 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"); @@ -126,7 +131,8 @@ function iterateChanged(root, ds) if (needsOpen) root.open = false; - compareConsoleMessages(); + if (expectedConsoleMessages.length) + compareConsoleMessages(); SimpleTest.finish(); } @@ -172,7 +178,8 @@ function checkResults(root, step) serializedXML = serializedXML.replace(nsrepl, ""); if (debug) dump("-------- " + adjtestid + " " + error + ":\n" + serializedXML + "\n"); - is(serializedXML, "Same", "Error is: " + error); + if (error) + is(serializedXML, "Same", "Error is: " + error); } } diff --git a/content/xul/templates/tests/chrome/test_tmpl_bindingsmultiple.xul b/content/xul/templates/tests/chrome/test_tmpl_bindingsmultiple.xul index 83a3e6e3d2d3..f63209a6ec91 100644 --- a/content/xul/templates/tests/chrome/test_tmpl_bindingsmultiple.xul +++ b/content/xul/templates/tests/chrome/test_tmpl_bindingsmultiple.xul @@ -58,7 +58,7 @@ var changes = [ ]]> - +