mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-17 23:35:34 +00:00
Merge mozilla-central to services-central
This commit is contained in:
commit
bbcabe833e
@ -157,11 +157,12 @@ NotificationController::ScheduleContentInsertion(nsAccessible* aContainer,
|
||||
if (mTreeConstructedState == eTreeConstructionPending)
|
||||
return;
|
||||
|
||||
nsRefPtr<ContentInsertion> insertion =
|
||||
new ContentInsertion(mDocument, aContainer, aStartChildNode, aEndChildNode);
|
||||
|
||||
if (insertion && mContentInsertions.AppendElement(insertion))
|
||||
nsRefPtr<ContentInsertion> insertion = new ContentInsertion(mDocument,
|
||||
aContainer);
|
||||
if (insertion && insertion->InitChildList(aStartChildNode, aEndChildNode) &&
|
||||
mContentInsertions.AppendElement(insertion)) {
|
||||
ScheduleProcessing();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -681,15 +682,31 @@ NotificationController::TextEnumerator(nsCOMPtrHashKey<nsIContent>* aEntry,
|
||||
// NotificationController: content inserted notification
|
||||
|
||||
NotificationController::ContentInsertion::
|
||||
ContentInsertion(nsDocAccessible* aDocument, nsAccessible* aContainer,
|
||||
nsIContent* aStartChildNode, nsIContent* aEndChildNode) :
|
||||
ContentInsertion(nsDocAccessible* aDocument, nsAccessible* aContainer) :
|
||||
mDocument(aDocument), mContainer(aContainer)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
NotificationController::ContentInsertion::
|
||||
InitChildList(nsIContent* aStartChildNode, nsIContent* aEndChildNode)
|
||||
{
|
||||
bool haveToUpdate = false;
|
||||
|
||||
nsIContent* node = aStartChildNode;
|
||||
while (node != aEndChildNode) {
|
||||
mInsertedContent.AppendElement(node);
|
||||
// Notification triggers for content insertion even if no content was
|
||||
// actually inserted, check if the given content has a frame to discard
|
||||
// this case early.
|
||||
if (node->GetPrimaryFrame()) {
|
||||
if (mInsertedContent.AppendElement(node))
|
||||
haveToUpdate = true;
|
||||
}
|
||||
|
||||
node = node->GetNextSibling();
|
||||
}
|
||||
|
||||
return haveToUpdate;
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(NotificationController::ContentInsertion)
|
||||
|
@ -321,13 +321,13 @@ private:
|
||||
class ContentInsertion
|
||||
{
|
||||
public:
|
||||
ContentInsertion(nsDocAccessible* aDocument, nsAccessible* aContainer,
|
||||
nsIContent* aStartChildNode, nsIContent* aEndChildNode);
|
||||
ContentInsertion(nsDocAccessible* aDocument, nsAccessible* aContainer);
|
||||
virtual ~ContentInsertion() { mDocument = nsnull; }
|
||||
|
||||
NS_INLINE_DECL_REFCOUNTING(ContentInsertion)
|
||||
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(ContentInsertion)
|
||||
|
||||
bool InitChildList(nsIContent* aStartChildNode, nsIContent* aEndChildNode);
|
||||
void Process();
|
||||
|
||||
private:
|
||||
|
@ -66,8 +66,8 @@ struct WalkState
|
||||
|
||||
nsAccTreeWalker::
|
||||
nsAccTreeWalker(nsIWeakReference* aShell, nsIContent* aContent,
|
||||
PRBool aWalkAnonContent) :
|
||||
mWeakShell(aShell), mState(nsnull)
|
||||
PRBool aWalkAnonContent, bool aWalkCache) :
|
||||
mWeakShell(aShell), mState(nsnull), mWalkCache(aWalkCache)
|
||||
{
|
||||
NS_ASSERTION(aContent, "No node for the accessible tree walker!");
|
||||
|
||||
@ -114,7 +114,8 @@ nsAccTreeWalker::NextChildInternal(bool aNoWalkUp)
|
||||
mState->childIdx++;
|
||||
|
||||
bool isSubtreeHidden = false;
|
||||
nsAccessible* accessible =
|
||||
nsAccessible* accessible = mWalkCache ?
|
||||
GetAccService()->GetAccessibleInWeakShell(childNode, mWeakShell) :
|
||||
GetAccService()->GetOrCreateAccessible(childNode, presShell, mWeakShell,
|
||||
&isSubtreeHidden);
|
||||
|
||||
|
@ -54,7 +54,7 @@ class nsAccTreeWalker
|
||||
{
|
||||
public:
|
||||
nsAccTreeWalker(nsIWeakReference *aShell, nsIContent *aNode,
|
||||
PRBool aWalkAnonymousContent);
|
||||
PRBool aWalkAnonymousContent, bool aWalkCache = false);
|
||||
virtual ~nsAccTreeWalker();
|
||||
|
||||
/**
|
||||
@ -95,6 +95,7 @@ private:
|
||||
|
||||
nsCOMPtr<nsIWeakReference> mWeakShell;
|
||||
PRInt32 mChildFilter;
|
||||
bool mWalkCache;
|
||||
WalkState* mState;
|
||||
};
|
||||
|
||||
|
@ -474,6 +474,9 @@ nsAccessible::GetFirstChild(nsIAccessible **aFirstChild)
|
||||
NS_ENSURE_ARG_POINTER(aFirstChild);
|
||||
*aFirstChild = nsnull;
|
||||
|
||||
if (IsDefunct())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
PRInt32 childCount = GetChildCount();
|
||||
NS_ENSURE_TRUE(childCount != -1, NS_ERROR_FAILURE);
|
||||
|
||||
@ -490,6 +493,9 @@ nsAccessible::GetLastChild(nsIAccessible **aLastChild)
|
||||
NS_ENSURE_ARG_POINTER(aLastChild);
|
||||
*aLastChild = nsnull;
|
||||
|
||||
if (IsDefunct())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
PRInt32 childCount = GetChildCount();
|
||||
NS_ENSURE_TRUE(childCount != -1, NS_ERROR_FAILURE);
|
||||
|
||||
@ -503,6 +509,9 @@ nsAccessible::GetChildAt(PRInt32 aChildIndex, nsIAccessible **aChild)
|
||||
NS_ENSURE_ARG_POINTER(aChild);
|
||||
*aChild = nsnull;
|
||||
|
||||
if (IsDefunct())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
PRInt32 childCount = GetChildCount();
|
||||
NS_ENSURE_TRUE(childCount != -1, NS_ERROR_FAILURE);
|
||||
|
||||
@ -526,6 +535,9 @@ nsAccessible::GetChildren(nsIArray **aOutChildren)
|
||||
NS_ENSURE_ARG_POINTER(aOutChildren);
|
||||
*aOutChildren = nsnull;
|
||||
|
||||
if (IsDefunct())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
PRInt32 childCount = GetChildCount();
|
||||
NS_ENSURE_TRUE(childCount != -1, NS_ERROR_FAILURE);
|
||||
|
||||
@ -555,6 +567,9 @@ nsAccessible::GetChildCount(PRInt32 *aChildCount)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aChildCount);
|
||||
|
||||
if (IsDefunct())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
*aChildCount = GetChildCount();
|
||||
return *aChildCount != -1 ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
@ -2812,9 +2827,6 @@ nsAccessible::RemoveChild(nsAccessible* aChild)
|
||||
nsAccessible*
|
||||
nsAccessible::GetChildAt(PRUint32 aIndex)
|
||||
{
|
||||
if (EnsureChildren())
|
||||
return nsnull;
|
||||
|
||||
nsAccessible *child = mChildren.SafeElementAt(aIndex, nsnull);
|
||||
if (!child)
|
||||
return nsnull;
|
||||
@ -2831,14 +2843,13 @@ nsAccessible::GetChildAt(PRUint32 aIndex)
|
||||
PRInt32
|
||||
nsAccessible::GetChildCount()
|
||||
{
|
||||
return EnsureChildren() ? -1 : mChildren.Length();
|
||||
return mChildren.Length();
|
||||
}
|
||||
|
||||
PRInt32
|
||||
nsAccessible::GetIndexOf(nsAccessible* aChild)
|
||||
{
|
||||
return EnsureChildren() || (aChild->mParent != this) ?
|
||||
-1 : aChild->GetIndexInParent();
|
||||
return (aChild->mParent != this) ? -1 : aChild->GetIndexInParent();
|
||||
}
|
||||
|
||||
PRInt32
|
||||
@ -2850,9 +2861,6 @@ nsAccessible::GetIndexInParent() const
|
||||
PRInt32
|
||||
nsAccessible::GetEmbeddedChildCount()
|
||||
{
|
||||
if (EnsureChildren())
|
||||
return -1;
|
||||
|
||||
if (IsChildrenFlag(eMixedChildren)) {
|
||||
if (!mEmbeddedObjCollector)
|
||||
mEmbeddedObjCollector = new EmbeddedObjCollector(this);
|
||||
@ -2865,9 +2873,6 @@ nsAccessible::GetEmbeddedChildCount()
|
||||
nsAccessible*
|
||||
nsAccessible::GetEmbeddedChildAt(PRUint32 aIndex)
|
||||
{
|
||||
if (EnsureChildren())
|
||||
return nsnull;
|
||||
|
||||
if (IsChildrenFlag(eMixedChildren)) {
|
||||
if (!mEmbeddedObjCollector)
|
||||
mEmbeddedObjCollector = new EmbeddedObjCollector(this);
|
||||
@ -2881,9 +2886,6 @@ nsAccessible::GetEmbeddedChildAt(PRUint32 aIndex)
|
||||
PRInt32
|
||||
nsAccessible::GetIndexOfEmbeddedChild(nsAccessible* aChild)
|
||||
{
|
||||
if (EnsureChildren())
|
||||
return -1;
|
||||
|
||||
if (IsChildrenFlag(eMixedChildren)) {
|
||||
if (!mEmbeddedObjCollector)
|
||||
mEmbeddedObjCollector = new EmbeddedObjCollector(this);
|
||||
|
@ -1427,7 +1427,7 @@ nsDocAccessible::ContentRemoved(nsIContent* aContainerNode,
|
||||
nsAccessible* container = aContainerNode ?
|
||||
GetAccessibleOrContainer(aContainerNode) : this;
|
||||
|
||||
UpdateTree(container, aChildNode, PR_FALSE);
|
||||
UpdateTree(container, aChildNode, false);
|
||||
}
|
||||
|
||||
void
|
||||
@ -1444,9 +1444,9 @@ nsDocAccessible::RecreateAccessible(nsIContent* aContent)
|
||||
nsAccessible* container = GetAccessibleOrContainer(parentContent);
|
||||
|
||||
// Remove and reinsert.
|
||||
UpdateTree(container, aContent, PR_FALSE);
|
||||
UpdateTree(container, aContent, false);
|
||||
container->UpdateChildren();
|
||||
UpdateTree(container, aContent, PR_TRUE);
|
||||
UpdateTree(container, aContent, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1468,15 +1468,20 @@ nsDocAccessible::NotifyOfCachingEnd(nsAccessible* aAccessible)
|
||||
// invalidation list.
|
||||
for (PRUint32 idx = 0; idx < mInvalidationList.Length(); idx++) {
|
||||
nsIContent* content = mInvalidationList[idx];
|
||||
if (!HasAccessible(content)) {
|
||||
// Make sure we keep children updated. While we're inside of caching
|
||||
// loop then we must exist it with cached children.
|
||||
nsAccessible* accessible = GetAccessible(content);
|
||||
if (!accessible) {
|
||||
nsAccessible* container = GetContainerAccessible(content);
|
||||
NS_ASSERTION(container,
|
||||
"Got a referenced element that is not in document!");
|
||||
if (container)
|
||||
if (container) {
|
||||
container->UpdateChildren();
|
||||
accessible = GetAccessible(content);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the subtree is created.
|
||||
if (accessible)
|
||||
CacheChildrenInSubtree(accessible);
|
||||
}
|
||||
mInvalidationList.Clear();
|
||||
|
||||
@ -1797,16 +1802,28 @@ nsDocAccessible::ProcessContentInserted(nsAccessible* aContainer,
|
||||
nsAccessible* directContainer =
|
||||
GetContainerAccessible(aInsertedContent->ElementAt(idx));
|
||||
if (directContainer)
|
||||
UpdateTree(directContainer, aInsertedContent->ElementAt(idx), PR_TRUE);
|
||||
UpdateTree(directContainer, aInsertedContent->ElementAt(idx), true);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDocAccessible::UpdateTree(nsAccessible* aContainer, nsIContent* aChildNode,
|
||||
PRBool aIsInsert)
|
||||
bool aIsInsert)
|
||||
{
|
||||
PRUint32 updateFlags =
|
||||
UpdateTreeInternal(aChildNode, aChildNode->GetNextSibling(), aIsInsert);
|
||||
PRUint32 updateFlags = eNoAccessible;
|
||||
|
||||
// If child node is not accessible then look for its accessible children.
|
||||
nsAccessible* child = GetAccessible(aChildNode);
|
||||
if (child) {
|
||||
updateFlags |= UpdateTreeInternal(child, aIsInsert);
|
||||
|
||||
} else {
|
||||
nsAccTreeWalker walker(mWeakShell, aChildNode,
|
||||
aContainer->GetAllowsAnonChildAccessibles(), true);
|
||||
|
||||
while ((child = walker.NextChild()))
|
||||
updateFlags |= UpdateTreeInternal(child, aIsInsert);
|
||||
}
|
||||
|
||||
// Content insertion/removal is not cause of accessible tree change.
|
||||
if (updateFlags == eNoAccessible)
|
||||
@ -1849,97 +1866,77 @@ nsDocAccessible::UpdateTree(nsAccessible* aContainer, nsIContent* aChildNode,
|
||||
}
|
||||
|
||||
PRUint32
|
||||
nsDocAccessible::UpdateTreeInternal(nsIContent* aStartNode,
|
||||
nsIContent* aEndNode,
|
||||
PRBool aIsInsert)
|
||||
nsDocAccessible::UpdateTreeInternal(nsAccessible* aChild, bool aIsInsert)
|
||||
{
|
||||
PRUint32 updateFlags = eNoAccessible;
|
||||
for (nsIContent* node = aStartNode; node != aEndNode;
|
||||
node = node->GetNextSibling()) {
|
||||
PRUint32 updateFlags = eAccessible;
|
||||
|
||||
// Tree update triggers for content insertion even if no content was
|
||||
// inserted actually, check if the given content has a frame to discard
|
||||
// this case early.
|
||||
if (aIsInsert && !node->GetPrimaryFrame())
|
||||
continue;
|
||||
nsINode* node = aChild->GetNode();
|
||||
if (aIsInsert) {
|
||||
// Create accessible tree for shown accessible.
|
||||
CacheChildrenInSubtree(aChild);
|
||||
|
||||
nsAccessible* accessible = GetAccessible(node);
|
||||
} else {
|
||||
// Fire menupopup end event before hide event if a menu goes away.
|
||||
|
||||
if (!accessible) {
|
||||
updateFlags |= UpdateTreeInternal(node->GetFirstChild(), nsnull,
|
||||
aIsInsert);
|
||||
continue;
|
||||
// XXX: We don't look into children of hidden subtree to find hiding
|
||||
// menupopup (as we did prior bug 570275) because we don't do that when
|
||||
// menu is showing (and that's impossible until bug 606924 is fixed).
|
||||
// Nevertheless we should do this at least because layout coalesces
|
||||
// the changes before our processing and we may miss some menupopup
|
||||
// events. Now we just want to be consistent in content insertion/removal
|
||||
// handling.
|
||||
if (aChild->ARIARole() == nsIAccessibleRole::ROLE_MENUPOPUP) {
|
||||
nsRefPtr<AccEvent> event =
|
||||
new AccEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END, aChild);
|
||||
|
||||
if (event)
|
||||
FireDelayedAccessibleEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
// Fire show/hide event.
|
||||
nsRefPtr<AccEvent> event;
|
||||
if (aIsInsert)
|
||||
event = new AccShowEvent(aChild, node);
|
||||
else
|
||||
event = new AccHideEvent(aChild, node);
|
||||
|
||||
if (event)
|
||||
FireDelayedAccessibleEvent(event);
|
||||
|
||||
if (aIsInsert) {
|
||||
PRUint32 ariaRole = aChild->ARIARole();
|
||||
if (ariaRole == nsIAccessibleRole::ROLE_MENUPOPUP) {
|
||||
// Fire EVENT_MENUPOPUP_START if ARIA menu appears.
|
||||
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_START,
|
||||
node, AccEvent::eRemoveDupes);
|
||||
|
||||
} else if (ariaRole == nsIAccessibleRole::ROLE_ALERT) {
|
||||
// Fire EVENT_ALERT if ARIA alert appears.
|
||||
updateFlags = eAlertAccessible;
|
||||
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_ALERT, node,
|
||||
AccEvent::eRemoveDupes);
|
||||
}
|
||||
|
||||
updateFlags |= eAccessible;
|
||||
|
||||
if (aIsInsert) {
|
||||
// Create accessible tree for shown accessible.
|
||||
CacheChildrenInSubtree(accessible);
|
||||
|
||||
} else {
|
||||
// Fire menupopup end event before hide event if a menu goes away.
|
||||
|
||||
// XXX: We don't look into children of hidden subtree to find hiding
|
||||
// menupopup (as we did prior bug 570275) because we don't do that when
|
||||
// menu is showing (and that's impossible until bug 606924 is fixed).
|
||||
// Nevertheless we should do this at least because layout coalesces
|
||||
// the changes before our processing and we may miss some menupopup
|
||||
// events. Now we just want to be consistent in content insertion/removal
|
||||
// handling.
|
||||
if (accessible->ARIARole() == nsIAccessibleRole::ROLE_MENUPOPUP) {
|
||||
nsRefPtr<AccEvent> event =
|
||||
new AccEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END, accessible);
|
||||
|
||||
if (event)
|
||||
FireDelayedAccessibleEvent(event);
|
||||
}
|
||||
// If focused node has been shown then it means its frame was recreated
|
||||
// while it's focused. Fire focus event on new focused accessible. If
|
||||
// the queue contains focus event for this node then it's suppressed by
|
||||
// this one.
|
||||
if (node == gLastFocusedNode) {
|
||||
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_FOCUS,
|
||||
node, AccEvent::eCoalesceFromSameDocument);
|
||||
}
|
||||
} else {
|
||||
// Update the tree for content removal.
|
||||
// The accessible parent may differ from container accessible if
|
||||
// the parent doesn't have own DOM node like list accessible for HTML
|
||||
// selects.
|
||||
nsAccessible* parent = aChild->GetParent();
|
||||
NS_ASSERTION(parent, "No accessible parent?!");
|
||||
if (parent)
|
||||
parent->RemoveChild(aChild);
|
||||
|
||||
// Fire show/hide event.
|
||||
nsRefPtr<AccEvent> event;
|
||||
if (aIsInsert)
|
||||
event = new AccShowEvent(accessible, node);
|
||||
else
|
||||
event = new AccHideEvent(accessible, node);
|
||||
|
||||
if (event)
|
||||
FireDelayedAccessibleEvent(event);
|
||||
|
||||
if (aIsInsert) {
|
||||
PRUint32 ariaRole = accessible->ARIARole();
|
||||
if (ariaRole == nsIAccessibleRole::ROLE_MENUPOPUP) {
|
||||
// Fire EVENT_MENUPOPUP_START if ARIA menu appears.
|
||||
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_START,
|
||||
node, AccEvent::eRemoveDupes);
|
||||
|
||||
} else if (ariaRole == nsIAccessibleRole::ROLE_ALERT) {
|
||||
// Fire EVENT_ALERT if ARIA alert appears.
|
||||
updateFlags = eAlertAccessible;
|
||||
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_ALERT, node,
|
||||
AccEvent::eRemoveDupes);
|
||||
}
|
||||
|
||||
// If focused node has been shown then it means its frame was recreated
|
||||
// while it's focused. Fire focus event on new focused accessible. If
|
||||
// the queue contains focus event for this node then it's suppressed by
|
||||
// this one.
|
||||
if (node == gLastFocusedNode) {
|
||||
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_FOCUS,
|
||||
node, AccEvent::eCoalesceFromSameDocument);
|
||||
}
|
||||
} else {
|
||||
// Update the tree for content removal.
|
||||
// The accessible parent may differ from container accessible if
|
||||
// the parent doesn't have own DOM node like list accessible for HTML
|
||||
// selects.
|
||||
nsAccessible* parent = accessible->GetParent();
|
||||
NS_ASSERTION(parent, "No accessible parent?!");
|
||||
if (parent)
|
||||
parent->RemoveChild(accessible);
|
||||
|
||||
UncacheChildrenInSubtree(accessible);
|
||||
}
|
||||
UncacheChildrenInSubtree(aChild);
|
||||
}
|
||||
|
||||
return updateFlags;
|
||||
|
@ -443,7 +443,7 @@ protected:
|
||||
* Update the accessible tree for content insertion or removal.
|
||||
*/
|
||||
void UpdateTree(nsAccessible* aContainer, nsIContent* aChildNode,
|
||||
PRBool aIsInsert);
|
||||
bool aIsInsert);
|
||||
|
||||
/**
|
||||
* Helper for UpdateTree() method. Go down to DOM subtree and updates
|
||||
@ -455,9 +455,7 @@ protected:
|
||||
eAlertAccessible = 2
|
||||
};
|
||||
|
||||
PRUint32 UpdateTreeInternal(nsIContent* aStartNode,
|
||||
nsIContent* aEndNode,
|
||||
PRBool aIsInsert);
|
||||
PRUint32 UpdateTreeInternal(nsAccessible* aChild, bool aIsInsert);
|
||||
|
||||
/**
|
||||
* Create accessible tree.
|
||||
|
@ -2212,7 +2212,6 @@ nsHyperTextAccessible::GetChildOffset(PRUint32 aChildIndex,
|
||||
PRUint32 lastOffset = mOffsets.IsEmpty() ?
|
||||
0 : mOffsets[mOffsets.Length() - 1];
|
||||
|
||||
EnsureChildren();
|
||||
while (mOffsets.Length() < aChildIndex) {
|
||||
nsAccessible* child = mChildren[mOffsets.Length()];
|
||||
lastOffset += nsAccUtils::TextLength(child);
|
||||
|
@ -69,6 +69,7 @@ _TEST_FILES =\
|
||||
test_focusdoc.html \
|
||||
test_menu.xul \
|
||||
test_mutation.html \
|
||||
test_mutation.xhtml \
|
||||
test_scroll.xul \
|
||||
test_selection.html \
|
||||
test_statechange.html \
|
||||
|
99
accessible/tests/mochitest/events/test_mutation.xhtml
Normal file
99
accessible/tests/mochitest/events/test_mutation.xhtml
Normal file
@ -0,0 +1,99 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<head>
|
||||
<title>Accessible mutation events testing</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
|
||||
<bindings xmlns="http://www.mozilla.org/xbl" >
|
||||
<binding id="button">
|
||||
<content>
|
||||
<button xmlns="http://www.w3.org/1999/xhtml">a button</button>
|
||||
</content>
|
||||
</binding>
|
||||
</bindings>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../events.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
|
||||
/**
|
||||
* Insert a not accessible bound element containing an accessible element
|
||||
* in anonymous content.
|
||||
*/
|
||||
function insertBinding(aContainerID)
|
||||
{
|
||||
this.containerNode = getNode(aContainerID);
|
||||
|
||||
function getButtonFromBinding(aNode)
|
||||
{
|
||||
try { return document.getAnonymousNodes(aNode.firstChild)[0]; }
|
||||
catch (e) { return null; }
|
||||
}
|
||||
|
||||
this.eventSeq = [
|
||||
new invokerChecker(EVENT_SHOW, getButtonFromBinding, this.containerNode),
|
||||
new invokerChecker(EVENT_REORDER, this.containerNode)
|
||||
];
|
||||
|
||||
this.invoke = function insertBinding_invoke()
|
||||
{
|
||||
var span = document.createElement("span");
|
||||
span.setAttribute("style", "-moz-binding:url(#button)");
|
||||
this.containerNode.appendChild(span);
|
||||
}
|
||||
|
||||
this.getID = function insertBinding_getID()
|
||||
{
|
||||
return "insert button binding";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do tests.
|
||||
*/
|
||||
var gQueue = null;
|
||||
//gA11yEventDumpID = "eventdump"; // debug stuff
|
||||
//gA11yEventDumpToConsole = true;
|
||||
|
||||
function doTests()
|
||||
{
|
||||
gQueue = new eventQueue();
|
||||
|
||||
gQueue.push(new insertBinding("testContainer"));
|
||||
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTests);
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=646369"
|
||||
title="UpdateTree should rely on accessible tree walker rather than DOM tree traversal">
|
||||
Mozilla Bug 646369</a>
|
||||
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
<div id="eventdump"></div>
|
||||
|
||||
<div id="testContainer"></div>
|
||||
</body>
|
||||
</html>
|
@ -3,7 +3,7 @@
|
||||
-moz-padding-end: 0;
|
||||
padding-bottom: 10px;
|
||||
-moz-padding-start: 0;
|
||||
width: 600px;
|
||||
width: 620px;
|
||||
}
|
||||
|
||||
#clientBox {
|
||||
@ -58,10 +58,6 @@
|
||||
-moz-padding-start: 0;
|
||||
}
|
||||
|
||||
#updateBox {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
#updateButton,
|
||||
#updateDeck > hbox > label {
|
||||
-moz-margin-start: 0;
|
||||
@ -99,3 +95,36 @@
|
||||
color: #999999;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#currentChannel {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#channelSelector {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#channelMenulist {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.channel-description {
|
||||
margin: 10px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#detailsBox,
|
||||
#channelSelector,
|
||||
.channel-description {
|
||||
-moz-transition: opacity 250ms;
|
||||
}
|
||||
|
||||
#contentDeck:not([selectedIndex="0"]) > #detailsBox,
|
||||
#contentDeck:not([selectedIndex="1"]) > #channelSelector,
|
||||
#channelDescriptionDeck:not([selectedIndex="0"]) > #releaseDescription,
|
||||
#channelDescriptionDeck:not([selectedIndex="1"]) > #betaDescription,
|
||||
#channelDescriptionDeck:not([selectedIndex="2"]) > #auroraDescription {
|
||||
opacity: 0;
|
||||
}
|
||||
|
@ -88,6 +88,8 @@ function init(aEvent)
|
||||
gAppUpdater = new appUpdater();
|
||||
#endif
|
||||
|
||||
gChannelSelector.init();
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
// it may not be sized at this point, and we need its width to calculate its position
|
||||
window.sizeToContent();
|
||||
@ -572,3 +574,68 @@ appUpdater.prototype =
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
var gChannelSelector = {
|
||||
validChannels: { release: 1, beta: 1, aurora: 1 },
|
||||
|
||||
init: function() {
|
||||
try {
|
||||
this.channelValue = Services.prefs.getCharPref("app.update.desiredChannel");
|
||||
} catch (e) {
|
||||
let defaults = Services.prefs.getDefaultBranch("");
|
||||
this.channelValue = defaults.getCharPref("app.update.channel");
|
||||
}
|
||||
|
||||
// Only show channel selector UI on valid update channels.
|
||||
if (this.channelValue in this.validChannels) {
|
||||
document.getElementById("currentChannelText").hidden = false;
|
||||
this.setChannelLabel(this.channelValue);
|
||||
this.setChannelMenuitem(this.channelValue);
|
||||
}
|
||||
},
|
||||
|
||||
selectChannel: function(aSelectedItem) {
|
||||
document.getElementById("channelDescriptionDeck").selectedPanel =
|
||||
document.getElementById(aSelectedItem.value + "Description");
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
this.setChannelMenuitem(this.channelValue);
|
||||
this.hide();
|
||||
},
|
||||
|
||||
apply: function() {
|
||||
this.channelValue = document.getElementById("channelMenulist").selectedItem.value;
|
||||
this.setChannelLabel(this.channelValue);
|
||||
|
||||
// Change app update channel.
|
||||
Services.prefs.setCharPref("app.update.desiredChannel", this.channelValue);
|
||||
|
||||
// App updater will look at app.update.desiredChannel for new channel value
|
||||
// and will clear it when the update is complete.
|
||||
gAppUpdater.isChecking = true;
|
||||
gAppUpdater.checker.checkForUpdates(gAppUpdater.updateCheckListener, true);
|
||||
|
||||
this.hide();
|
||||
},
|
||||
|
||||
show: function() {
|
||||
document.getElementById("contentDeck").selectedPanel =
|
||||
document.getElementById("channelSelector");
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
document.getElementById("contentDeck").selectedPanel =
|
||||
document.getElementById("detailsBox");
|
||||
},
|
||||
|
||||
setChannelLabel: function(aValue) {
|
||||
let channelLabel = document.getElementById("currentChannel");
|
||||
channelLabel.value = document.getElementById(aValue + "Menuitem").label;
|
||||
},
|
||||
|
||||
setChannelMenuitem: function(aValue) {
|
||||
document.getElementById("channelMenulist").selectedItem =
|
||||
document.getElementById(aValue + "Menuitem");
|
||||
}
|
||||
}
|
||||
|
@ -77,44 +77,81 @@
|
||||
#expand <label id="version" value="__MOZ_APP_VERSION__"/>
|
||||
<label id="distribution" class="text-blurb"/>
|
||||
<label id="distributionId" class="text-blurb"/>
|
||||
<vbox id="updateBox">
|
||||
|
||||
<!-- Make sure the selectedIndex attribute is always set so that the CSS
|
||||
selectors for transitions work -->
|
||||
<deck id="contentDeck" selectedIndex="0">
|
||||
<vbox id="detailsBox">
|
||||
<vbox id="updateBox">
|
||||
#ifdef MOZ_UPDATER
|
||||
<deck id="updateDeck" orient="vertical">
|
||||
<hbox id="updateButtonBox" align="center">
|
||||
<button id="updateButton" align="start"
|
||||
oncommand="gAppUpdater.buttonOnCommand();"/>
|
||||
<spacer flex="1"/>
|
||||
</hbox>
|
||||
<hbox id="checkingForUpdates" align="center">
|
||||
<image class="update-throbber"/><label>&update.checkingForUpdates;</label>
|
||||
</hbox>
|
||||
<hbox id="checkingAddonCompat" align="center">
|
||||
<image class="update-throbber"/><label>&update.checkingAddonCompat;</label>
|
||||
</hbox>
|
||||
<hbox id="downloading" align="center">
|
||||
<image class="update-throbber"/><label>&update.downloading.start;</label><label id="downloadStatus"/><label>&update.downloading.end;</label>
|
||||
</hbox>
|
||||
<hbox id="downloadFailed" align="center">
|
||||
<label>&update.failed.start;</label><label id="failedLink" class="text-link">&update.failed.linkText;</label><label>&update.failed.end;</label>
|
||||
</hbox>
|
||||
<hbox id="adminDisabled" align="center">
|
||||
<label>&update.adminDisabled;</label>
|
||||
</hbox>
|
||||
<hbox id="noUpdatesFound" align="center">
|
||||
<label>&update.noUpdatesFound;</label>
|
||||
</hbox>
|
||||
<hbox id="manualUpdate" align="center">
|
||||
<label>&update.manual.start;</label><label id="manualLink" class="text-link"/><label>&update.manual.end;</label>
|
||||
</hbox>
|
||||
</deck>
|
||||
<deck id="updateDeck" orient="vertical">
|
||||
<hbox id="updateButtonBox" align="center">
|
||||
<button id="updateButton" align="start"
|
||||
oncommand="gAppUpdater.buttonOnCommand();"/>
|
||||
<spacer flex="1"/>
|
||||
</hbox>
|
||||
<hbox id="checkingForUpdates" align="center">
|
||||
<image class="update-throbber"/><label>&update.checkingForUpdates;</label>
|
||||
</hbox>
|
||||
<hbox id="checkingAddonCompat" align="center">
|
||||
<image class="update-throbber"/><label>&update.checkingAddonCompat;</label>
|
||||
</hbox>
|
||||
<hbox id="downloading" align="center">
|
||||
<image class="update-throbber"/><label>&update.downloading.start;</label><label id="downloadStatus"/><label>&update.downloading.end;</label>
|
||||
</hbox>
|
||||
<hbox id="downloadFailed" align="center">
|
||||
<label>&update.failed.start;</label><label id="failedLink" class="text-link">&update.failed.linkText;</label><label>&update.failed.end;</label>
|
||||
</hbox>
|
||||
<hbox id="adminDisabled" align="center">
|
||||
<label>&update.adminDisabled;</label>
|
||||
</hbox>
|
||||
<hbox id="noUpdatesFound" align="center">
|
||||
<label>&update.noUpdatesFound;</label>
|
||||
</hbox>
|
||||
<hbox id="manualUpdate" align="center">
|
||||
<label>&update.manual.start;</label><label id="manualLink" class="text-link"/><label>&update.manual.end;</label>
|
||||
</hbox>
|
||||
</deck>
|
||||
#endif
|
||||
</vbox>
|
||||
<description class="text-blurb">
|
||||
&community.start2;<label class="text-link" href="http://www.mozilla.org/">&community.mozillaLink;</label>&community.middle2;<label class="text-link" href="about:credits">&community.creditsLink;</label>&community.end2;
|
||||
</description>
|
||||
<description class="text-blurb">
|
||||
&contribute.start;<label class="text-link" href="http://www.mozilla.org/contribute/">&contribute.getInvolvedLink;</label>&contribute.end;
|
||||
</description>
|
||||
</vbox>
|
||||
|
||||
<description class="text-blurb" id="currentChannelText" hidden="true">
|
||||
&channel.description.start;<label id="currentChannel"/>&channel.description.end;<label id="channelChangeLink" class="text-link" onclick="gChannelSelector.show();">&channel.change;</label>
|
||||
</description>
|
||||
<description class="text-blurb">
|
||||
&community.start2;<label class="text-link" href="http://www.mozilla.org/">&community.mozillaLink;</label>&community.middle2;<label class="text-link" href="about:credits">&community.creditsLink;</label>&community.end2;
|
||||
</description>
|
||||
<description class="text-blurb">
|
||||
&contribute.start;<label class="text-link" href="http://www.mozilla.org/contribute/">&contribute.getInvolvedLink;</label>&contribute.end;
|
||||
</description>
|
||||
</vbox>
|
||||
|
||||
<vbox id="channelSelector">
|
||||
<hbox pack="center" align="center">
|
||||
<label>&channel.selector.start;</label>
|
||||
<menulist id="channelMenulist" onselect="gChannelSelector.selectChannel(this.selectedItem);">
|
||||
<menupopup>
|
||||
<menuitem id="releaseMenuitem" label="Release" value="release"/>
|
||||
<menuitem id="betaMenuitem" label="Beta" value="beta"/>
|
||||
<menuseparator/>
|
||||
<menuitem id="auroraMenuitem" label="Aurora" value="aurora"/>
|
||||
</menupopup>
|
||||
</menulist>
|
||||
<label>&channel.selector.end;</label>
|
||||
</hbox>
|
||||
|
||||
<deck id="channelDescriptionDeck" selectedIndex="0">
|
||||
<description id="releaseDescription" class="channel-description">&channel.release.description;</description>
|
||||
<description id="betaDescription" class="channel-description">&channel.beta.description;</description>
|
||||
<description id="auroraDescription" class="channel-description">&channel.aurora.description;</description>
|
||||
</deck>
|
||||
|
||||
<hbox id="channelSelectorButtons" pack="center">
|
||||
<button oncommand="gChannelSelector.apply();" label="&channel.selector.applyButton;"/>
|
||||
<button oncommand="gChannelSelector.cancel();" label="&channel.selector.cancelButton;"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</deck>
|
||||
</vbox>
|
||||
</hbox>
|
||||
<vbox id="bottomBox">
|
||||
|
@ -320,8 +320,10 @@ let TabView = {
|
||||
// ----------
|
||||
// On move to group pop showing.
|
||||
moveToGroupPopupShowing: function TabView_moveToGroupPopupShowing(event) {
|
||||
// there are hidden tabs so initialize the iframe and update the context menu
|
||||
if ((gBrowser.tabs.length - gBrowser.visibleTabs.length) > 0)
|
||||
// Update the context menu only if Panorama was already initialized or if
|
||||
// there are hidden tabs.
|
||||
let numHiddenTabs = gBrowser.tabs.length - gBrowser.visibleTabs.length;
|
||||
if (this._window || numHiddenTabs > 0)
|
||||
this.updateContextMenu(TabContextMenu.contextTab, event.target);
|
||||
},
|
||||
|
||||
|
@ -691,6 +691,7 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
|
||||
});
|
||||
|
||||
this.droppable(false);
|
||||
this.removeTrenches();
|
||||
this._createUndoButton();
|
||||
} else
|
||||
this.close();
|
||||
@ -740,6 +741,7 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
|
||||
this.$undoContainer.remove();
|
||||
this.$undoContainer = null;
|
||||
this.droppable(true);
|
||||
this.setTrenches(this.bounds);
|
||||
|
||||
GroupItems.setActiveGroupItem(this);
|
||||
if (this._activeTab)
|
||||
@ -1237,8 +1239,8 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
|
||||
count: count || this._children.length,
|
||||
hideTitle: false
|
||||
};
|
||||
let arrObj = Items.arrange(null, bb, options);
|
||||
|
||||
let arrObj = Items.arrange(this._children, bb, options);
|
||||
|
||||
let shouldStack = arrObj.childWidth < TabItems.minTabWidth * 1.35;
|
||||
this._columns = shouldStack ? null : arrObj.columns;
|
||||
|
||||
|
@ -914,7 +914,6 @@ let Items = {
|
||||
// width of children and the columns.
|
||||
// count - overrides the item count for layout purposes;
|
||||
// default: the actual item count
|
||||
// padding - pixels between each item
|
||||
// columns - (int) a preset number of columns to use
|
||||
// dropPos - a <Point> which should have a one-tab space left open, used
|
||||
// when a tab is dragged over.
|
||||
|
@ -229,16 +229,6 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
|
||||
this.tabCanvas.paint();
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: _getFontSizeFromWidth
|
||||
// Private method that returns the fontsize to use given the tab's width
|
||||
_getFontSizeFromWidth: function TabItem__getFontSizeFromWidth(width) {
|
||||
let widthRange = new Range(0,TabItems.tabWidth);
|
||||
let proportion = widthRange.proportion(width-TabItems.tabItemPadding.x, true);
|
||||
// proportion is in [0,1]
|
||||
return TabItems.fontSizeRange.scale(proportion);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: unforceCanvasSize
|
||||
// Stops holding the thumbnail resolution; allows it to shift to the
|
||||
@ -373,6 +363,19 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
|
||||
(!GroupItems.getActiveGroupItem() && !self.tab.hidden))
|
||||
GroupItems.setActiveGroupItem(self.parent);
|
||||
}
|
||||
} else {
|
||||
// When duplicating a non-blank orphaned tab, create a group including both of them.
|
||||
// This prevents overlaid tabs in Tab View (only one tab appears to be there).
|
||||
// In addition, as only one active orphaned tab is shown when Tab View is hidden
|
||||
// and there are two tabs shown after the duplication, it also prevents
|
||||
// the inactive tab to suddenly disappear when toggling Tab View twice.
|
||||
//
|
||||
// Fixes:
|
||||
// Bug 645653 - Middle-click on reload button to duplicate orphan tabs does not create a group
|
||||
// Bug 643119 - Ctrl+Drag to duplicate does not work for orphaned tabs
|
||||
// ... (and any other way of duplicating a non-blank orphaned tab).
|
||||
if (GroupItems.getActiveGroupItem() == null)
|
||||
GroupItems.newTab(self, {immediately: true});
|
||||
}
|
||||
} else {
|
||||
// create tab by double click is handled in UI_init().
|
||||
@ -440,16 +443,14 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
|
||||
|
||||
if (rect.width != this.bounds.width || options.force) {
|
||||
css.width = rect.width - TabItems.tabItemPadding.x;
|
||||
css.fontSize = this._getFontSizeFromWidth(rect.width);
|
||||
css.fontSize = TabItems.getFontSizeFromWidth(rect.width);
|
||||
css.fontSize += 'px';
|
||||
}
|
||||
|
||||
if (rect.height != this.bounds.height || options.force) {
|
||||
css.height = rect.height - TabItems.tabItemPadding.y;
|
||||
if (!this.isStacked)
|
||||
css.height = rect.height - TabItems.tabItemPadding.y -
|
||||
TabItems.fontSizeRange.max;
|
||||
else
|
||||
css.height = rect.height - TabItems.tabItemPadding.y;
|
||||
css.height -= TabItems.fontSizeRange.max;
|
||||
}
|
||||
|
||||
if (Utils.isEmptyObject(css))
|
||||
@ -1280,57 +1281,59 @@ let TabItems = {
|
||||
|
||||
return sane;
|
||||
},
|
||||
|
||||
|
||||
// ----------
|
||||
// Function: getFontSizeFromWidth
|
||||
// Private method that returns the fontsize to use given the tab's width
|
||||
getFontSizeFromWidth: function TabItem_getFontSizeFromWidth(width) {
|
||||
let widthRange = new Range(0, TabItems.tabWidth);
|
||||
let proportion = widthRange.proportion(width - TabItems.tabItemPadding.x, true);
|
||||
// proportion is in [0,1]
|
||||
return TabItems.fontSizeRange.scale(proportion);
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: _getWidthForHeight
|
||||
// Private method that returns the tabitem width given a height.
|
||||
// Set options.hideTitle=true to measure without a title.
|
||||
// Default is to measure with a title.
|
||||
_getWidthForHeight: function TabItems__getWidthForHeight(height, options) {
|
||||
let titleSize = (options !== undefined && options.hideTitle === true) ?
|
||||
0 : TabItems.fontSizeRange.max;
|
||||
return Math.max(0, Math.max(TabItems.minTabHeight, height - titleSize)) *
|
||||
TabItems.invTabAspect;
|
||||
_getWidthForHeight: function TabItems__getWidthForHeight(height) {
|
||||
return height * TabItems.invTabAspect;
|
||||
},
|
||||
|
||||
// ----------
|
||||
// Function: _getHeightForWidth
|
||||
// Private method that returns the tabitem height given a width.
|
||||
// Set options.hideTitle=false to measure without a title.
|
||||
// Default is to measure with a title.
|
||||
_getHeightForWidth: function TabItems__getHeightForWidth(width, options) {
|
||||
let titleSize = (options !== undefined && options.hideTitle === true) ?
|
||||
0 : TabItems.fontSizeRange.max;
|
||||
return Math.max(0, Math.max(TabItems.minTabWidth,width)) *
|
||||
TabItems.tabAspect + titleSize;
|
||||
_getHeightForWidth: function TabItems__getHeightForWidth(width) {
|
||||
return width * TabItems.tabAspect;
|
||||
},
|
||||
|
||||
|
||||
// ----------
|
||||
// Function: calcValidSize
|
||||
// Pass in a desired size, and receive a size based on proper title
|
||||
// size and aspect ratio.
|
||||
calcValidSize: function TabItems_calcValidSize(size, options) {
|
||||
Utils.assert(Utils.isPoint(size), 'input is a Point');
|
||||
let retSize = new Point(0,0);
|
||||
if (size.x==-1) {
|
||||
retSize.x = this._getWidthForHeight(size.y, options);
|
||||
retSize.y = size.y;
|
||||
} else if (size.y==-1) {
|
||||
retSize.x = size.x;
|
||||
retSize.y = this._getHeightForWidth(size.x, options);
|
||||
} else {
|
||||
let fitHeight = this._getHeightForWidth(size.x, options);
|
||||
let fitWidth = this._getWidthForHeight(size.y, options);
|
||||
|
||||
// Go with the smallest final dimension.
|
||||
if (fitWidth < size.x) {
|
||||
retSize.x = fitWidth;
|
||||
retSize.y = size.y;
|
||||
} else {
|
||||
retSize.x = size.x;
|
||||
retSize.y = fitHeight;
|
||||
}
|
||||
let width = Math.max(TabItems.minTabWidth, size.x);
|
||||
let showTitle = !options || !options.hideTitle;
|
||||
let titleSize = showTitle ? TabItems.fontSizeRange.max : 0;
|
||||
let height = Math.max(TabItems.minTabHeight, size.y - titleSize);
|
||||
let retSize = new Point(width, height);
|
||||
|
||||
if (size.x > -1)
|
||||
retSize.y = this._getHeightForWidth(width);
|
||||
if (size.y > -1)
|
||||
retSize.x = this._getWidthForHeight(height);
|
||||
|
||||
if (size.x > -1 && size.y > -1) {
|
||||
if (retSize.x < size.x)
|
||||
retSize.y = this._getHeightForWidth(retSize.x);
|
||||
else
|
||||
retSize.x = this._getWidthForHeight(retSize.y);
|
||||
}
|
||||
|
||||
if (showTitle)
|
||||
retSize.y += titleSize;
|
||||
|
||||
return retSize;
|
||||
}
|
||||
};
|
||||
|
@ -15,7 +15,7 @@
|
||||
*
|
||||
* The Original Code is sanitize dialog test code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Corp.
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
|
@ -15,7 +15,7 @@
|
||||
*
|
||||
* The Original Code is sanitize dialog test code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Corp.
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
|
@ -73,6 +73,7 @@ _BROWSER_FILES = \
|
||||
browser_tabview_bug597360.js \
|
||||
browser_tabview_bug597399.js \
|
||||
browser_tabview_bug597980.js \
|
||||
browser_tabview_bug598375.js \
|
||||
browser_tabview_bug598600.js \
|
||||
browser_tabview_bug599626.js \
|
||||
browser_tabview_bug600645.js \
|
||||
@ -105,6 +106,7 @@ _BROWSER_FILES = \
|
||||
browser_tabview_bug624953.js \
|
||||
browser_tabview_bug625269.js \
|
||||
browser_tabview_bug625424.js \
|
||||
browser_tabview_bug625666.js \
|
||||
browser_tabview_bug626368.js \
|
||||
browser_tabview_bug626525.js \
|
||||
browser_tabview_bug626791.js \
|
||||
@ -122,7 +124,10 @@ _BROWSER_FILES = \
|
||||
browser_tabview_bug634077.js \
|
||||
browser_tabview_bug634085.js \
|
||||
browser_tabview_bug634158.js \
|
||||
browser_tabview_bug634672.js \
|
||||
browser_tabview_bug635696.js \
|
||||
browser_tabview_bug641802.js \
|
||||
browser_tabview_bug645653.js \
|
||||
browser_tabview_dragdrop.js \
|
||||
browser_tabview_exit_button.js \
|
||||
browser_tabview_expander.js \
|
||||
|
@ -0,0 +1,19 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
newWindowWithTabView(function (win) {
|
||||
registerCleanupFunction(function () win.close());
|
||||
|
||||
let cw = win.TabView.getContentWindow();
|
||||
let groupItem = cw.GroupItems.groupItems[0];
|
||||
groupItem.setBounds(new cw.Rect(cw.innerWidth - 200, 0, 200, 200));
|
||||
|
||||
whenTabViewIsHidden(finish, win);
|
||||
|
||||
let button = cw.document.getElementById("exit-button");
|
||||
EventUtils.synthesizeMouseAtCenter(button, {}, cw);
|
||||
});
|
||||
}
|
@ -125,18 +125,20 @@ function test() {
|
||||
// make sure we don't freeze item size when removing an item from a stack
|
||||
let testRemoveWhileStacked = function () {
|
||||
let oldBounds = groupItem.getBounds();
|
||||
groupItem.setSize(150, 200, true);
|
||||
groupItem.setSize(250, 250, true);
|
||||
groupItem.setUserSize();
|
||||
|
||||
let originalBounds = groupItem.getChild(0).getBounds();
|
||||
ok(!groupItem._isStacked, 'testRemoveWhileStacked: group is not stacked');
|
||||
ok(!groupItem.isStacked(), 'testRemoveWhileStacked: group is not stacked');
|
||||
|
||||
// add a new tab to let the group stack
|
||||
win.gBrowser.loadOneTab('about:blank', {inBackground: true});
|
||||
ok(groupItem._isStacked, 'testRemoveWhileStacked: group is now stacked');
|
||||
// add new tabs to let the group stack
|
||||
while (!groupItem.isStacked())
|
||||
win.gBrowser.loadOneTab('about:blank', {inBackground: true});
|
||||
|
||||
afterAllTabsLoaded(function () {
|
||||
groupItem.getChild(0).close();
|
||||
ok(!groupItem.isStacked(), 'testRemoveWhileStacked: group is not stacked');
|
||||
|
||||
let bounds = groupItem.getChild(0).getBounds();
|
||||
ok(originalBounds.equals(bounds), 'testRemoveWhileStacked: tabs did not change their size');
|
||||
|
||||
@ -156,7 +158,7 @@ function test() {
|
||||
groupItem.setSize(100, 100, true);
|
||||
groupItem.setUserSize();
|
||||
|
||||
ok(groupItem._isStacked, 'testExpandedMode: group is stacked');
|
||||
ok(groupItem.isStacked(), 'testExpandedMode: group is stacked');
|
||||
|
||||
groupItem.addSubscriber(groupItem, 'expanded', function () {
|
||||
groupItem.removeSubscriber(groupItem, 'expanded');
|
||||
@ -173,7 +175,7 @@ function test() {
|
||||
let tabItem = groupItem.getChild(1);
|
||||
let bounds = tabItem.getBounds();
|
||||
|
||||
for (let i=0; i<3; i++)
|
||||
while (groupItem.getChildren().length > 2)
|
||||
groupItem.getChild(1).close();
|
||||
|
||||
ok(originalBounds.equals(groupItem.getChild(0).getBounds()), 'testExpandedMode: tabs did not change their size');
|
||||
|
@ -3,24 +3,11 @@
|
||||
|
||||
function test() {
|
||||
let cw;
|
||||
|
||||
let createGroupItem = function () {
|
||||
let bounds = new cw.Rect(20, 20, 400, 200);
|
||||
let groupItem = new cw.GroupItem([], {bounds: bounds, immediately: true});
|
||||
|
||||
let groupItemId = groupItem.id;
|
||||
registerCleanupFunction(function() {
|
||||
let groupItem = cw.GroupItems.groupItem(groupItemId);
|
||||
if (groupItem)
|
||||
groupItem.close();
|
||||
});
|
||||
|
||||
return groupItem;
|
||||
}
|
||||
let win;
|
||||
|
||||
let testFocusTitle = function () {
|
||||
let title = 'title';
|
||||
let groupItem = createGroupItem();
|
||||
let groupItem = cw.GroupItems.groupItems[0];
|
||||
groupItem.setTitle(title);
|
||||
|
||||
let target = groupItem.$titleShield[0];
|
||||
@ -34,14 +21,21 @@ function test() {
|
||||
is(input.selectionStart, title.length, 'caret is at the rightmost position and no text is selected');
|
||||
is(input.selectionEnd, title.length, 'caret is at the rightmost position and no text is selected');
|
||||
|
||||
groupItem.close();
|
||||
hideTabView(finish);
|
||||
win.close();
|
||||
finish();
|
||||
}
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
showTabView(function () {
|
||||
cw = TabView.getContentWindow();
|
||||
testFocusTitle();
|
||||
newWindowWithTabView(function (tvwin) {
|
||||
win = tvwin;
|
||||
|
||||
registerCleanupFunction(function () {
|
||||
if (!win.closed)
|
||||
win.close();
|
||||
});
|
||||
|
||||
cw = win.TabView.getContentWindow();
|
||||
SimpleTest.waitForFocus(testFocusTitle, cw);
|
||||
});
|
||||
}
|
||||
|
@ -4,36 +4,20 @@
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
let finishTest = function (groupItem) {
|
||||
groupItem.addSubscriber(groupItem, 'groupHidden', function () {
|
||||
groupItem.removeSubscriber(groupItem, 'groupHidden');
|
||||
groupItem.closeHidden();
|
||||
hideTabView(finish);
|
||||
});
|
||||
|
||||
groupItem.closeAll();
|
||||
}
|
||||
|
||||
showTabView(function () {
|
||||
let cw = TabView.getContentWindow();
|
||||
|
||||
let bounds = new cw.Rect(20, 20, 150, 200);
|
||||
let groupItem = new cw.GroupItem([], {bounds: bounds, immediately: true});
|
||||
cw.GroupItems.setActiveGroupItem(groupItem);
|
||||
|
||||
for (let i=0; i<4; i++)
|
||||
gBrowser.loadOneTab('about:blank', {inBackground: true});
|
||||
|
||||
ok(!groupItem._isStacked, 'groupItem is not stacked');
|
||||
let groupItem = createGroupItemWithBlankTabs(window, 200, 240, 20, 4)
|
||||
ok(!groupItem.isStacked(), 'groupItem is not stacked');
|
||||
cw.GroupItems.pauseArrange();
|
||||
|
||||
groupItem.setSize(150, 150);
|
||||
groupItem.setSize(100, 100, true);
|
||||
groupItem.setUserSize();
|
||||
ok(!groupItem._isStacked, 'groupItem is still not stacked');
|
||||
ok(!groupItem.isStacked(), 'groupItem is still not stacked');
|
||||
|
||||
cw.GroupItems.resumeArrange();
|
||||
ok(groupItem._isStacked, 'groupItem is now stacked');
|
||||
ok(groupItem.isStacked(), 'groupItem is now stacked');
|
||||
|
||||
finishTest(groupItem);
|
||||
closeGroupItem(groupItem, finish);
|
||||
});
|
||||
}
|
||||
|
@ -0,0 +1,49 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
let cw;
|
||||
|
||||
let getTabItemAspect = function (tabItem) {
|
||||
let bounds = cw.iQ('.thumb', tabItem.container).bounds();
|
||||
let padding = cw.TabItems.tabItemPadding;
|
||||
return (bounds.height + padding.y) / (bounds.width + padding.x);
|
||||
}
|
||||
|
||||
let getAspectRange = function () {
|
||||
let aspect = cw.TabItems.tabAspect;
|
||||
let variance = aspect / 100 * 1.5;
|
||||
return new cw.Range(aspect - variance, aspect + variance);
|
||||
}
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
newWindowWithTabView(function (win) {
|
||||
registerCleanupFunction(function () win.close());
|
||||
cw = win.TabView.getContentWindow();
|
||||
|
||||
// prepare orphan tab
|
||||
let tabItem = win.gBrowser.tabs[0]._tabViewTabItem;
|
||||
tabItem.parent.remove(tabItem, {immediately: true});
|
||||
tabItem.setBounds(new cw.Rect(20, 20, 200, 165), true);
|
||||
|
||||
let bounds = tabItem.getBounds();
|
||||
|
||||
// prepare group item
|
||||
let box = new cw.Rect(20, 300, 400, 200);
|
||||
let groupItem = new cw.GroupItem([], {bounds: box, immediately: true});
|
||||
|
||||
groupItem.setBounds(new cw.Rect(20, 100, 400, 200));
|
||||
groupItem.pushAway(true);
|
||||
|
||||
let newBounds = tabItem.getBounds();
|
||||
ok(newBounds.width < bounds.width, "The new width of item is smaller than the old one.");
|
||||
ok(newBounds.height < bounds.height, "The new height of item is smaller than the old one.");
|
||||
|
||||
let aspectRange = getAspectRange();
|
||||
let aspect = getTabItemAspect(tabItem);
|
||||
ok(aspectRange.contains(aspect), "orphaned tabItem's aspect is correct");
|
||||
|
||||
finish();
|
||||
});
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
let cw;
|
||||
let win;
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
newWindowWithTabView(function (tvwin) {
|
||||
win = tvwin;
|
||||
cw = win.TabView.getContentWindow();
|
||||
|
||||
registerCleanupFunction(function () {
|
||||
if (win && !win.closed)
|
||||
win.close();
|
||||
});
|
||||
|
||||
// fill the group item with some tabs
|
||||
for (let i = 0; i < 5; i++)
|
||||
win.gBrowser.loadOneTab("about:blank");
|
||||
|
||||
let groupItem = cw.GroupItems.groupItems[0];
|
||||
groupItem.setSize(400, 400, true);
|
||||
let range = new cw.Range(1, 400);
|
||||
|
||||
// determine the groupItem's largest possible stacked size
|
||||
while (range.extent > 1) {
|
||||
let pivot = Math.floor(range.extent / 2);
|
||||
groupItem.setSize(range.min + pivot, range.min + pivot, true);
|
||||
|
||||
if (groupItem.isStacked())
|
||||
range.min += pivot;
|
||||
else
|
||||
range.max -= pivot;
|
||||
}
|
||||
|
||||
// stack the group
|
||||
groupItem.setSize(range.min, range.min, true);
|
||||
ok(groupItem.isStacked(), "groupItem is stacked");
|
||||
|
||||
// one step back to un-stack the groupItem
|
||||
groupItem.setSize(range.max, range.max, true);
|
||||
ok(!groupItem.isStacked(), "groupItem is no longer stacked");
|
||||
|
||||
// check that close buttons are visible
|
||||
let tabItem = groupItem.getChild(0);
|
||||
isnot(tabItem.$close.css("display"), "none", "close button is visible");
|
||||
|
||||
finish();
|
||||
});
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
let openMoveToGroupPopup = function () {
|
||||
let tab = gBrowser.selectedTab;
|
||||
document.popupNode = tab;
|
||||
contextMenu.openPopup(tab, "end_after", 0, 0, true, false);
|
||||
tvMenuPopup.openPopup(tvMenu, "end_after", 0, 0, true, false);
|
||||
}
|
||||
|
||||
let hideMoveToGroupPopup = function () {
|
||||
tvMenuPopup.hidePopup();
|
||||
contextMenu.hidePopup();
|
||||
}
|
||||
|
||||
let assertValidPrerequisites = function (visible) {
|
||||
let cw = TabView.getContentWindow();
|
||||
is(cw.GroupItems.groupItems.length, 1, "there is one groupItem");
|
||||
is(gBrowser.tabs.length, 1, "there is one tab");
|
||||
is(TabView.isVisible(), visible, "tabview is visible");
|
||||
}
|
||||
|
||||
let tvMenu = document.getElementById("context_tabViewMenu");
|
||||
let contextMenu = document.getElementById("tabContextMenu");
|
||||
let tvMenuPopup = document.getElementById("context_tabViewMenuPopup");
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
registerCleanupFunction(function () {
|
||||
hideMoveToGroupPopup();
|
||||
hideTabView(function () {});
|
||||
|
||||
let groupItems = TabView.getContentWindow().GroupItems.groupItems;
|
||||
if (groupItems.length > 1)
|
||||
closeGroupItem(groupItems[0], function () {});
|
||||
});
|
||||
|
||||
showTabView(function () {
|
||||
assertValidPrerequisites(true);
|
||||
|
||||
hideTabView(function () {
|
||||
let groupItem = createGroupItemWithBlankTabs(window, 200, 200, 10, 1);
|
||||
groupItem.setTitle("group2");
|
||||
|
||||
gBrowser.selectedTab = gBrowser.tabs[0];
|
||||
|
||||
executeSoon(function () {
|
||||
openMoveToGroupPopup();
|
||||
is(tvMenuPopup.firstChild.getAttribute("label"), "group2", "menuItem is present");
|
||||
hideMoveToGroupPopup();
|
||||
|
||||
closeGroupItem(groupItem, function () {
|
||||
openMoveToGroupPopup();
|
||||
is(tvMenuPopup.firstChild.tagName, "menuseparator", "menuItem is not present");
|
||||
hideMoveToGroupPopup();
|
||||
|
||||
assertValidPrerequisites(false);
|
||||
finish();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* Orphans a non-blank tab, duplicates it and checks whether a new group is created with two tabs.
|
||||
* The original one should be the first tab of the new group.
|
||||
*
|
||||
* This prevents overlaid tabs in Tab View (only one tab appears to be there).
|
||||
* In addition, as only one active orphaned tab is shown when Tab View is hidden
|
||||
* and there are two tabs shown after the duplication, it also prevents
|
||||
* the inactive tab to suddenly disappear when toggling Tab View twice.
|
||||
*
|
||||
* Covers:
|
||||
* Bug 645653 - Middle-click on reload button to duplicate orphan tabs does not create a group
|
||||
* Bug 643119 - Ctrl+Drag to duplicate does not work for orphaned tabs
|
||||
* ... (and any other way of duplicating a non-blank orphaned tab).
|
||||
*
|
||||
* See tabitems.js::_reconnect() for the fix.
|
||||
*
|
||||
* Miguel Ojeda <miguel.ojeda.sandonis@gmail.com>
|
||||
*/
|
||||
|
||||
function loadedAboutMozilla(tab) {
|
||||
return tab.linkedBrowser.contentDocument.getElementById('moztext');
|
||||
}
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
showTabView(function() {
|
||||
ok(TabView.isVisible(), "Tab View is visible");
|
||||
|
||||
let contentWindow = TabView.getContentWindow();
|
||||
is(contentWindow.GroupItems.groupItems.length, 1, "There is one group item on startup.");
|
||||
|
||||
let originalGroupItem = contentWindow.GroupItems.groupItems[0];
|
||||
is(originalGroupItem.getChildren().length, 1, "There is one tab item in that group.");
|
||||
|
||||
let originalTabItem = originalGroupItem.getChild(0);
|
||||
ok(originalTabItem, "The tabitem has been found.");
|
||||
|
||||
// close the group => orphan the tab
|
||||
originalGroupItem.close();
|
||||
contentWindow.GroupItems.setActiveGroupItem(originalGroupItem);
|
||||
is(contentWindow.GroupItems.groupItems.length, 0, "There are not any groups now.");
|
||||
|
||||
ok(TabView.isVisible(), "Tab View is still shown.");
|
||||
|
||||
hideTabView(function() {
|
||||
ok(!TabView.isVisible(), "Tab View is not shown anymore.");
|
||||
|
||||
// load a non-blank page
|
||||
loadURI('about:mozilla');
|
||||
|
||||
afterAllTabsLoaded(function() {
|
||||
ok(loadedAboutMozilla(originalTabItem.tab), "The original tab loaded about:mozilla.");
|
||||
|
||||
// duplicate it
|
||||
duplicateTabIn(originalTabItem.tab, "tabshift");
|
||||
|
||||
afterAllTabsLoaded(function() {
|
||||
// check
|
||||
is(gBrowser.selectedTab, originalTabItem.tab, "The selected tab is the original one.");
|
||||
is(contentWindow.GroupItems.groupItems.length, 1, "There is one group item again.");
|
||||
let groupItem = contentWindow.GroupItems.groupItems[0];
|
||||
is(groupItem.getChildren().length, 2, "There are two tab items in that group.");
|
||||
is(originalTabItem, groupItem.getChild(0), "The first tab item in the group is the original one.");
|
||||
let otherTab = groupItem.getChild(1);
|
||||
ok(loadedAboutMozilla(otherTab.tab), "The other tab loaded about:mozilla.");
|
||||
|
||||
// clean up
|
||||
gBrowser.removeTab(otherTab.tab);
|
||||
is(contentWindow.GroupItems.groupItems.length, 1, "There is one group item after closing the second tab.");
|
||||
is(groupItem.getChildren().length, 1, "There is only one tab item after closing the second tab.");
|
||||
is(originalTabItem, groupItem.getChild(0), "The first tab item in the group is still the original one.");
|
||||
loadURI("about:blank");
|
||||
afterAllTabsLoaded(function() {
|
||||
finish();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ function onTabViewWindowLoaded(win) {
|
||||
// let's create a group small enough to get stacked
|
||||
let group = new contentWindow.GroupItem([], {
|
||||
immediately: true,
|
||||
bounds: {left: 20, top: 300, width: 300, height: 300}
|
||||
bounds: {left: 20, top: 300, width: 400, height: 400}
|
||||
});
|
||||
|
||||
let expander = contentWindow.iQ(group.container).find(".stackExpander");
|
||||
|
@ -106,7 +106,8 @@ function afterAllTabsLoaded(callback, win) {
|
||||
|
||||
for (let a = 0; a < win.gBrowser.tabs.length; a++) {
|
||||
let browser = win.gBrowser.tabs[a].linkedBrowser;
|
||||
if (browser.contentDocument.readyState != "complete") {
|
||||
if (browser.contentDocument.readyState != "complete" ||
|
||||
browser.webProgress.isLoadingDocument) {
|
||||
stillToLoad++;
|
||||
browser.addEventListener("load", onLoad, true);
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
#
|
||||
# The Original Code is Places test code.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Mozilla Corp.
|
||||
# The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2008
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
|
@ -15,7 +15,7 @@
|
||||
*
|
||||
* The Original Code is Places test code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Corp.
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
|
@ -14,7 +14,7 @@
|
||||
*
|
||||
* The Original Code is Places test code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Corp.
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
|
@ -14,7 +14,7 @@
|
||||
*
|
||||
* The Original Code is Places test code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Corp.
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
|
@ -14,7 +14,7 @@
|
||||
*
|
||||
* The Original Code is Places test code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Corp.
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
|
@ -13,7 +13,7 @@
|
||||
*
|
||||
* The Original Code is Places test code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Corp.
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
|
@ -15,7 +15,7 @@
|
||||
*
|
||||
* The Original Code is Places test code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Corp.
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
|
@ -15,7 +15,7 @@
|
||||
*
|
||||
* The Original Code is Places test code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Corp.
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
|
@ -14,7 +14,7 @@
|
||||
*
|
||||
* The Original Code is Places test code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Corp.
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
|
@ -15,7 +15,7 @@
|
||||
*
|
||||
* The Original Code is Places test code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Corp.
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
|
@ -15,7 +15,7 @@
|
||||
*
|
||||
* The Original Code is Places test code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Corp.
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
|
@ -15,7 +15,7 @@
|
||||
*
|
||||
* The Original Code is Places test code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Corp.
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
|
@ -15,7 +15,7 @@
|
||||
*
|
||||
* The Original Code is Places test code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Corp.
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
|
@ -13,7 +13,7 @@
|
||||
#
|
||||
# The Original Code is Places test code.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Mozilla Corp.
|
||||
# The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2009
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
|
@ -15,7 +15,7 @@
|
||||
-
|
||||
- The Original Code is the Places test code.
|
||||
-
|
||||
- The Initial Developer of the Original Code is Mozilla Corp.
|
||||
- The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
- Portions created by the Initial Developer are Copyright (C) 2009
|
||||
- the Initial Developer. All Rights Reserved.
|
||||
-
|
||||
|
@ -15,7 +15,7 @@
|
||||
-
|
||||
- The Original Code is the Places test code.
|
||||
-
|
||||
- The Initial Developer of the Original Code is Mozilla Corp.
|
||||
- The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
- Portions created by the Initial Developer are Copyright (C) 2009
|
||||
- the Initial Developer. All Rights Reserved.
|
||||
-
|
||||
|
@ -15,7 +15,7 @@
|
||||
-
|
||||
- The Original Code is the Places test code.
|
||||
-
|
||||
- The Initial Developer of the Original Code is Mozilla Corp.
|
||||
- The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
- Portions created by the Initial Developer are Copyright (C) 2009
|
||||
- the Initial Developer. All Rights Reserved.
|
||||
-
|
||||
|
@ -15,7 +15,7 @@
|
||||
-
|
||||
- The Original Code is the Places test code.
|
||||
-
|
||||
- The Initial Developer of the Original Code is Mozilla Corp.
|
||||
- The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
- Portions created by the Initial Developer are Copyright (C) 2009
|
||||
- the Initial Developer. All Rights Reserved.
|
||||
-
|
||||
|
@ -15,7 +15,7 @@
|
||||
*
|
||||
* The Original Code is Bug 384370 code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Corp.
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
|
@ -15,7 +15,7 @@
|
||||
*
|
||||
* The Original Code is mozilla.com code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Corp.
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2007
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
|
@ -15,7 +15,7 @@
|
||||
*
|
||||
* The Original Code is Places Unit Test code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Corp.
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
|
@ -15,7 +15,7 @@
|
||||
*
|
||||
* The Original Code is Bug 457441 code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Corp.
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
|
@ -15,7 +15,7 @@
|
||||
*
|
||||
* The Original Code is Places test code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Corp.
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
|
@ -15,7 +15,7 @@
|
||||
*
|
||||
* The Original Code is mozilla.com code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Corp.
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2007
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
|
@ -13,7 +13,7 @@
|
||||
*
|
||||
* The Original Code is Privacy PrefPane Test.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Corp.
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
|
@ -54,3 +54,27 @@
|
||||
example: Downloading update — 111 KB of 13 MB -->
|
||||
<!ENTITY update.downloading.start "Downloading update — ">
|
||||
<!ENTITY update.downloading.end "">
|
||||
|
||||
<!-- LOCALIZATION NOTE (channel.description.start,channel.description.end): channel.description.start and
|
||||
channel.description.end create one sentence, with the current channel label inserted in between.
|
||||
example: You are currently on the _Stable_ update channel. -->
|
||||
<!ENTITY channel.description.start "You are currently on the ">
|
||||
<!ENTITY channel.description.end " update channel. ">
|
||||
|
||||
<!ENTITY channel.change "Change">
|
||||
|
||||
<!ENTITY channel.release.description "Enjoy the tried and tested final release being used by hundreds of millions around the world. Stay in control of your online experience with super speed, easy customization and the latest Web technologies.">
|
||||
<!ENTITY channel.beta.description "Experience cutting edge features with more stability. Provide feedback to help refine and polish what will be in the final release.">
|
||||
<!ENTITY channel.aurora.description "Experience the newest innovations in an unstable environment that's not for the faint of heart. Provide feedback on features and performance to help determine what makes the final release.">
|
||||
|
||||
<!-- LOCALIZATION NOTE (channel.selector.start,channel.selector.end): channel.selector.start and
|
||||
channel.selector.end create one sentence, with a channel selection menulist instered in between.
|
||||
This is all in one line, so try to make the localized text short.
|
||||
example: Switch to the [Stable] update channel. -->
|
||||
<!ENTITY channel.selector.start "Switch to the">
|
||||
<!ENTITY channel.selector.end "update channel.">
|
||||
|
||||
<!-- LOCALIZATION NOTE (channel.selector.applyButton): This button applies the user's choice to switch
|
||||
to a new update channel and starts the application update process. -->
|
||||
<!ENTITY channel.selector.applyButton "Apply and Update">
|
||||
<!ENTITY channel.selector.cancelButton "Cancel">
|
||||
|
@ -76,8 +76,8 @@ enum nsLinkState {
|
||||
|
||||
// IID for the nsIContent interface
|
||||
#define NS_ICONTENT_IID \
|
||||
{ 0x5788c9eb, 0x646a, 0x4285, \
|
||||
{ 0xa2, 0x8c, 0xde, 0x0d, 0x43, 0x6b, 0x47, 0x72 } }
|
||||
{ 0x32b94ba0, 0x1ebc, 0x4dfc, \
|
||||
{ 0xba, 0x8c, 0x5f, 0x24, 0x2b, 0xcb, 0xaf, 0xce } }
|
||||
|
||||
/**
|
||||
* A node of content in a document's content model. This interface
|
||||
@ -800,7 +800,7 @@ public:
|
||||
* GetIDAttributeName(). This may be null if there is no ID.
|
||||
*/
|
||||
nsIAtom* GetID() const {
|
||||
if (HasFlag(NODE_HAS_ID)) {
|
||||
if (HasID()) {
|
||||
return DoGetID();
|
||||
}
|
||||
return nsnull;
|
||||
@ -960,7 +960,7 @@ public:
|
||||
protected:
|
||||
/**
|
||||
* Hook for implementing GetID. This is guaranteed to only be
|
||||
* called if the NODE_HAS_ID flag is set.
|
||||
* called if HasID() is true.
|
||||
*/
|
||||
virtual nsIAtom* DoGetID() const = 0;
|
||||
|
||||
|
@ -166,7 +166,7 @@ public:
|
||||
mIsBeingUsedAsImage(PR_FALSE),
|
||||
mPartID(0)
|
||||
{
|
||||
mParentPtrBits |= PARENT_BIT_INDOCUMENT;
|
||||
SetInDocument();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -78,22 +78,19 @@ class Element;
|
||||
} // namespace mozilla
|
||||
|
||||
enum {
|
||||
// This bit will be set if the node doesn't have nsSlots
|
||||
NODE_DOESNT_HAVE_SLOTS = 0x00000001U,
|
||||
|
||||
// This bit will be set if the node has a listener manager in the listener
|
||||
// manager hash
|
||||
NODE_HAS_LISTENERMANAGER = 0x00000002U,
|
||||
NODE_HAS_LISTENERMANAGER = 0x00000001U,
|
||||
|
||||
// Whether this node has had any properties set on it
|
||||
NODE_HAS_PROPERTIES = 0x00000004U,
|
||||
NODE_HAS_PROPERTIES = 0x00000002U,
|
||||
|
||||
// Whether this node is the root of an anonymous subtree. Note that this
|
||||
// need not be a native anonymous subtree. Any anonymous subtree, including
|
||||
// XBL-generated ones, will do. This flag is set-once: once a node has it,
|
||||
// it must not be removed.
|
||||
// NOTE: Should only be used on nsIContent nodes
|
||||
NODE_IS_ANONYMOUS = 0x00000008U,
|
||||
NODE_IS_ANONYMOUS = 0x00000004U,
|
||||
|
||||
// Whether the node has some ancestor, possibly itself, that is native
|
||||
// anonymous. This includes ancestors crossing XBL scopes, in cases when an
|
||||
@ -101,44 +98,39 @@ enum {
|
||||
// ancestor. This flag is set-once: once a node has it, it must not be
|
||||
// removed.
|
||||
// NOTE: Should only be used on nsIContent nodes
|
||||
NODE_IS_IN_ANONYMOUS_SUBTREE = 0x00000010U,
|
||||
NODE_IS_IN_ANONYMOUS_SUBTREE = 0x00000008U,
|
||||
|
||||
// Whether this node is the root of a native anonymous (from the perspective
|
||||
// of its parent) subtree. This flag is set-once: once a node has it, it
|
||||
// must not be removed.
|
||||
// NOTE: Should only be used on nsIContent nodes
|
||||
NODE_IS_NATIVE_ANONYMOUS_ROOT = 0x00000020U,
|
||||
NODE_IS_NATIVE_ANONYMOUS_ROOT = 0x00000010U,
|
||||
|
||||
// Forces the XBL code to treat this node as if it were
|
||||
// in the document and therefore should get bindings attached.
|
||||
NODE_FORCE_XBL_BINDINGS = 0x00000040U,
|
||||
NODE_FORCE_XBL_BINDINGS = 0x00000020U,
|
||||
|
||||
// Whether a binding manager may have a pointer to this
|
||||
NODE_MAY_BE_IN_BINDING_MNGR = 0x00000080U,
|
||||
NODE_MAY_BE_IN_BINDING_MNGR = 0x00000040U,
|
||||
|
||||
NODE_IS_EDITABLE = 0x00000100U,
|
||||
NODE_IS_EDITABLE = 0x00000080U,
|
||||
|
||||
// Set to true if the element has a non-empty id attribute. This can in rare
|
||||
// cases lie for nsXMLElement, such as when the node has been moved between
|
||||
// documents with different id mappings.
|
||||
NODE_HAS_ID = 0x00000200U,
|
||||
// For all Element nodes, NODE_MAY_HAVE_CLASS is guaranteed to be set if the
|
||||
// node in fact has a class, but may be set even if it doesn't.
|
||||
NODE_MAY_HAVE_CLASS = 0x00000400U,
|
||||
NODE_MAY_HAVE_STYLE = 0x00000800U,
|
||||
NODE_MAY_HAVE_CLASS = 0x00000100U,
|
||||
|
||||
NODE_IS_INSERTION_PARENT = 0x00001000U,
|
||||
NODE_IS_INSERTION_PARENT = 0x00000200U,
|
||||
|
||||
// Node has an :empty or :-moz-only-whitespace selector
|
||||
NODE_HAS_EMPTY_SELECTOR = 0x00002000U,
|
||||
NODE_HAS_EMPTY_SELECTOR = 0x00000400U,
|
||||
|
||||
// A child of the node has a selector such that any insertion,
|
||||
// removal, or appending of children requires restyling the parent.
|
||||
NODE_HAS_SLOW_SELECTOR = 0x00004000U,
|
||||
NODE_HAS_SLOW_SELECTOR = 0x00000800U,
|
||||
|
||||
// A child of the node has a :first-child, :-moz-first-node,
|
||||
// :only-child, :last-child or :-moz-last-node selector.
|
||||
NODE_HAS_EDGE_CHILD_SELECTOR = 0x00008000U,
|
||||
NODE_HAS_EDGE_CHILD_SELECTOR = 0x00001000U,
|
||||
|
||||
// A child of the node has a selector such that any insertion or
|
||||
// removal of children requires restyling later siblings of that
|
||||
@ -148,42 +140,33 @@ enum {
|
||||
// matching :empty due to a grandchild insertion or removal), the
|
||||
// child's later siblings must also be restyled.
|
||||
NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS
|
||||
= 0x00010000U,
|
||||
= 0x00002000U,
|
||||
|
||||
NODE_ALL_SELECTOR_FLAGS = NODE_HAS_EMPTY_SELECTOR |
|
||||
NODE_HAS_SLOW_SELECTOR |
|
||||
NODE_HAS_EDGE_CHILD_SELECTOR |
|
||||
NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS,
|
||||
|
||||
NODE_MAY_HAVE_CONTENT_EDITABLE_ATTR
|
||||
= 0x00020000U,
|
||||
|
||||
NODE_ATTACH_BINDING_ON_POSTCREATE
|
||||
= 0x00040000U,
|
||||
= 0x00004000U,
|
||||
|
||||
// This node needs to go through frame construction to get a frame (or
|
||||
// undisplayed entry).
|
||||
NODE_NEEDS_FRAME = 0x00080000U,
|
||||
NODE_NEEDS_FRAME = 0x00008000U,
|
||||
|
||||
// At least one descendant in the flattened tree has NODE_NEEDS_FRAME set.
|
||||
// This should be set on every node on the flattened tree path between the
|
||||
// node(s) with NODE_NEEDS_FRAME and the root content.
|
||||
NODE_DESCENDANTS_NEED_FRAMES = 0x00100000U,
|
||||
|
||||
// Set if the node is an element.
|
||||
NODE_IS_ELEMENT = 0x00200000U,
|
||||
|
||||
// Set if the node has the accesskey attribute set.
|
||||
NODE_HAS_ACCESSKEY = 0x00400000U,
|
||||
NODE_DESCENDANTS_NEED_FRAMES = 0x00010000U,
|
||||
|
||||
// Set if the node has the accesskey attribute set.
|
||||
NODE_HAS_NAME = 0x00800000U,
|
||||
NODE_HAS_ACCESSKEY = 0x00020000U,
|
||||
|
||||
// Two bits for the script-type ID. Not enough to represent all
|
||||
// nsIProgrammingLanguage values, but we don't care. In practice,
|
||||
// we can represent the ones we want, and we can fail the others at
|
||||
// runtime.
|
||||
NODE_SCRIPT_TYPE_OFFSET = 24,
|
||||
NODE_SCRIPT_TYPE_OFFSET = 18,
|
||||
|
||||
NODE_SCRIPT_TYPE_SIZE = 2,
|
||||
|
||||
@ -294,8 +277,8 @@ private:
|
||||
|
||||
// IID for the nsINode interface
|
||||
#define NS_INODE_IID \
|
||||
{ 0x2a8dc794, 0x9178, 0x400e, \
|
||||
{ 0x81, 0xff, 0x55, 0x30, 0x30, 0xb6, 0x74, 0x3b } }
|
||||
{ 0x4776aa9a, 0xa886, 0x40c9, \
|
||||
{ 0xae, 0x4c, 0x4d, 0x92, 0xe2, 0xf0, 0xd9, 0x61 } }
|
||||
|
||||
/**
|
||||
* An internal interface that abstracts some DOMNode-related parts that both
|
||||
@ -316,12 +299,13 @@ public:
|
||||
#ifdef MOZILLA_INTERNAL_API
|
||||
nsINode(already_AddRefed<nsINodeInfo> aNodeInfo)
|
||||
: mNodeInfo(aNodeInfo),
|
||||
mParentPtrBits(0),
|
||||
mFlagsOrSlots(NODE_DOESNT_HAVE_SLOTS),
|
||||
mParent(nsnull),
|
||||
mFlags(0),
|
||||
mBoolFlags(0),
|
||||
mNextSibling(nsnull),
|
||||
mPreviousSibling(nsnull),
|
||||
mFirstChild(nsnull),
|
||||
mNodeHasRenderingObservers(false)
|
||||
mSlots(nsnull)
|
||||
{
|
||||
}
|
||||
|
||||
@ -373,8 +357,8 @@ public:
|
||||
/**
|
||||
* Return whether the node is an Element node
|
||||
*/
|
||||
PRBool IsElement() const {
|
||||
return HasFlag(NODE_IS_ELEMENT);
|
||||
bool IsElement() const {
|
||||
return GetBoolFlag(NodeIsElement);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -433,9 +417,9 @@ public:
|
||||
*
|
||||
* @return whether this content is in a document tree
|
||||
*/
|
||||
PRBool IsInDoc() const
|
||||
bool IsInDoc() const
|
||||
{
|
||||
return mParentPtrBits & PARENT_BIT_INDOCUMENT;
|
||||
return GetBoolFlag(IsInDocument);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -705,12 +689,9 @@ public:
|
||||
* Get the parent nsIContent for this node.
|
||||
* @return the parent, or null if no parent or the parent is not an nsIContent
|
||||
*/
|
||||
nsIContent* GetParent() const
|
||||
{
|
||||
return NS_LIKELY(mParentPtrBits & PARENT_BIT_PARENT_IS_CONTENT) ?
|
||||
reinterpret_cast<nsIContent*>
|
||||
(mParentPtrBits & ~kParentBitMask) :
|
||||
nsnull;
|
||||
nsIContent* GetParent() const {
|
||||
return NS_LIKELY(GetBoolFlag(ParentIsContent)) ?
|
||||
reinterpret_cast<nsIContent*>(mParent) : nsnull;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -720,7 +701,7 @@ public:
|
||||
*/
|
||||
nsINode* GetNodeParent() const
|
||||
{
|
||||
return reinterpret_cast<nsINode*>(mParentPtrBits & ~kParentBitMask);
|
||||
return mParent;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -800,9 +781,8 @@ public:
|
||||
class nsSlots
|
||||
{
|
||||
public:
|
||||
nsSlots(PtrBits aFlags)
|
||||
: mFlags(aFlags),
|
||||
mChildNodes(nsnull),
|
||||
nsSlots()
|
||||
: mChildNodes(nsnull),
|
||||
mWeakReference(nsnull)
|
||||
{
|
||||
}
|
||||
@ -811,13 +791,6 @@ public:
|
||||
// putting a DestroySlots function on nsINode
|
||||
virtual ~nsSlots();
|
||||
|
||||
/**
|
||||
* Storage for flags for this node. These are the same flags as the
|
||||
* mFlagsOrSlots member, but these are used when the slots class
|
||||
* is allocated.
|
||||
*/
|
||||
PtrBits mFlags;
|
||||
|
||||
/**
|
||||
* A list of mutation observers
|
||||
*/
|
||||
@ -853,12 +826,12 @@ public:
|
||||
return !!(GetFlags() & aFlag);
|
||||
}
|
||||
|
||||
PtrBits GetFlags() const
|
||||
PRUint32 GetFlags() const
|
||||
{
|
||||
return NS_UNLIKELY(HasSlots()) ? FlagsAsSlots()->mFlags : mFlagsOrSlots;
|
||||
return mFlags;
|
||||
}
|
||||
|
||||
void SetFlags(PtrBits aFlagsToSet)
|
||||
void SetFlags(PRUint32 aFlagsToSet)
|
||||
{
|
||||
NS_ASSERTION(!(aFlagsToSet & (NODE_IS_ANONYMOUS |
|
||||
NODE_IS_NATIVE_ANONYMOUS_ROOT |
|
||||
@ -868,21 +841,17 @@ public:
|
||||
NODE_NEEDS_FRAME)) ||
|
||||
IsNodeOfType(eCONTENT),
|
||||
"Flag only permitted on nsIContent nodes");
|
||||
PtrBits* flags = HasSlots() ? &FlagsAsSlots()->mFlags :
|
||||
&mFlagsOrSlots;
|
||||
*flags |= aFlagsToSet;
|
||||
mFlags |= aFlagsToSet;
|
||||
}
|
||||
|
||||
void UnsetFlags(PtrBits aFlagsToUnset)
|
||||
void UnsetFlags(PRUint32 aFlagsToUnset)
|
||||
{
|
||||
NS_ASSERTION(!(aFlagsToUnset &
|
||||
(NODE_IS_ANONYMOUS |
|
||||
NODE_IS_IN_ANONYMOUS_SUBTREE |
|
||||
NODE_IS_NATIVE_ANONYMOUS_ROOT)),
|
||||
"Trying to unset write-only flags");
|
||||
PtrBits* flags = HasSlots() ? &FlagsAsSlots()->mFlags :
|
||||
&mFlagsOrSlots;
|
||||
*flags &= ~aFlagsToUnset;
|
||||
mFlags &= ~aFlagsToUnset;
|
||||
}
|
||||
|
||||
void SetEditableFlag(PRBool aEditable)
|
||||
@ -1148,10 +1117,80 @@ public:
|
||||
NS_NOTREACHED("How did we get here?");
|
||||
}
|
||||
|
||||
bool HasRenderingObservers() { return mNodeHasRenderingObservers; }
|
||||
void SetHasRenderingObservers(bool aValue)
|
||||
{ mNodeHasRenderingObservers = aValue; }
|
||||
/**
|
||||
* Boolean flags
|
||||
*/
|
||||
private:
|
||||
enum BooleanFlag {
|
||||
// Set if we're being used from -moz-element
|
||||
NodeHasRenderingObservers,
|
||||
// Set if our parent chain (including this node itself) terminates
|
||||
// in a document
|
||||
IsInDocument,
|
||||
// Set if mParent is an nsIContent
|
||||
ParentIsContent,
|
||||
// Set if this node is an Element
|
||||
NodeIsElement,
|
||||
// Set if the element has a non-empty id attribute. This can in rare
|
||||
// cases lie for nsXMLElement, such as when the node has been moved between
|
||||
// documents with different id mappings.
|
||||
ElementHasID,
|
||||
// Set if the element might have inline style.
|
||||
ElementMayHaveStyle,
|
||||
// Set if the element has a name attribute set.
|
||||
ElementHasName,
|
||||
// Set if the element might have a contenteditable attribute set.
|
||||
ElementMayHaveContentEditableAttr,
|
||||
// Guard value
|
||||
BooleanFlagCount
|
||||
};
|
||||
|
||||
void SetBoolFlag(BooleanFlag name, bool value) {
|
||||
PR_STATIC_ASSERT(BooleanFlagCount <= 8*sizeof(mBoolFlags));
|
||||
mBoolFlags = (mBoolFlags & ~(1 << name)) | (value << name);
|
||||
}
|
||||
|
||||
void SetBoolFlag(BooleanFlag name) {
|
||||
PR_STATIC_ASSERT(BooleanFlagCount <= 8*sizeof(mBoolFlags));
|
||||
mBoolFlags |= (1 << name);
|
||||
}
|
||||
|
||||
void ClearBoolFlag(BooleanFlag name) {
|
||||
PR_STATIC_ASSERT(BooleanFlagCount <= 8*sizeof(mBoolFlags));
|
||||
mBoolFlags &= ~(1 << name);
|
||||
}
|
||||
|
||||
bool GetBoolFlag(BooleanFlag name) const {
|
||||
PR_STATIC_ASSERT(BooleanFlagCount <= 8*sizeof(mBoolFlags));
|
||||
return mBoolFlags & (1 << name);
|
||||
}
|
||||
|
||||
public:
|
||||
bool HasRenderingObservers() const
|
||||
{ return GetBoolFlag(NodeHasRenderingObservers); }
|
||||
void SetHasRenderingObservers(bool aValue)
|
||||
{ SetBoolFlag(NodeHasRenderingObservers, aValue); }
|
||||
bool HasID() const { return GetBoolFlag(ElementHasID); }
|
||||
bool MayHaveStyle() const { return GetBoolFlag(ElementMayHaveStyle); }
|
||||
bool HasName() const { return GetBoolFlag(ElementHasName); }
|
||||
bool MayHaveContentEditableAttr() const
|
||||
{ return GetBoolFlag(ElementMayHaveContentEditableAttr); }
|
||||
|
||||
protected:
|
||||
void SetParentIsContent(bool aValue) { SetBoolFlag(ParentIsContent, aValue); }
|
||||
void SetInDocument() { SetBoolFlag(IsInDocument); }
|
||||
void ClearInDocument() { ClearBoolFlag(IsInDocument); }
|
||||
void SetIsElement() { SetBoolFlag(NodeIsElement); }
|
||||
void ClearIsElement() { ClearBoolFlag(NodeIsElement); }
|
||||
void SetHasID() { SetBoolFlag(ElementHasID); }
|
||||
void ClearHasID() { ClearBoolFlag(ElementHasID); }
|
||||
void SetMayHaveStyle() { SetBoolFlag(ElementMayHaveStyle); }
|
||||
void SetHasName() { SetBoolFlag(ElementHasName); }
|
||||
void ClearHasName() { ClearBoolFlag(ElementHasName); }
|
||||
void SetMayHaveContentEditableAttr()
|
||||
{ SetBoolFlag(ElementMayHaveContentEditableAttr); }
|
||||
|
||||
public:
|
||||
// Optimized way to get classinfo.
|
||||
virtual nsXPCClassInfo* GetClassInfo() = 0;
|
||||
protected:
|
||||
@ -1161,37 +1200,25 @@ protected:
|
||||
|
||||
PRBool HasSlots() const
|
||||
{
|
||||
return !(mFlagsOrSlots & NODE_DOESNT_HAVE_SLOTS);
|
||||
}
|
||||
|
||||
nsSlots* FlagsAsSlots() const
|
||||
{
|
||||
NS_ASSERTION(HasSlots(), "check HasSlots first");
|
||||
return reinterpret_cast<nsSlots*>(mFlagsOrSlots);
|
||||
return mSlots != nsnull;
|
||||
}
|
||||
|
||||
nsSlots* GetExistingSlots() const
|
||||
{
|
||||
return HasSlots() ? FlagsAsSlots() : nsnull;
|
||||
return mSlots;
|
||||
}
|
||||
|
||||
nsSlots* GetSlots()
|
||||
{
|
||||
if (HasSlots()) {
|
||||
return FlagsAsSlots();
|
||||
if (!HasSlots()) {
|
||||
mSlots = CreateSlots();
|
||||
}
|
||||
|
||||
nsSlots* newSlots = CreateSlots();
|
||||
if (newSlots) {
|
||||
mFlagsOrSlots = reinterpret_cast<PtrBits>(newSlots);
|
||||
}
|
||||
|
||||
return newSlots;
|
||||
return GetExistingSlots();
|
||||
}
|
||||
|
||||
nsTObserverArray<nsIMutationObserver*> *GetMutationObservers()
|
||||
{
|
||||
return HasSlots() ? &FlagsAsSlots()->mMutationObservers : nsnull;
|
||||
return HasSlots() ? &GetExistingSlots()->mMutationObservers : nsnull;
|
||||
}
|
||||
|
||||
PRBool IsEditableInternal() const;
|
||||
@ -1268,25 +1295,21 @@ protected:
|
||||
|
||||
nsCOMPtr<nsINodeInfo> mNodeInfo;
|
||||
|
||||
enum { PARENT_BIT_INDOCUMENT = 1 << 0, PARENT_BIT_PARENT_IS_CONTENT = 1 << 1 };
|
||||
enum { kParentBitMask = 0x3 };
|
||||
nsINode* mParent;
|
||||
|
||||
PtrBits mParentPtrBits;
|
||||
PRUint32 mFlags;
|
||||
|
||||
/**
|
||||
* Used for either storing flags for this node or a pointer to
|
||||
* this contents nsContentSlots. See the definition of the
|
||||
* NODE_* macros for the layout of the bits in this
|
||||
* member.
|
||||
*/
|
||||
PtrBits mFlagsOrSlots;
|
||||
private:
|
||||
// Boolean flags.
|
||||
PRUint32 mBoolFlags;
|
||||
|
||||
protected:
|
||||
nsIContent* mNextSibling;
|
||||
nsIContent* mPreviousSibling;
|
||||
nsIContent* mFirstChild;
|
||||
|
||||
// More flags
|
||||
bool mNodeHasRenderingObservers : 1;
|
||||
// Storage for more members that are usually not needed; allocated lazily.
|
||||
nsSlots* mSlots;
|
||||
};
|
||||
|
||||
|
||||
|
@ -187,7 +187,7 @@ NS_NewDocumentFragment(nsIDOMDocumentFragment** aInstancePtrResult,
|
||||
nsDocumentFragment::nsDocumentFragment(already_AddRefed<nsINodeInfo> aNodeInfo)
|
||||
: nsGenericElement(aNodeInfo)
|
||||
{
|
||||
UnsetFlags(NODE_IS_ELEMENT);
|
||||
ClearIsElement();
|
||||
}
|
||||
|
||||
nsDocumentFragment::~nsDocumentFragment()
|
||||
|
@ -523,19 +523,19 @@ nsGenericDOMDataNode::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
|
||||
// Set parent
|
||||
if (aParent) {
|
||||
mParentPtrBits =
|
||||
reinterpret_cast<PtrBits>(aParent) | PARENT_BIT_PARENT_IS_CONTENT;
|
||||
mParent = aParent;
|
||||
}
|
||||
else {
|
||||
mParentPtrBits = reinterpret_cast<PtrBits>(aDocument);
|
||||
mParent = aDocument;
|
||||
}
|
||||
SetParentIsContent(aParent);
|
||||
|
||||
// XXXbz sXBL/XBL2 issue!
|
||||
|
||||
// Set document
|
||||
if (aDocument) {
|
||||
// XXX See the comment in nsGenericElement::BindToTree
|
||||
mParentPtrBits |= PARENT_BIT_INDOCUMENT;
|
||||
SetInDocument();
|
||||
if (mText.IsBidi()) {
|
||||
aDocument->SetBidiEnabled();
|
||||
}
|
||||
@ -570,7 +570,11 @@ nsGenericDOMDataNode::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
|
||||
document->BindingManager()->RemovedFromDocument(this, document);
|
||||
}
|
||||
|
||||
mParentPtrBits = aNullParent ? 0 : mParentPtrBits & ~PARENT_BIT_INDOCUMENT;
|
||||
if (aNullParent) {
|
||||
mParent = nsnull;
|
||||
SetParentIsContent(false);
|
||||
}
|
||||
ClearInDocument();
|
||||
|
||||
nsDataSlots *slots = GetExistingDataSlots();
|
||||
if (slots) {
|
||||
@ -783,7 +787,7 @@ nsGenericDOMDataNode::IsLink(nsIURI** aURI) const
|
||||
nsINode::nsSlots*
|
||||
nsGenericDOMDataNode::CreateSlots()
|
||||
{
|
||||
return new nsDataSlots(mFlagsOrSlots);
|
||||
return new nsDataSlots();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
@ -301,8 +301,8 @@ protected:
|
||||
class nsDataSlots : public nsINode::nsSlots
|
||||
{
|
||||
public:
|
||||
nsDataSlots(PtrBits aFlags)
|
||||
: nsINode::nsSlots(aFlags),
|
||||
nsDataSlots()
|
||||
: nsINode::nsSlots(),
|
||||
mBindingParent(nsnull)
|
||||
{
|
||||
}
|
||||
|
@ -273,7 +273,7 @@ nsGenericElement::GetSystemEventGroup(nsIDOMEventGroup** aGroup)
|
||||
nsINode::nsSlots*
|
||||
nsINode::CreateSlots()
|
||||
{
|
||||
return new nsSlots(mFlagsOrSlots);
|
||||
return new nsSlots();
|
||||
}
|
||||
|
||||
PRBool
|
||||
@ -2138,8 +2138,8 @@ nsNodeSelectorTearoff::QuerySelectorAll(const nsAString& aSelector,
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
nsGenericElement::nsDOMSlots::nsDOMSlots(PtrBits aFlags)
|
||||
: nsINode::nsSlots(aFlags),
|
||||
nsGenericElement::nsDOMSlots::nsDOMSlots()
|
||||
: nsINode::nsSlots(),
|
||||
mBindingParent(nsnull)
|
||||
{
|
||||
}
|
||||
@ -2160,8 +2160,8 @@ nsGenericElement::nsGenericElement(already_AddRefed<nsINodeInfo> aNodeInfo)
|
||||
{
|
||||
// Set the default scriptID to JS - but skip SetScriptTypeID as it
|
||||
// does extra work we know isn't necessary here...
|
||||
SetFlags(NODE_IS_ELEMENT |
|
||||
(nsIProgrammingLanguage::JAVASCRIPT << NODE_SCRIPT_TYPE_OFFSET));
|
||||
SetFlags((nsIProgrammingLanguage::JAVASCRIPT << NODE_SCRIPT_TYPE_OFFSET));
|
||||
SetIsElement();
|
||||
}
|
||||
|
||||
nsGenericElement::~nsGenericElement()
|
||||
@ -2943,15 +2943,16 @@ nsGenericElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
|
||||
// Now set the parent and set the "Force attach xbl" flag if needed.
|
||||
if (aParent) {
|
||||
mParentPtrBits = reinterpret_cast<PtrBits>(aParent) | PARENT_BIT_PARENT_IS_CONTENT;
|
||||
mParent = aParent;
|
||||
|
||||
if (aParent->HasFlag(NODE_FORCE_XBL_BINDINGS)) {
|
||||
SetFlags(NODE_FORCE_XBL_BINDINGS);
|
||||
}
|
||||
}
|
||||
else {
|
||||
mParentPtrBits = reinterpret_cast<PtrBits>(aDocument);
|
||||
mParent = aDocument;
|
||||
}
|
||||
SetParentIsContent(aParent);
|
||||
|
||||
// XXXbz sXBL/XBL2 issue!
|
||||
|
||||
@ -2967,7 +2968,7 @@ nsGenericElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
// aDocument);
|
||||
|
||||
// Being added to a document.
|
||||
mParentPtrBits |= PARENT_BIT_INDOCUMENT;
|
||||
SetInDocument();
|
||||
|
||||
// Unset this flag since we now really are in a document.
|
||||
UnsetFlags(NODE_FORCE_XBL_BINDINGS |
|
||||
@ -3049,7 +3050,11 @@ nsGenericElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
|
||||
nsIDocument *document =
|
||||
HasFlag(NODE_FORCE_XBL_BINDINGS) ? GetOwnerDoc() : GetCurrentDoc();
|
||||
|
||||
mParentPtrBits = aNullParent ? 0 : mParentPtrBits & ~PARENT_BIT_INDOCUMENT;
|
||||
if (aNullParent) {
|
||||
mParent = nsnull;
|
||||
SetParentIsContent(false);
|
||||
}
|
||||
ClearInDocument();
|
||||
|
||||
if (document) {
|
||||
// Notify XBL- & nsIAnonymousContentCreator-generated
|
||||
@ -5287,7 +5292,7 @@ nsGenericElement::IndexOf(nsINode* aPossibleChild) const
|
||||
nsINode::nsSlots*
|
||||
nsGenericElement::CreateSlots()
|
||||
{
|
||||
return new nsDOMSlots(mFlagsOrSlots);
|
||||
return new nsDOMSlots();
|
||||
}
|
||||
|
||||
PRBool
|
||||
|
@ -943,7 +943,7 @@ public:
|
||||
class nsDOMSlots : public nsINode::nsSlots
|
||||
{
|
||||
public:
|
||||
nsDOMSlots(PtrBits aFlags);
|
||||
nsDOMSlots();
|
||||
virtual ~nsDOMSlots();
|
||||
|
||||
/**
|
||||
@ -1024,14 +1024,14 @@ protected:
|
||||
* Add/remove this element to the documents id cache
|
||||
*/
|
||||
void AddToIdTable(nsIAtom* aId) {
|
||||
NS_ASSERTION(HasFlag(NODE_HAS_ID), "Node lacking NODE_HAS_ID flag");
|
||||
NS_ASSERTION(HasID(), "Node doesn't have an ID?");
|
||||
nsIDocument* doc = GetCurrentDoc();
|
||||
if (doc && (!IsInAnonymousSubtree() || doc->IsXUL())) {
|
||||
doc->AddToIdTable(this, aId);
|
||||
}
|
||||
}
|
||||
void RemoveFromIdTable() {
|
||||
if (HasFlag(NODE_HAS_ID)) {
|
||||
if (HasID()) {
|
||||
nsIDocument* doc = GetCurrentDoc();
|
||||
if (doc) {
|
||||
nsIAtom* id = DoGetID();
|
||||
|
@ -252,9 +252,8 @@ nsNodeUtils::LastRelease(nsINode* aNode)
|
||||
NodeWillBeDestroyed, (aNode));
|
||||
}
|
||||
|
||||
PtrBits flags = slots->mFlags | NODE_DOESNT_HAVE_SLOTS;
|
||||
delete slots;
|
||||
aNode->mFlagsOrSlots = flags;
|
||||
aNode->mSlots = nsnull;
|
||||
}
|
||||
|
||||
// Kill properties first since that may run external code, so we want to
|
||||
|
@ -63,25 +63,25 @@ namespace css = mozilla::css;
|
||||
// nsIContent methods
|
||||
|
||||
nsIAtom*
|
||||
nsStyledElement::GetClassAttributeName() const
|
||||
nsStyledElementNotElementCSSInlineStyle::GetClassAttributeName() const
|
||||
{
|
||||
return nsGkAtoms::_class;
|
||||
}
|
||||
|
||||
nsIAtom*
|
||||
nsStyledElement::GetIDAttributeName() const
|
||||
nsStyledElementNotElementCSSInlineStyle::GetIDAttributeName() const
|
||||
{
|
||||
return nsGkAtoms::id;
|
||||
}
|
||||
|
||||
nsIAtom*
|
||||
nsStyledElement::DoGetID() const
|
||||
nsStyledElementNotElementCSSInlineStyle::DoGetID() const
|
||||
{
|
||||
NS_ASSERTION(HasFlag(NODE_HAS_ID), "Unexpected call");
|
||||
NS_ASSERTION(HasID(), "Unexpected call");
|
||||
|
||||
// The nullcheck here is needed because nsGenericElement::UnsetAttr calls
|
||||
// out to various code between removing the attribute and we get a chance to
|
||||
// clear the NODE_HAS_ID flag.
|
||||
// ClearHasID().
|
||||
|
||||
const nsAttrValue* attr = mAttrsAndChildren.GetAttr(nsGkAtoms::id);
|
||||
|
||||
@ -89,19 +89,21 @@ nsStyledElement::DoGetID() const
|
||||
}
|
||||
|
||||
const nsAttrValue*
|
||||
nsStyledElement::DoGetClasses() const
|
||||
nsStyledElementNotElementCSSInlineStyle::DoGetClasses() const
|
||||
{
|
||||
NS_ASSERTION(HasFlag(NODE_MAY_HAVE_CLASS), "Unexpected call");
|
||||
return mAttrsAndChildren.GetAttr(nsGkAtoms::_class);
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsStyledElement::ParseAttribute(PRInt32 aNamespaceID, nsIAtom* aAttribute,
|
||||
const nsAString& aValue, nsAttrValue& aResult)
|
||||
nsStyledElementNotElementCSSInlineStyle::ParseAttribute(PRInt32 aNamespaceID,
|
||||
nsIAtom* aAttribute,
|
||||
const nsAString& aValue,
|
||||
nsAttrValue& aResult)
|
||||
{
|
||||
if (aNamespaceID == kNameSpaceID_None) {
|
||||
if (aAttribute == nsGkAtoms::style) {
|
||||
SetFlags(NODE_MAY_HAVE_STYLE);
|
||||
SetMayHaveStyle();
|
||||
ParseStyleAttribute(aValue, aResult, PR_FALSE);
|
||||
return PR_TRUE;
|
||||
}
|
||||
@ -115,11 +117,11 @@ nsStyledElement::ParseAttribute(PRInt32 aNamespaceID, nsIAtom* aAttribute,
|
||||
// not that it has an emptystring as the id.
|
||||
RemoveFromIdTable();
|
||||
if (aValue.IsEmpty()) {
|
||||
UnsetFlags(NODE_HAS_ID);
|
||||
ClearHasID();
|
||||
return PR_FALSE;
|
||||
}
|
||||
aResult.ParseAtom(aValue);
|
||||
SetFlags(NODE_HAS_ID);
|
||||
SetHasID();
|
||||
AddToIdTable(aResult.GetAtomValue());
|
||||
return PR_TRUE;
|
||||
}
|
||||
@ -130,8 +132,9 @@ nsStyledElement::ParseAttribute(PRInt32 aNamespaceID, nsIAtom* aAttribute,
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsStyledElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
|
||||
PRBool aNotify)
|
||||
nsStyledElementNotElementCSSInlineStyle::UnsetAttr(PRInt32 aNameSpaceID,
|
||||
nsIAtom* aAttribute,
|
||||
PRBool aNotify)
|
||||
{
|
||||
nsAutoRemovableScriptBlocker scriptBlocker;
|
||||
if (aAttribute == nsGkAtoms::id && aNameSpaceID == kNameSpaceID_None) {
|
||||
@ -143,15 +146,17 @@ nsStyledElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsStyledElement::AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aAttribute,
|
||||
const nsAString* aValue, PRBool aNotify)
|
||||
nsStyledElementNotElementCSSInlineStyle::AfterSetAttr(PRInt32 aNamespaceID,
|
||||
nsIAtom* aAttribute,
|
||||
const nsAString* aValue,
|
||||
PRBool aNotify)
|
||||
{
|
||||
if (aNamespaceID == kNameSpaceID_None && !aValue &&
|
||||
aAttribute == nsGkAtoms::id) {
|
||||
// The id has been removed when calling UnsetAttr but we kept it because
|
||||
// the id is used for some layout stuff between UnsetAttr and AfterSetAttr.
|
||||
// Now. the id is really removed so it would not be safe to keep this flag.
|
||||
UnsetFlags(NODE_HAS_ID);
|
||||
ClearHasID();
|
||||
}
|
||||
|
||||
return nsGenericElement::AfterSetAttr(aNamespaceID, aAttribute, aValue,
|
||||
@ -159,9 +164,10 @@ nsStyledElement::AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aAttribute,
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsStyledElement::SetInlineStyleRule(css::StyleRule* aStyleRule, PRBool aNotify)
|
||||
nsStyledElementNotElementCSSInlineStyle::SetInlineStyleRule(css::StyleRule* aStyleRule,
|
||||
PRBool aNotify)
|
||||
{
|
||||
SetFlags(NODE_MAY_HAVE_STYLE);
|
||||
SetMayHaveStyle();
|
||||
PRBool modification = PR_FALSE;
|
||||
nsAutoString oldValueStr;
|
||||
|
||||
@ -198,9 +204,9 @@ nsStyledElement::SetInlineStyleRule(css::StyleRule* aStyleRule, PRBool aNotify)
|
||||
}
|
||||
|
||||
css::StyleRule*
|
||||
nsStyledElement::GetInlineStyleRule()
|
||||
nsStyledElementNotElementCSSInlineStyle::GetInlineStyleRule()
|
||||
{
|
||||
if (!HasFlag(NODE_MAY_HAVE_STYLE)) {
|
||||
if (!MayHaveStyle()) {
|
||||
return nsnull;
|
||||
}
|
||||
const nsAttrValue* attrVal = mAttrsAndChildren.GetAttr(nsGkAtoms::style);
|
||||
@ -213,16 +219,17 @@ nsStyledElement::GetInlineStyleRule()
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsStyledElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
nsIContent* aBindingParent,
|
||||
PRBool aCompileEventHandlers)
|
||||
nsStyledElementNotElementCSSInlineStyle::BindToTree(nsIDocument* aDocument,
|
||||
nsIContent* aParent,
|
||||
nsIContent* aBindingParent,
|
||||
PRBool aCompileEventHandlers)
|
||||
{
|
||||
nsresult rv = nsStyledElementBase::BindToTree(aDocument, aParent,
|
||||
aBindingParent,
|
||||
aCompileEventHandlers);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (aDocument && HasFlag(NODE_HAS_ID) && !GetBindingParent()) {
|
||||
if (aDocument && HasID() && !GetBindingParent()) {
|
||||
aDocument->AddToIdTable(this, DoGetID());
|
||||
}
|
||||
|
||||
@ -236,7 +243,8 @@ nsStyledElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
}
|
||||
|
||||
void
|
||||
nsStyledElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
|
||||
nsStyledElementNotElementCSSInlineStyle::UnbindFromTree(PRBool aDeep,
|
||||
PRBool aNullParent)
|
||||
{
|
||||
RemoveFromIdTable();
|
||||
|
||||
@ -248,7 +256,7 @@ nsStyledElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
|
||||
// Others and helpers
|
||||
|
||||
nsIDOMCSSStyleDeclaration*
|
||||
nsStyledElement::GetStyle(nsresult* retval)
|
||||
nsStyledElementNotElementCSSInlineStyle::GetStyle(nsresult* retval)
|
||||
{
|
||||
nsXULElement* xulElement = nsXULElement::FromContent(this);
|
||||
if (xulElement) {
|
||||
@ -270,7 +278,7 @@ nsStyledElement::GetStyle(nsresult* retval)
|
||||
, PR_FALSE
|
||||
#endif // MOZ_SMIL
|
||||
);
|
||||
SetFlags(NODE_MAY_HAVE_STYLE);
|
||||
SetMayHaveStyle();
|
||||
}
|
||||
|
||||
*retval = NS_OK;
|
||||
@ -278,9 +286,9 @@ nsStyledElement::GetStyle(nsresult* retval)
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsStyledElement::ReparseStyleAttribute(PRBool aForceInDataDoc)
|
||||
nsStyledElementNotElementCSSInlineStyle::ReparseStyleAttribute(PRBool aForceInDataDoc)
|
||||
{
|
||||
if (!HasFlag(NODE_MAY_HAVE_STYLE)) {
|
||||
if (!MayHaveStyle()) {
|
||||
return NS_OK;
|
||||
}
|
||||
const nsAttrValue* oldVal = mAttrsAndChildren.GetAttr(nsGkAtoms::style);
|
||||
@ -300,9 +308,9 @@ nsStyledElement::ReparseStyleAttribute(PRBool aForceInDataDoc)
|
||||
}
|
||||
|
||||
void
|
||||
nsStyledElement::ParseStyleAttribute(const nsAString& aValue,
|
||||
nsAttrValue& aResult,
|
||||
PRBool aForceInDataDoc)
|
||||
nsStyledElementNotElementCSSInlineStyle::ParseStyleAttribute(const nsAString& aValue,
|
||||
nsAttrValue& aResult,
|
||||
PRBool aForceInDataDoc)
|
||||
{
|
||||
nsIDocument* doc = GetOwnerDoc();
|
||||
|
||||
|
@ -57,12 +57,12 @@ class StyleRule;
|
||||
|
||||
typedef nsGenericElement nsStyledElementBase;
|
||||
|
||||
class nsStyledElement : public nsStyledElementBase
|
||||
class nsStyledElementNotElementCSSInlineStyle : public nsStyledElementBase
|
||||
{
|
||||
|
||||
protected:
|
||||
|
||||
inline nsStyledElement(already_AddRefed<nsINodeInfo> aNodeInfo)
|
||||
inline nsStyledElementNotElementCSSInlineStyle(already_AddRefed<nsINodeInfo> aNodeInfo)
|
||||
: nsStyledElementBase(aNodeInfo)
|
||||
{}
|
||||
|
||||
@ -114,4 +114,11 @@ protected:
|
||||
nsresult ReparseStyleAttribute(PRBool aForceInDataDoc);
|
||||
};
|
||||
|
||||
class nsStyledElement : public nsStyledElementNotElementCSSInlineStyle {
|
||||
protected:
|
||||
inline nsStyledElement(already_AddRefed<nsINodeInfo> aNodeInfo)
|
||||
: nsStyledElementNotElementCSSInlineStyle(aNodeInfo)
|
||||
{}
|
||||
};
|
||||
|
||||
#endif // __NS_STYLEDELEMENT_H_
|
||||
|
@ -215,7 +215,9 @@ nsTextNode::BindToAttribute(nsIAttribute* aAttr)
|
||||
NS_ASSERTION(!GetNodeParent(), "Unbind before binding!");
|
||||
NS_ASSERTION(HasSameOwnerDoc(aAttr), "Wrong owner document!");
|
||||
|
||||
mParentPtrBits = reinterpret_cast<PtrBits>(aAttr);
|
||||
mParent = aAttr;
|
||||
SetParentIsContent(false);
|
||||
ClearInDocument();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -226,7 +228,7 @@ nsTextNode::UnbindFromAttribute()
|
||||
NS_ASSERTION(GetNodeParent() &&
|
||||
GetNodeParent()->IsNodeOfType(nsINode::eATTRIBUTE),
|
||||
"Use this method only to unbind from an attribute!");
|
||||
mParentPtrBits = 0;
|
||||
mParent = nsnull;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1119,6 +1119,7 @@ nsCanvasRenderingContext2D::SetDimensions(PRInt32 width, PRInt32 height)
|
||||
}
|
||||
|
||||
gCanvasMemoryUsed += width * height * 4;
|
||||
JS_updateMallocCounter(nsContentUtils::GetCurrentJSContext(), width * height * 4);
|
||||
}
|
||||
|
||||
return InitializeWithSurface(NULL, surface, width, height);
|
||||
@ -3844,32 +3845,51 @@ nsCanvasRenderingContext2D::GetImageData_explicit(PRInt32 x, PRInt32 y, PRUint32
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
if (w == 0 || h == 0)
|
||||
if (w == 0 || h == 0 || aDataLen != w * h * 4)
|
||||
return NS_ERROR_DOM_SYNTAX_ERR;
|
||||
|
||||
if (!CanvasUtils::CheckSaneSubrectSize (x, y, w, h, mWidth, mHeight))
|
||||
return NS_ERROR_DOM_SYNTAX_ERR;
|
||||
CheckedInt32 rightMost = CheckedInt32(x) + w;
|
||||
CheckedInt32 bottomMost = CheckedInt32(y) + h;
|
||||
|
||||
PRUint32 len = w * h * 4;
|
||||
if (aDataLen != len)
|
||||
if (!rightMost.valid() || !bottomMost.valid())
|
||||
return NS_ERROR_DOM_SYNTAX_ERR;
|
||||
|
||||
/* Copy the surface contents to the buffer */
|
||||
nsRefPtr<gfxImageSurface> tmpsurf = new gfxImageSurface(aData,
|
||||
gfxIntSize(w, h),
|
||||
w * 4,
|
||||
gfxASurface::ImageFormatARGB32);
|
||||
if (!tmpsurf || tmpsurf->CairoStatus())
|
||||
nsRefPtr<gfxImageSurface> tmpsurf =
|
||||
new gfxImageSurface(aData,
|
||||
gfxIntSize(w, h),
|
||||
w * 4,
|
||||
gfxASurface::ImageFormatARGB32);
|
||||
|
||||
if (tmpsurf->CairoStatus())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsRefPtr<gfxContext> tmpctx = new gfxContext(tmpsurf);
|
||||
|
||||
if (!tmpctx || tmpctx->HasError())
|
||||
if (tmpctx->HasError())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
tmpctx->SetOperator(gfxContext::OPERATOR_SOURCE);
|
||||
tmpctx->SetSource(mSurface, gfxPoint(-(int)x, -(int)y));
|
||||
tmpctx->Paint();
|
||||
gfxRect srcRect(0, 0, mWidth, mHeight);
|
||||
gfxRect destRect(x, y, w, h);
|
||||
|
||||
bool finishedPainting = false;
|
||||
// In the common case, we want to avoid the Rectangle call.
|
||||
if (!srcRect.Contains(destRect)) {
|
||||
// If the requested area is entirely outside the canvas, we're done.
|
||||
gfxRect tmp = srcRect.Intersect(destRect);
|
||||
finishedPainting = tmp.IsEmpty();
|
||||
|
||||
// Set clipping region if necessary.
|
||||
if (!finishedPainting) {
|
||||
tmpctx->Rectangle(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
if (!finishedPainting) {
|
||||
tmpctx->SetOperator(gfxContext::OPERATOR_SOURCE);
|
||||
tmpctx->SetSource(mSurface, gfxPoint(-x, -y));
|
||||
tmpctx->Paint();
|
||||
}
|
||||
|
||||
// make sure sUnpremultiplyTable has been created
|
||||
EnsureUnpremultiplyTable();
|
||||
|
@ -7957,19 +7957,15 @@ ctx.fillRect(0, 0, 100, 50);
|
||||
ctx.fillStyle = '#fff';
|
||||
ctx.fillRect(20, 10, 60, 10);
|
||||
|
||||
try {
|
||||
var imgdata1 = ctx.getImageData(85, 25, -10, -10);
|
||||
ok(imgdata1.data[0] === 255, "imgdata1.data[\""+(0)+"\"] === 255");
|
||||
ok(imgdata1.data[1] === 255, "imgdata1.data[\""+(1)+"\"] === 255");
|
||||
ok(imgdata1.data[2] === 255, "imgdata1.data[\""+(2)+"\"] === 255");
|
||||
ok(imgdata1.data[3] === 255, "imgdata1.data[\""+(3)+"\"] === 255");
|
||||
ok(imgdata1.data[imgdata1.data.length-4+0] === 0, "imgdata1.data[imgdata1.data.length-4+0] === 0");
|
||||
ok(imgdata1.data[imgdata1.data.length-4+1] === 0, "imgdata1.data[imgdata1.data.length-4+1] === 0");
|
||||
ok(imgdata1.data[imgdata1.data.length-4+2] === 0, "imgdata1.data[imgdata1.data.length-4+2] === 0");
|
||||
ok(imgdata1.data[imgdata1.data.length-4+3] === 255, "imgdata1.data[imgdata1.data.length-4+3] === 255");
|
||||
} catch (e) {
|
||||
ok(false, "Unexpected exception in first part of test_2d_imageData_get_source_negative()", e);
|
||||
}
|
||||
var imgdata1 = ctx.getImageData(85, 25, -10, -10);
|
||||
ok(imgdata1.data[0] === 255, "imgdata1.data[\""+(0)+"\"] === 255");
|
||||
ok(imgdata1.data[1] === 255, "imgdata1.data[\""+(1)+"\"] === 255");
|
||||
ok(imgdata1.data[2] === 255, "imgdata1.data[\""+(2)+"\"] === 255");
|
||||
ok(imgdata1.data[3] === 255, "imgdata1.data[\""+(3)+"\"] === 255");
|
||||
ok(imgdata1.data[imgdata1.data.length-4+0] === 0, "imgdata1.data[imgdata1.data.length-4+0] === 0");
|
||||
ok(imgdata1.data[imgdata1.data.length-4+1] === 0, "imgdata1.data[imgdata1.data.length-4+1] === 0");
|
||||
ok(imgdata1.data[imgdata1.data.length-4+2] === 0, "imgdata1.data[imgdata1.data.length-4+2] === 0");
|
||||
ok(imgdata1.data[imgdata1.data.length-4+3] === 255, "imgdata1.data[imgdata1.data.length-4+3] === 255");
|
||||
|
||||
var imgdata2 = ctx.getImageData(0, 0, -1, -1);
|
||||
ok(imgdata2.data[0] === 0, "imgdata2.data[\""+(0)+"\"] === 0");
|
||||
@ -7980,7 +7976,7 @@ ok(imgdata2.data[3] === 0, "imgdata2.data[\""+(3)+"\"] === 0");
|
||||
} catch (e) {
|
||||
_thrown_outer = true;
|
||||
}
|
||||
todo(!_thrown_outer, 'should not throw exception');
|
||||
ok(!_thrown_outer, 'should not throw exception');
|
||||
|
||||
|
||||
}
|
||||
@ -8028,10 +8024,44 @@ ok(imgdata4.data[1] === 0, "imgdata4.data[\""+(1)+"\"] === 0");
|
||||
ok(imgdata4.data[2] === 0, "imgdata4.data[\""+(2)+"\"] === 0");
|
||||
ok(imgdata4.data[3] === 0, "imgdata4.data[\""+(3)+"\"] === 0");
|
||||
|
||||
var imgdata5 = ctx.getImageData(100, 10, 1, 1);
|
||||
ok(imgdata5.data[0] === 0, "imgdata5.data[\""+(0)+"\"] === 0");
|
||||
ok(imgdata5.data[1] === 0, "imgdata5.data[\""+(1)+"\"] === 0");
|
||||
ok(imgdata5.data[2] === 0, "imgdata5.data[\""+(2)+"\"] === 0");
|
||||
ok(imgdata5.data[3] === 0, "imgdata5.data[\""+(3)+"\"] === 0");
|
||||
|
||||
var imgdata6 = ctx.getImageData(0, 10, 1, 1);
|
||||
ok(imgdata6.data[0] === 0, "imgdata6.data[\""+(0)+"\"] === 0");
|
||||
ok(imgdata6.data[1] === 136, "imgdata6.data[\""+(1)+"\"] === 136");
|
||||
ok(imgdata6.data[2] === 255, "imgdata6.data[\""+(2)+"\"] === 255");
|
||||
ok(imgdata6.data[3] === 255, "imgdata6.data[\""+(3)+"\"] === 255");
|
||||
|
||||
var imgdata7 = ctx.getImageData(-10, 10, 20, 20);
|
||||
ok(imgdata7.data[ 0*4+0] === 0, "imgdata7.data[ 0*4+0] === 0");
|
||||
ok(imgdata7.data[ 0*4+1] === 0, "imgdata7.data[ 0*4+1] === 0");
|
||||
ok(imgdata7.data[ 0*4+2] === 0, "imgdata7.data[ 0*4+2] === 0");
|
||||
ok(imgdata7.data[ 0*4+3] === 0, "imgdata7.data[ 0*4+3] === 0");
|
||||
ok(imgdata7.data[ 9*4+0] === 0, "imgdata7.data[ 9*4+0] === 0");
|
||||
ok(imgdata7.data[ 9*4+1] === 0, "imgdata7.data[ 9*4+1] === 0");
|
||||
ok(imgdata7.data[ 9*4+2] === 0, "imgdata7.data[ 9*4+2] === 0");
|
||||
ok(imgdata7.data[ 9*4+3] === 0, "imgdata7.data[ 9*4+3] === 0");
|
||||
ok(imgdata7.data[10*4+0] === 0, "imgdata7.data[10*4+0] === 0");
|
||||
ok(imgdata7.data[10*4+1] === 136, "imgdata7.data[10*4+1] === 136");
|
||||
ok(imgdata7.data[10*4+2] === 255, "imgdata7.data[10*4+2] === 255");
|
||||
ok(imgdata7.data[10*4+3] === 255, "imgdata7.data[10*4+3] === 255");
|
||||
ok(imgdata7.data[19*4+0] === 0, "imgdata7.data[19*4+0] === 0");
|
||||
ok(imgdata7.data[19*4+1] === 136, "imgdata7.data[19*4+1] === 136");
|
||||
ok(imgdata7.data[19*4+2] === 255, "imgdata7.data[19*4+2] === 255");
|
||||
ok(imgdata7.data[19*4+3] === 255, "imgdata7.data[19*4+3] === 255");
|
||||
ok(imgdata7.data[20*4+0] === 0, "imgdata7.data[20*4+0] === 0");
|
||||
ok(imgdata7.data[20*4+1] === 0, "imgdata7.data[20*4+1] === 0");
|
||||
ok(imgdata7.data[20*4+2] === 0, "imgdata7.data[20*4+2] === 0");
|
||||
ok(imgdata7.data[20*4+3] === 0, "imgdata7.data[20*4+3] === 0");
|
||||
|
||||
} catch (e) {
|
||||
_thrown_outer = true;
|
||||
}
|
||||
todo(!_thrown_outer, 'should not throw exception');
|
||||
ok(!_thrown_outer, 'should not throw exception');
|
||||
|
||||
|
||||
}
|
||||
|
@ -948,7 +948,7 @@ nsGenericHTMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (aDocument) {
|
||||
if (HasFlag(NODE_HAS_NAME)) {
|
||||
if (HasName()) {
|
||||
aDocument->
|
||||
AddToNameTable(this, GetParsedAttr(nsGkAtoms::name)->GetAtomValue());
|
||||
}
|
||||
@ -1188,7 +1188,7 @@ nsGenericHTMLElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
|
||||
PRInt32 change;
|
||||
if (contentEditable) {
|
||||
change = GetContentEditableValue() == eTrue ? -1 : 0;
|
||||
SetFlags(NODE_MAY_HAVE_CONTENT_EDITABLE_ATTR);
|
||||
SetMayHaveContentEditableAttr();
|
||||
}
|
||||
|
||||
nsresult rv = nsStyledElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue,
|
||||
@ -1218,7 +1218,7 @@ nsGenericHTMLElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
|
||||
if (aAttribute == nsGkAtoms::name) {
|
||||
// Have to do this before clearing flag. See RemoveFromNameTable
|
||||
RemoveFromNameTable();
|
||||
UnsetFlags(NODE_HAS_NAME);
|
||||
ClearHasName();
|
||||
}
|
||||
else if (aAttribute == nsGkAtoms::contenteditable) {
|
||||
contentEditable = PR_TRUE;
|
||||
@ -1287,14 +1287,14 @@ nsGenericHTMLElement::ParseAttribute(PRInt32 aNamespaceID,
|
||||
// not that it has an emptystring as the name.
|
||||
RemoveFromNameTable();
|
||||
if (aValue.IsEmpty()) {
|
||||
UnsetFlags(NODE_HAS_NAME);
|
||||
ClearHasName();
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
aResult.ParseAtom(aValue);
|
||||
|
||||
if (CanHaveName(Tag())) {
|
||||
SetFlags(NODE_HAS_NAME);
|
||||
SetHasName();
|
||||
AddToNameTable(aResult.GetAtomValue());
|
||||
}
|
||||
|
||||
|
@ -527,14 +527,14 @@ protected:
|
||||
* Add/remove this element to the documents name cache
|
||||
*/
|
||||
void AddToNameTable(nsIAtom* aName) {
|
||||
NS_ASSERTION(HasFlag(NODE_HAS_NAME), "Node lacking NODE_HAS_NAME flag");
|
||||
NS_ASSERTION(HasName(), "Node doesn't have name?");
|
||||
nsIDocument* doc = GetCurrentDoc();
|
||||
if (doc && !IsInAnonymousSubtree()) {
|
||||
doc->AddToNameTable(this, aName);
|
||||
}
|
||||
}
|
||||
void RemoveFromNameTable() {
|
||||
if (HasFlag(NODE_HAS_NAME)) {
|
||||
if (HasName()) {
|
||||
nsIDocument* doc = GetCurrentDoc();
|
||||
if (doc) {
|
||||
doc->RemoveFromNameTable(this, GetParsedAttr(nsGkAtoms::name)->
|
||||
@ -791,7 +791,7 @@ protected:
|
||||
static const nsIContent::AttrValuesArray values[] =
|
||||
{ &nsGkAtoms::_false, &nsGkAtoms::_true, &nsGkAtoms::_empty, nsnull };
|
||||
|
||||
if (!HasFlag(NODE_MAY_HAVE_CONTENT_EDITABLE_ATTR))
|
||||
if (!MayHaveContentEditableAttr())
|
||||
return eInherit;
|
||||
|
||||
PRInt32 value = FindAttrValueIn(kNameSpaceID_None,
|
||||
|
@ -2613,7 +2613,7 @@ FindNamedItems(nsIAtom* aName, nsIContent *aContent,
|
||||
NS_ASSERTION(!aEntry->IsInvalidName(),
|
||||
"Entry that should never have a list passed to FindNamedItems()!");
|
||||
|
||||
if (aContent->HasFlag(NODE_HAS_NAME)) {
|
||||
if (aContent->HasName()) {
|
||||
NS_ASSERTION(nsGenericHTMLElement::FromContent(aContent),
|
||||
"Only HTML Elements should have a name");
|
||||
|
||||
|
@ -70,7 +70,8 @@ nsMediaChannelStream::nsMediaChannelStream(nsMediaDecoder* aDecoder,
|
||||
mReopenOnError(PR_FALSE), mIgnoreClose(PR_FALSE),
|
||||
mCacheStream(this),
|
||||
mLock("nsMediaChannelStream.mLock"),
|
||||
mCacheSuspendCount(0)
|
||||
mCacheSuspendCount(0),
|
||||
mIgnoreResume(PR_FALSE)
|
||||
{
|
||||
}
|
||||
|
||||
@ -276,7 +277,10 @@ nsMediaChannelStream::OnStartRequest(nsIRequest* aRequest)
|
||||
mIgnoreClose = PR_FALSE;
|
||||
if (mSuspendCount > 0) {
|
||||
// Re-suspend the channel if it needs to be suspended
|
||||
// No need to call PossiblySuspend here since the channel is
|
||||
// definitely in the right state for us in OneStartRequest.
|
||||
mChannel->Suspend();
|
||||
mIgnoreResume = PR_FALSE;
|
||||
}
|
||||
|
||||
// Fires an initial progress event and sets up the stall counter so stall events
|
||||
@ -556,7 +560,7 @@ void nsMediaChannelStream::CloseChannel()
|
||||
if (mChannel) {
|
||||
if (mSuspendCount > 0) {
|
||||
// Resume the channel before we cancel it
|
||||
mChannel->Resume();
|
||||
PossiblyResume();
|
||||
}
|
||||
// The status we use here won't be passed to the decoder, since
|
||||
// we've already revoked the listener. It can however be passed
|
||||
@ -626,7 +630,7 @@ void nsMediaChannelStream::Suspend(PRBool aCloseImmediately)
|
||||
MutexAutoLock lock(mLock);
|
||||
mChannelStatistics.Stop(TimeStamp::Now());
|
||||
}
|
||||
mChannel->Suspend();
|
||||
PossiblySuspend();
|
||||
element->DownloadSuspended();
|
||||
}
|
||||
}
|
||||
@ -657,7 +661,7 @@ void nsMediaChannelStream::Resume()
|
||||
// if an error occurs after Resume, assume it's because the server
|
||||
// timed out the connection and we should reopen it.
|
||||
mReopenOnError = PR_TRUE;
|
||||
mChannel->Resume();
|
||||
PossiblyResume();
|
||||
element->DownloadResumed();
|
||||
} else {
|
||||
PRInt64 totalLength = mCacheStream.GetLength();
|
||||
@ -867,6 +871,29 @@ nsMediaChannelStream::GetLength()
|
||||
return mCacheStream.GetLength();
|
||||
}
|
||||
|
||||
void
|
||||
nsMediaChannelStream::PossiblySuspend()
|
||||
{
|
||||
PRBool isPending = PR_FALSE;
|
||||
nsresult rv = mChannel->IsPending(&isPending);
|
||||
if (NS_SUCCEEDED(rv) && isPending) {
|
||||
mChannel->Suspend();
|
||||
mIgnoreResume = PR_FALSE;
|
||||
} else {
|
||||
mIgnoreResume = PR_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsMediaChannelStream::PossiblyResume()
|
||||
{
|
||||
if (!mIgnoreResume) {
|
||||
mChannel->Resume();
|
||||
} else {
|
||||
mIgnoreResume = PR_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
class nsMediaFileStream : public nsMediaStream
|
||||
{
|
||||
public:
|
||||
|
@ -452,6 +452,14 @@ protected:
|
||||
PRUint32 aCount,
|
||||
PRUint32 *aWriteCount);
|
||||
|
||||
// Suspend the channel only if the channels is currently downloading data.
|
||||
// If it isn't we set a flag, mIgnoreResume, so that PossiblyResume knows
|
||||
// whether to acutually resume or not.
|
||||
void PossiblySuspend();
|
||||
|
||||
// Resume from a suspend if we actually suspended (See PossiblySuspend).
|
||||
void PossiblyResume();
|
||||
|
||||
// Main thread access only
|
||||
PRInt64 mOffset;
|
||||
nsRefPtr<Listener> mListener;
|
||||
@ -473,6 +481,11 @@ protected:
|
||||
Mutex mLock;
|
||||
nsChannelStatistics mChannelStatistics;
|
||||
PRUint32 mCacheSuspendCount;
|
||||
|
||||
// PR_TRUE if we couldn't suspend the stream and we therefore don't want
|
||||
// to resume later. This is usually due to the channel not being in the
|
||||
// isPending state at the time of the suspend request.
|
||||
PRPackedBool mIgnoreResume;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -92,6 +92,21 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGLengthList)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
|
||||
nsIDOMSVGLength*
|
||||
DOMSVGLengthList::GetItemWithoutAddRef(PRUint32 aIndex)
|
||||
{
|
||||
#ifdef MOZ_SMIL
|
||||
if (IsAnimValList()) {
|
||||
Element()->FlushAnimations();
|
||||
}
|
||||
#endif
|
||||
if (aIndex < Length()) {
|
||||
EnsureItemAt(aIndex);
|
||||
return mItems[aIndex];
|
||||
}
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
void
|
||||
DOMSVGLengthList::InternalListLengthWillChange(PRUint32 aNewLength)
|
||||
{
|
||||
@ -210,18 +225,12 @@ NS_IMETHODIMP
|
||||
DOMSVGLengthList::GetItem(PRUint32 index,
|
||||
nsIDOMSVGLength **_retval)
|
||||
{
|
||||
#ifdef MOZ_SMIL
|
||||
if (IsAnimValList()) {
|
||||
Element()->FlushAnimations();
|
||||
*_retval = GetItemWithoutAddRef(index);
|
||||
if (!*_retval) {
|
||||
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
||||
}
|
||||
#endif
|
||||
if (index < Length()) {
|
||||
EnsureItemAt(index);
|
||||
NS_ADDREF(*_retval = mItems[index]);
|
||||
return NS_OK;
|
||||
}
|
||||
*_retval = nsnull;
|
||||
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
||||
NS_ADDREF(*_retval);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -367,6 +376,12 @@ DOMSVGLengthList::AppendItem(nsIDOMSVGLength *newItem,
|
||||
return InsertItemBefore(newItem, Length(), _retval);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DOMSVGLengthList::GetLength(PRUint32 *aNumberOfItems)
|
||||
{
|
||||
return GetNumberOfItems(aNumberOfItems);
|
||||
}
|
||||
|
||||
void
|
||||
DOMSVGLengthList::EnsureItemAt(PRUint32 aIndex)
|
||||
{
|
||||
|
@ -109,6 +109,8 @@ public:
|
||||
return mItems.Length();
|
||||
}
|
||||
|
||||
nsIDOMSVGLength* GetItemWithoutAddRef(PRUint32 aIndex);
|
||||
|
||||
/// Called to notify us to syncronize our length and detach excess items.
|
||||
void InternalListLengthWillChange(PRUint32 aNewLength);
|
||||
|
||||
|
@ -92,6 +92,21 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGNumberList)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
|
||||
nsIDOMSVGNumber*
|
||||
DOMSVGNumberList::GetItemWithoutAddRef(PRUint32 aIndex)
|
||||
{
|
||||
#ifdef MOZ_SMIL
|
||||
if (IsAnimValList()) {
|
||||
Element()->FlushAnimations();
|
||||
}
|
||||
#endif
|
||||
if (aIndex < Length()) {
|
||||
EnsureItemAt(aIndex);
|
||||
return mItems[aIndex];
|
||||
}
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
void
|
||||
DOMSVGNumberList::InternalListLengthWillChange(PRUint32 aNewLength)
|
||||
{
|
||||
@ -210,18 +225,12 @@ NS_IMETHODIMP
|
||||
DOMSVGNumberList::GetItem(PRUint32 index,
|
||||
nsIDOMSVGNumber **_retval)
|
||||
{
|
||||
#ifdef MOZ_SMIL
|
||||
if (IsAnimValList()) {
|
||||
Element()->FlushAnimations();
|
||||
*_retval = GetItemWithoutAddRef(index);
|
||||
if (!*_retval) {
|
||||
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
||||
}
|
||||
#endif
|
||||
if (index < Length()) {
|
||||
EnsureItemAt(index);
|
||||
NS_ADDREF(*_retval = mItems[index]);
|
||||
return NS_OK;
|
||||
}
|
||||
*_retval = nsnull;
|
||||
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
||||
NS_ADDREF(*_retval);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -367,6 +376,12 @@ DOMSVGNumberList::AppendItem(nsIDOMSVGNumber *newItem,
|
||||
return InsertItemBefore(newItem, Length(), _retval);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DOMSVGNumberList::GetLength(PRUint32 *aNumberOfItems)
|
||||
{
|
||||
return GetNumberOfItems(aNumberOfItems);
|
||||
}
|
||||
|
||||
void
|
||||
DOMSVGNumberList::EnsureItemAt(PRUint32 aIndex)
|
||||
{
|
||||
|
@ -108,6 +108,8 @@ public:
|
||||
return mItems.Length();
|
||||
}
|
||||
|
||||
nsIDOMSVGNumber* GetItemWithoutAddRef(PRUint32 aIndex);
|
||||
|
||||
/// Called to notify us to syncronize our length and detach excess items.
|
||||
void InternalListLengthWillChange(PRUint32 aNewLength);
|
||||
|
||||
|
@ -96,6 +96,21 @@ DOMSVGPathSegList::~DOMSVGPathSegList()
|
||||
sSVGPathSegListTearoffTable.RemoveTearoff(key);
|
||||
}
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
DOMSVGPathSegList::GetItemWithoutAddRef(PRUint32 aIndex)
|
||||
{
|
||||
#ifdef MOZ_SMIL
|
||||
if (IsAnimValList()) {
|
||||
Element()->FlushAnimations();
|
||||
}
|
||||
#endif
|
||||
if (aIndex < Length()) {
|
||||
EnsureItemAt(aIndex);
|
||||
return ItemAt(aIndex);
|
||||
}
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
void
|
||||
DOMSVGPathSegList::InternalListWillChangeTo(const SVGPathData& aNewValue)
|
||||
{
|
||||
@ -305,18 +320,12 @@ NS_IMETHODIMP
|
||||
DOMSVGPathSegList::GetItem(PRUint32 aIndex,
|
||||
nsIDOMSVGPathSeg **_retval)
|
||||
{
|
||||
#ifdef MOZ_SMIL
|
||||
if (IsAnimValList()) {
|
||||
Element()->FlushAnimations();
|
||||
*_retval = GetItemWithoutAddRef(aIndex);
|
||||
if (!*_retval) {
|
||||
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
||||
}
|
||||
#endif
|
||||
if (aIndex < Length()) {
|
||||
EnsureItemAt(aIndex);
|
||||
NS_ADDREF(*_retval = ItemAt(aIndex));
|
||||
return NS_OK;
|
||||
}
|
||||
*_retval = nsnull;
|
||||
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
||||
NS_ADDREF(*_retval);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -498,6 +507,12 @@ DOMSVGPathSegList::AppendItem(nsIDOMSVGPathSeg *aNewItem,
|
||||
return InsertItemBefore(aNewItem, Length(), _retval);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DOMSVGPathSegList::GetLength(PRUint32 *aNumberOfItems)
|
||||
{
|
||||
return GetNumberOfItems(aNumberOfItems);
|
||||
}
|
||||
|
||||
void
|
||||
DOMSVGPathSegList::EnsureItemAt(PRUint32 aIndex)
|
||||
{
|
||||
|
@ -126,6 +126,8 @@ public:
|
||||
return mItems.Length();
|
||||
}
|
||||
|
||||
nsIDOMSVGPathSeg* GetItemWithoutAddRef(PRUint32 aIndex);
|
||||
|
||||
/**
|
||||
* WATCH OUT! If you add code to call this on a baseVal wrapper, then you
|
||||
* must also call it on the animVal wrapper too if necessary!! See other
|
||||
|
@ -116,6 +116,21 @@ DOMSVGPointList::~DOMSVGPointList()
|
||||
sSVGPointListTearoffTable.RemoveTearoff(key);
|
||||
}
|
||||
|
||||
nsIDOMSVGPoint*
|
||||
DOMSVGPointList::GetItemWithoutAddRef(PRUint32 aIndex)
|
||||
{
|
||||
#ifdef MOZ_SMIL
|
||||
if (IsAnimValList()) {
|
||||
Element()->FlushAnimations();
|
||||
}
|
||||
#endif
|
||||
if (aIndex < Length()) {
|
||||
EnsureItemAt(aIndex);
|
||||
return mItems[aIndex];
|
||||
}
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
void
|
||||
DOMSVGPointList::InternalListWillChangeTo(const SVGPointList& aNewValue)
|
||||
{
|
||||
@ -261,18 +276,12 @@ NS_IMETHODIMP
|
||||
DOMSVGPointList::GetItem(PRUint32 aIndex,
|
||||
nsIDOMSVGPoint **_retval)
|
||||
{
|
||||
#ifdef MOZ_SMIL
|
||||
if (IsAnimValList()) {
|
||||
Element()->FlushAnimations();
|
||||
*_retval = GetItemWithoutAddRef(aIndex);
|
||||
if (!*_retval) {
|
||||
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
||||
}
|
||||
#endif
|
||||
if (aIndex < Length()) {
|
||||
EnsureItemAt(aIndex);
|
||||
NS_ADDREF(*_retval = mItems[aIndex]);
|
||||
return NS_OK;
|
||||
}
|
||||
*_retval = nsnull;
|
||||
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
||||
NS_ADDREF(*_retval);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -418,6 +427,12 @@ DOMSVGPointList::AppendItem(nsIDOMSVGPoint *aNewItem,
|
||||
return InsertItemBefore(aNewItem, Length(), _retval);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DOMSVGPointList::GetLength(PRUint32 *aNumberOfItems)
|
||||
{
|
||||
return GetNumberOfItems(aNumberOfItems);
|
||||
}
|
||||
|
||||
void
|
||||
DOMSVGPointList::EnsureItemAt(PRUint32 aIndex)
|
||||
{
|
||||
|
@ -126,6 +126,8 @@ public:
|
||||
return mItems.Length();
|
||||
}
|
||||
|
||||
nsIDOMSVGPoint* GetItemWithoutAddRef(PRUint32 aIndex);
|
||||
|
||||
/**
|
||||
* WATCH OUT! If you add code to call this on a baseVal wrapper, then you
|
||||
* must also call it on the animVal wrapper too if necessary!! See other
|
||||
|
@ -124,18 +124,17 @@ SVGPathData::AppendSeg(PRUint32 aType, ...)
|
||||
float
|
||||
SVGPathData::GetPathLength() const
|
||||
{
|
||||
float length = 0.0;
|
||||
SVGPathTraversalState state;
|
||||
|
||||
PRUint32 i = 0;
|
||||
while (i < mData.Length()) {
|
||||
length += SVGPathSegUtils::GetLength(&mData[i], state);
|
||||
SVGPathSegUtils::TraversePathSegment(&mData[i], state);
|
||||
i += 1 + SVGPathSegUtils::ArgCountForType(mData[i]);
|
||||
}
|
||||
|
||||
NS_ABORT_IF_FALSE(i == mData.Length(), "Very, very bad - mData corrupt");
|
||||
|
||||
return length;
|
||||
return state.length;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -163,7 +162,9 @@ SVGPathData::GetSegmentLengths(nsTArray<double> *aLengths) const
|
||||
|
||||
PRUint32 i = 0;
|
||||
while (i < mData.Length()) {
|
||||
if (!aLengths->AppendElement(SVGPathSegUtils::GetLength(&mData[i], state))) {
|
||||
state.length = 0.0;
|
||||
SVGPathSegUtils::TraversePathSegment(&mData[i], state);
|
||||
if (!aLengths->AppendElement(state.length)) {
|
||||
aLengths->Clear();
|
||||
return PR_FALSE;
|
||||
}
|
||||
@ -178,7 +179,6 @@ SVGPathData::GetSegmentLengths(nsTArray<double> *aLengths) const
|
||||
PRBool
|
||||
SVGPathData::GetDistancesFromOriginToEndsOfVisibleSegments(nsTArray<double> *aOutput) const
|
||||
{
|
||||
double distRunningTotal = 0.0;
|
||||
SVGPathTraversalState state;
|
||||
|
||||
aOutput->Clear();
|
||||
@ -186,6 +186,7 @@ SVGPathData::GetDistancesFromOriginToEndsOfVisibleSegments(nsTArray<double> *aOu
|
||||
PRUint32 i = 0;
|
||||
while (i < mData.Length()) {
|
||||
PRUint32 segType = SVGPathSegUtils::DecodeType(mData[i]);
|
||||
SVGPathSegUtils::TraversePathSegment(&mData[i], state);
|
||||
|
||||
// We skip all moveto commands except an initial moveto. See the text 'A
|
||||
// "move to" command does not count as an additional point when dividing up
|
||||
@ -199,8 +200,7 @@ SVGPathData::GetDistancesFromOriginToEndsOfVisibleSegments(nsTArray<double> *aOu
|
||||
|
||||
if (i == 0 || (segType != nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS &&
|
||||
segType != nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL)) {
|
||||
distRunningTotal += SVGPathSegUtils::GetLength(&mData[i], state);
|
||||
if (!aOutput->AppendElement(distRunningTotal)) {
|
||||
if (!aOutput->AppendElement(state.length)) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
}
|
||||
@ -220,13 +220,12 @@ SVGPathData::GetPathSegAtLength(float aDistance) const
|
||||
// Return -1? Throwing would better help authors avoid tricky bugs (DOM
|
||||
// could do that if we return -1).
|
||||
|
||||
double distRunningTotal = 0.0;
|
||||
PRUint32 i = 0, segIndex = 0;
|
||||
SVGPathTraversalState state;
|
||||
|
||||
while (i < mData.Length()) {
|
||||
distRunningTotal += SVGPathSegUtils::GetLength(&mData[i], state);
|
||||
if (distRunningTotal >= aDistance) {
|
||||
SVGPathSegUtils::TraversePathSegment(&mData[i], state);
|
||||
if (state.length >= aDistance) {
|
||||
return segIndex;
|
||||
}
|
||||
i += 1 + SVGPathSegUtils::ArgCountForType(mData[i]);
|
||||
|
@ -105,6 +105,7 @@ class SVGPathData
|
||||
// are responsible for that!
|
||||
|
||||
public:
|
||||
typedef const float* const_iterator;
|
||||
|
||||
SVGPathData(){}
|
||||
~SVGPathData(){}
|
||||
@ -121,7 +122,7 @@ public:
|
||||
|
||||
#ifdef DEBUG
|
||||
/**
|
||||
* This method iterates over the encoded segment data and countes the number
|
||||
* This method iterates over the encoded segment data and counts the number
|
||||
* of segments we currently have.
|
||||
*/
|
||||
PRUint32 CountItems() const;
|
||||
@ -178,6 +179,9 @@ public:
|
||||
|
||||
void ConstructPath(gfxContext *aCtx) const;
|
||||
|
||||
const_iterator begin() const { return mData.Elements(); }
|
||||
const_iterator end() const { return mData.Elements() + mData.Length(); }
|
||||
|
||||
// Access to methods that can modify objects of this type is deliberately
|
||||
// limited. This is to reduce the chances of someone modifying objects of
|
||||
// this type without taking the necessary steps to keep DOM wrappers in sync.
|
||||
@ -186,6 +190,7 @@ public:
|
||||
// can take care of keeping DOM wrappers in sync.
|
||||
|
||||
protected:
|
||||
typedef float* iterator;
|
||||
|
||||
/**
|
||||
* This may fail on OOM if the internal capacity needs to be increased, in
|
||||
@ -221,6 +226,9 @@ protected:
|
||||
|
||||
nsresult AppendSeg(PRUint32 aType, ...); // variable number of float args
|
||||
|
||||
iterator begin() { return mData.Elements(); }
|
||||
iterator end() { return mData.Elements() + mData.Length(); }
|
||||
|
||||
nsTArray<float> mData;
|
||||
};
|
||||
|
||||
@ -236,7 +244,6 @@ protected:
|
||||
class SVGPathDataAndOwner : public SVGPathData
|
||||
{
|
||||
public:
|
||||
|
||||
SVGPathDataAndOwner(nsSVGElement *aElement = nsnull)
|
||||
: mElement(aElement)
|
||||
{}
|
||||
@ -259,18 +266,14 @@ public:
|
||||
* SVGPathDataAndOwner objects. Note that callers should also call
|
||||
* SetElement() when using this method!
|
||||
*/
|
||||
nsresult CopyFrom(const SVGPathData& rhs) {
|
||||
return SVGPathData::CopyFrom(rhs);
|
||||
}
|
||||
const float& operator[](PRUint32 aIndex) const {
|
||||
return SVGPathData::operator[](aIndex);
|
||||
}
|
||||
float& operator[](PRUint32 aIndex) {
|
||||
return SVGPathData::operator[](aIndex);
|
||||
}
|
||||
PRBool SetLength(PRUint32 aNumberOfItems) {
|
||||
return SVGPathData::SetLength(aNumberOfItems);
|
||||
}
|
||||
using SVGPathData::CopyFrom;
|
||||
|
||||
// Exposed since SVGPathData objects can be modified.
|
||||
using SVGPathData::iterator;
|
||||
using SVGPathData::operator[];
|
||||
using SVGPathData::SetLength;
|
||||
using SVGPathData::begin;
|
||||
using SVGPathData::end;
|
||||
|
||||
private:
|
||||
// We must keep a strong reference to our element because we may belong to a
|
||||
|
@ -89,6 +89,240 @@ SVGPathSegListSMILType::IsEqual(const nsSMILValue& aLeft,
|
||||
*static_cast<const SVGPathDataAndOwner*>(aRight.mU.mPtr);
|
||||
}
|
||||
|
||||
static PRBool
|
||||
ArcFlagsDiffer(SVGPathDataAndOwner::const_iterator aPathData1,
|
||||
SVGPathDataAndOwner::const_iterator aPathData2)
|
||||
{
|
||||
NS_ABORT_IF_FALSE
|
||||
(SVGPathSegUtils::IsArcType(SVGPathSegUtils::DecodeType(aPathData1[0])),
|
||||
"ArcFlagsDiffer called with non-arc segment");
|
||||
NS_ABORT_IF_FALSE
|
||||
(SVGPathSegUtils::IsArcType(SVGPathSegUtils::DecodeType(aPathData2[0])),
|
||||
"ArcFlagsDiffer called with non-arc segment");
|
||||
|
||||
return aPathData1[4] != aPathData2[4] || // large arc flag
|
||||
aPathData1[5] != aPathData2[5]; // sweep flag
|
||||
}
|
||||
|
||||
enum PathInterpolationResult {
|
||||
eCannotInterpolate,
|
||||
eRequiresConversion,
|
||||
eCanInterpolate
|
||||
};
|
||||
|
||||
static PathInterpolationResult
|
||||
CanInterpolate(const SVGPathDataAndOwner& aStart,
|
||||
const SVGPathDataAndOwner& aEnd)
|
||||
{
|
||||
if (aStart.IsEmpty()) {
|
||||
return eCanInterpolate;
|
||||
}
|
||||
|
||||
if (aStart.Length() != aEnd.Length()) {
|
||||
return eCannotInterpolate;
|
||||
}
|
||||
|
||||
PathInterpolationResult result = eCanInterpolate;
|
||||
|
||||
SVGPathDataAndOwner::const_iterator pStart = aStart.begin();
|
||||
SVGPathDataAndOwner::const_iterator pEnd = aEnd.begin();
|
||||
SVGPathDataAndOwner::const_iterator pStartDataEnd = aStart.end();
|
||||
SVGPathDataAndOwner::const_iterator pEndDataEnd = aEnd.end();
|
||||
|
||||
while (pStart < pStartDataEnd && pEnd < pEndDataEnd) {
|
||||
PRUint32 startType = SVGPathSegUtils::DecodeType(*pStart);
|
||||
PRUint32 endType = SVGPathSegUtils::DecodeType(*pEnd);
|
||||
|
||||
if (SVGPathSegUtils::IsArcType(startType) &&
|
||||
SVGPathSegUtils::IsArcType(endType) &&
|
||||
ArcFlagsDiffer(pStart, pEnd)) {
|
||||
return eCannotInterpolate;
|
||||
}
|
||||
|
||||
if (startType != endType) {
|
||||
if (!SVGPathSegUtils::SameTypeModuloRelativeness(startType, endType)) {
|
||||
return eCannotInterpolate;
|
||||
}
|
||||
|
||||
result = eRequiresConversion;
|
||||
}
|
||||
|
||||
pStart += 1 + SVGPathSegUtils::ArgCountForType(startType);
|
||||
pEnd += 1 + SVGPathSegUtils::ArgCountForType(endType);
|
||||
}
|
||||
|
||||
NS_ABORT_IF_FALSE(pStart <= pStartDataEnd && pEnd <= pEndDataEnd,
|
||||
"Iterated past end of buffer! (Corrupt path data?)");
|
||||
|
||||
if (pStart != pStartDataEnd || pEnd != pEndDataEnd) {
|
||||
return eCannotInterpolate;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
InterpolatePathSegmentData(SVGPathDataAndOwner::const_iterator& aStart,
|
||||
SVGPathDataAndOwner::const_iterator& aEnd,
|
||||
SVGPathDataAndOwner::iterator& aResult,
|
||||
float aUnitDistance)
|
||||
{
|
||||
PRUint32 startType = SVGPathSegUtils::DecodeType(*aStart);
|
||||
PRUint32 endType = SVGPathSegUtils::DecodeType(*aEnd);
|
||||
|
||||
NS_ABORT_IF_FALSE
|
||||
(startType == endType,
|
||||
"InterpolatePathSegmentData expects segment types to be the same!");
|
||||
|
||||
NS_ABORT_IF_FALSE
|
||||
(!(SVGPathSegUtils::IsArcType(startType) && ArcFlagsDiffer(aStart, aEnd)),
|
||||
"InterpolatePathSegmentData cannot interpolate arc segments with different flag values!");
|
||||
|
||||
PRUint32 argCount = SVGPathSegUtils::ArgCountForType(startType);
|
||||
|
||||
// Copy over segment type.
|
||||
*aResult++ = *aStart++;
|
||||
++aEnd;
|
||||
|
||||
// Interpolate the arguments.
|
||||
SVGPathDataAndOwner::const_iterator startSegEnd = aStart + argCount;
|
||||
while (aStart != startSegEnd) {
|
||||
*aResult = *aStart + (*aEnd - *aStart) * aUnitDistance;
|
||||
++aStart;
|
||||
++aEnd;
|
||||
++aResult;
|
||||
}
|
||||
}
|
||||
|
||||
enum RelativenessAdjustmentType {
|
||||
eAbsoluteToRelative,
|
||||
eRelativeToAbsolute
|
||||
};
|
||||
|
||||
static inline void
|
||||
AdjustSegmentForRelativeness(RelativenessAdjustmentType aAdjustmentType,
|
||||
const SVGPathDataAndOwner::iterator& aSegmentToAdjust,
|
||||
const SVGPathTraversalState& aState)
|
||||
{
|
||||
if (aAdjustmentType == eAbsoluteToRelative) {
|
||||
aSegmentToAdjust[0] -= aState.pos.x;
|
||||
aSegmentToAdjust[1] -= aState.pos.y;
|
||||
} else {
|
||||
aSegmentToAdjust[0] += aState.pos.x;
|
||||
aSegmentToAdjust[1] += aState.pos.y;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ConvertPathSegmentData(SVGPathDataAndOwner::const_iterator& aStart,
|
||||
SVGPathDataAndOwner::const_iterator& aEnd,
|
||||
SVGPathDataAndOwner::iterator& aResult,
|
||||
SVGPathTraversalState& aState)
|
||||
{
|
||||
PRUint32 startType = SVGPathSegUtils::DecodeType(*aStart);
|
||||
PRUint32 endType = SVGPathSegUtils::DecodeType(*aEnd);
|
||||
|
||||
PRUint32 segmentLengthIncludingType =
|
||||
1 + SVGPathSegUtils::ArgCountForType(startType);
|
||||
|
||||
SVGPathDataAndOwner::const_iterator pResultSegmentBegin = aResult;
|
||||
|
||||
if (startType == endType) {
|
||||
// No conversion need, just directly copy aStart.
|
||||
aEnd += segmentLengthIncludingType;
|
||||
while (segmentLengthIncludingType) {
|
||||
*aResult++ = *aStart++;
|
||||
--segmentLengthIncludingType;
|
||||
}
|
||||
SVGPathSegUtils::TraversePathSegment(pResultSegmentBegin, aState);
|
||||
return;
|
||||
}
|
||||
|
||||
NS_ABORT_IF_FALSE
|
||||
(SVGPathSegUtils::SameTypeModuloRelativeness(startType, endType),
|
||||
"Incompatible path segment types passed to ConvertPathSegmentData!");
|
||||
|
||||
RelativenessAdjustmentType adjustmentType =
|
||||
SVGPathSegUtils::IsRelativeType(startType) ? eRelativeToAbsolute
|
||||
: eAbsoluteToRelative;
|
||||
|
||||
NS_ABORT_IF_FALSE
|
||||
(segmentLengthIncludingType ==
|
||||
1 + SVGPathSegUtils::ArgCountForType(endType),
|
||||
"Compatible path segment types for interpolation had different lengths!");
|
||||
|
||||
aResult[0] = aEnd[0];
|
||||
|
||||
switch (endType) {
|
||||
case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS:
|
||||
case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_REL:
|
||||
aResult[1] = aStart[1] +
|
||||
(adjustmentType == eRelativeToAbsolute ? 1 : -1) * aState.pos.x;
|
||||
break;
|
||||
case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS:
|
||||
case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_REL:
|
||||
aResult[1] = aStart[1] +
|
||||
(adjustmentType == eRelativeToAbsolute ? 1 : -1) * aState.pos.y;
|
||||
break;
|
||||
case nsIDOMSVGPathSeg::PATHSEG_ARC_ABS:
|
||||
case nsIDOMSVGPathSeg::PATHSEG_ARC_REL:
|
||||
aResult[1] = aStart[1];
|
||||
aResult[2] = aStart[2];
|
||||
aResult[3] = aStart[3];
|
||||
aResult[4] = aStart[4];
|
||||
aResult[5] = aStart[5];
|
||||
aResult[6] = aStart[6];
|
||||
aResult[7] = aStart[7];
|
||||
AdjustSegmentForRelativeness(adjustmentType, aResult + 6, aState);
|
||||
break;
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS:
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL:
|
||||
aResult[5] = aStart[5];
|
||||
aResult[6] = aStart[6];
|
||||
AdjustSegmentForRelativeness(adjustmentType, aResult + 5, aState);
|
||||
// fall through
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS:
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL:
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
|
||||
aResult[3] = aStart[3];
|
||||
aResult[4] = aStart[4];
|
||||
AdjustSegmentForRelativeness(adjustmentType, aResult + 3, aState);
|
||||
// fall through
|
||||
case nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS:
|
||||
case nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL:
|
||||
case nsIDOMSVGPathSeg::PATHSEG_LINETO_ABS:
|
||||
case nsIDOMSVGPathSeg::PATHSEG_LINETO_REL:
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
|
||||
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
|
||||
aResult[1] = aStart[1];
|
||||
aResult[2] = aStart[2];
|
||||
AdjustSegmentForRelativeness(adjustmentType, aResult + 1, aState);
|
||||
break;
|
||||
}
|
||||
|
||||
SVGPathSegUtils::TraversePathSegment(pResultSegmentBegin, aState);
|
||||
aStart += segmentLengthIncludingType;
|
||||
aEnd += segmentLengthIncludingType;
|
||||
aResult += segmentLengthIncludingType;
|
||||
}
|
||||
|
||||
static void
|
||||
ConvertAllPathSegmentData(SVGPathDataAndOwner::const_iterator aStart,
|
||||
SVGPathDataAndOwner::const_iterator aStartDataEnd,
|
||||
SVGPathDataAndOwner::const_iterator aEnd,
|
||||
SVGPathDataAndOwner::const_iterator aEndDataEnd,
|
||||
SVGPathDataAndOwner::iterator aResult)
|
||||
{
|
||||
SVGPathTraversalState state;
|
||||
state.mode = SVGPathTraversalState::eUpdateOnlyStartAndCurrentPos;
|
||||
while (aStart < aStartDataEnd && aEnd < aEndDataEnd) {
|
||||
ConvertPathSegmentData(aStart, aEnd, aResult, state);
|
||||
}
|
||||
NS_ABORT_IF_FALSE(aStart == aStartDataEnd && aEnd == aEndDataEnd,
|
||||
"Failed to convert all path segment data! (Corrupt?)");
|
||||
}
|
||||
|
||||
nsresult
|
||||
SVGPathSegListSMILType::Add(nsSMILValue& aDest,
|
||||
const nsSMILValue& aValueToAdd,
|
||||
@ -102,36 +336,33 @@ SVGPathSegListSMILType::Add(nsSMILValue& aDest,
|
||||
const SVGPathDataAndOwner& valueToAdd =
|
||||
*static_cast<const SVGPathDataAndOwner*>(aValueToAdd.mU.mPtr);
|
||||
|
||||
if (dest.Length() != valueToAdd.Length()) {
|
||||
// Allow addition to empty dest:
|
||||
if (dest.Length() == 0) {
|
||||
return dest.CopyFrom(valueToAdd);
|
||||
}
|
||||
// For now we only support animation to a list with the same number of
|
||||
// items (and with the same segment types).
|
||||
// nsSVGUtils::ReportToConsole
|
||||
// Allow addition to empty dest.
|
||||
if (dest.IsEmpty()) {
|
||||
return dest.CopyFrom(valueToAdd);
|
||||
}
|
||||
|
||||
PathInterpolationResult check = CanInterpolate(dest, valueToAdd);
|
||||
|
||||
if (check == eCannotInterpolate) {
|
||||
// nsSVGUtils::ReportToConsole - can't add path segment lists with different
|
||||
// numbers of segments, with arcs with different flag values, or with
|
||||
// incompatible segment types.
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (check == eRequiresConversion) {
|
||||
ConvertAllPathSegmentData(dest.begin(), dest.end(),
|
||||
valueToAdd.begin(), valueToAdd.end(),
|
||||
dest.begin());
|
||||
}
|
||||
|
||||
PRUint32 i = 0;
|
||||
while (i < dest.Length()) {
|
||||
PRUint32 type = SVGPathSegUtils::DecodeType(dest[i]);
|
||||
if (type != SVGPathSegUtils::DecodeType(valueToAdd[i])) {
|
||||
// nsSVGUtils::ReportToConsole - can't yet animate between different
|
||||
// types, although it would make sense to allow animation between
|
||||
// some.
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
i++;
|
||||
if ((type == nsIDOMSVGPathSeg::PATHSEG_ARC_ABS ||
|
||||
type == nsIDOMSVGPathSeg::PATHSEG_ARC_REL) &&
|
||||
(dest[i+3] != valueToAdd[i+3] || dest[i+4] != valueToAdd[i+4])) {
|
||||
// boolean args largeArcFlag and sweepFlag must be the same
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
PRUint32 segEnd = i + SVGPathSegUtils::ArgCountForType(type);
|
||||
for (; i < segEnd; ++i) {
|
||||
dest[i] += valueToAdd[i];
|
||||
dest[i] += valueToAdd[i] * aCount;
|
||||
}
|
||||
}
|
||||
|
||||
@ -175,10 +406,12 @@ SVGPathSegListSMILType::Interpolate(const nsSMILValue& aStartVal,
|
||||
SVGPathDataAndOwner& result =
|
||||
*static_cast<SVGPathDataAndOwner*>(aResult.mU.mPtr);
|
||||
|
||||
if (start.Length() != end.Length() && start.Length() != 0) {
|
||||
// For now we only support animation to a list with the same number of
|
||||
// items (and with the same segment types).
|
||||
// nsSVGUtils::ReportToConsole
|
||||
PathInterpolationResult check = CanInterpolate(start, end);
|
||||
|
||||
if (check == eCannotInterpolate) {
|
||||
// nsSVGUtils::ReportToConsole - can't interpolate path segment lists with
|
||||
// different numbers of segments, with arcs with different flag values, or
|
||||
// with incompatible segment types.
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
@ -186,16 +419,14 @@ SVGPathSegListSMILType::Interpolate(const nsSMILValue& aStartVal,
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
PRUint32 i = 0;
|
||||
|
||||
if (start.Length() == 0) { // identity path
|
||||
if (start.IsEmpty()) { // identity path
|
||||
PRUint32 i = 0;
|
||||
while (i < end.Length()) {
|
||||
PRUint32 type = SVGPathSegUtils::DecodeType(end[i]);
|
||||
result[i] = end[i];
|
||||
i++;
|
||||
PRUint32 segEnd = i + SVGPathSegUtils::ArgCountForType(type);
|
||||
if ((type == nsIDOMSVGPathSeg::PATHSEG_ARC_ABS ||
|
||||
type == nsIDOMSVGPathSeg::PATHSEG_ARC_REL)) {
|
||||
if (SVGPathSegUtils::IsArcType(type)) {
|
||||
result[i] = end[i] * aUnitDistance;
|
||||
result[i+1] = end[i+1] * aUnitDistance;
|
||||
result[i+2] = end[i+2] * aUnitDistance;
|
||||
@ -211,32 +442,30 @@ SVGPathSegListSMILType::Interpolate(const nsSMILValue& aStartVal,
|
||||
}
|
||||
}
|
||||
}
|
||||
NS_ABORT_IF_FALSE(i == end.Length() && i == result.Length(),
|
||||
"Very, very bad - path data corrupt");
|
||||
} else {
|
||||
while (i < end.Length()) {
|
||||
PRUint32 type = SVGPathSegUtils::DecodeType(end[i]);
|
||||
if (type != SVGPathSegUtils::DecodeType(start[i])) {
|
||||
// nsSVGUtils::ReportToConsole - can't yet interpolate between different
|
||||
// types, although it would make sense to allow interpolation between
|
||||
// some.
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
result[i] = end[i];
|
||||
i++;
|
||||
if ((type == nsIDOMSVGPathSeg::PATHSEG_ARC_ABS ||
|
||||
type == nsIDOMSVGPathSeg::PATHSEG_ARC_REL) &&
|
||||
(start[i+3] != end[i+3] || start[i+4] != end[i+4])) {
|
||||
// boolean args largeArcFlag and sweepFlag must be the same
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
PRUint32 segEnd = i + SVGPathSegUtils::ArgCountForType(type);
|
||||
for (; i < segEnd; ++i) {
|
||||
result[i] = start[i] + (end[i] - start[i]) * aUnitDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
SVGPathDataAndOwner::const_iterator pStart = start.begin();
|
||||
SVGPathDataAndOwner::const_iterator pStartDataEnd = start.end();
|
||||
SVGPathDataAndOwner::const_iterator pEnd = end.begin();
|
||||
SVGPathDataAndOwner::const_iterator pEndDataEnd = end.end();
|
||||
SVGPathDataAndOwner::iterator pResult = result.begin();
|
||||
|
||||
NS_ABORT_IF_FALSE(i == end.Length(), "Very, very bad - path data corrupt");
|
||||
if (check == eRequiresConversion) {
|
||||
ConvertAllPathSegmentData(pStart, pStartDataEnd, pEnd, pEndDataEnd,
|
||||
pResult);
|
||||
pStart = pResult;
|
||||
pStartDataEnd = result.end();
|
||||
}
|
||||
|
||||
while (pStart != pStartDataEnd && pEnd != pEndDataEnd) {
|
||||
InterpolatePathSegmentData(pStart, pEnd, pResult, aUnitDistance);
|
||||
}
|
||||
|
||||
NS_ABORT_IF_FALSE(pStart == pStartDataEnd && pEnd == pEndDataEnd &&
|
||||
pResult == result.end(),
|
||||
"Very, very bad - path data corrupt");
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -54,17 +54,18 @@ static const PRUint32 MAX_RECURSION = 10;
|
||||
|
||||
|
||||
/* static */ void
|
||||
SVGPathSegUtils::GetValueAsString(const float *aSeg, nsAString& aValue)
|
||||
SVGPathSegUtils::GetValueAsString(const float* aSeg, nsAString& aValue)
|
||||
{
|
||||
// Adding new seg type? Is the formatting below acceptable for the new types?
|
||||
PR_STATIC_ASSERT(NS_SVG_PATH_SEG_LAST_VALID_TYPE ==
|
||||
nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL);
|
||||
PR_STATIC_ASSERT(NS_SVG_PATH_SEG_MAX_ARGS == 7);
|
||||
|
||||
PRUint32 type = DecodeType(aSeg[0]);
|
||||
PRUnichar typeAsChar = GetPathSegTypeAsLetter(type);
|
||||
|
||||
// Special case arcs:
|
||||
if (type == nsIDOMSVGPathSeg::PATHSEG_ARC_ABS ||
|
||||
type == nsIDOMSVGPathSeg::PATHSEG_ARC_REL) {
|
||||
if (IsArcType(type)) {
|
||||
PRBool largeArcFlag = aSeg[4] != 0.0f;
|
||||
PRBool sweepFlag = aSeg[5] != 0.0f;
|
||||
nsTextFormatter::ssprintf(aValue,
|
||||
@ -119,343 +120,366 @@ SVGPathSegUtils::GetValueAsString(const float *aSeg, nsAString& aValue)
|
||||
|
||||
|
||||
static float
|
||||
CalcDistanceBetweenPoints(const gfxPoint &p1, const gfxPoint &p2)
|
||||
CalcDistanceBetweenPoints(const gfxPoint& aP1, const gfxPoint& aP2)
|
||||
{
|
||||
return NS_hypot(p2.x - p1.x, p2.y - p1.y);
|
||||
return NS_hypot(aP2.x - aP1.x, aP2.y - aP1.y);
|
||||
}
|
||||
|
||||
|
||||
static void SplitQuadraticBezier(const gfxPoint *curve,
|
||||
gfxPoint *left,
|
||||
gfxPoint *right)
|
||||
static void
|
||||
SplitQuadraticBezier(const gfxPoint* aCurve, gfxPoint* aLeft, gfxPoint* aRight)
|
||||
{
|
||||
left[0].x = curve[0].x;
|
||||
left[0].y = curve[0].y;
|
||||
right[2].x = curve[2].x;
|
||||
right[2].y = curve[2].y;
|
||||
left[1].x = (curve[0].x + curve[1].x) / 2;
|
||||
left[1].y = (curve[0].y + curve[1].y) / 2;
|
||||
right[1].x = (curve[1].x + curve[2].x) / 2;
|
||||
right[1].y = (curve[1].y + curve[2].y) / 2;
|
||||
left[2].x = right[0].x = (left[1].x + right[1].x) / 2;
|
||||
left[2].y = right[0].y = (left[1].y + right[1].y) / 2;
|
||||
aLeft[0].x = aCurve[0].x;
|
||||
aLeft[0].y = aCurve[0].y;
|
||||
aRight[2].x = aCurve[2].x;
|
||||
aRight[2].y = aCurve[2].y;
|
||||
aLeft[1].x = (aCurve[0].x + aCurve[1].x) / 2;
|
||||
aLeft[1].y = (aCurve[0].y + aCurve[1].y) / 2;
|
||||
aRight[1].x = (aCurve[1].x + aCurve[2].x) / 2;
|
||||
aRight[1].y = (aCurve[1].y + aCurve[2].y) / 2;
|
||||
aLeft[2].x = aRight[0].x = (aLeft[1].x + aRight[1].x) / 2;
|
||||
aLeft[2].y = aRight[0].y = (aLeft[1].y + aRight[1].y) / 2;
|
||||
}
|
||||
|
||||
static void SplitCubicBezier(const gfxPoint *curve,
|
||||
gfxPoint *left,
|
||||
gfxPoint *right)
|
||||
static void
|
||||
SplitCubicBezier(const gfxPoint* aCurve, gfxPoint* aLeft, gfxPoint* aRight)
|
||||
{
|
||||
gfxPoint tmp;
|
||||
tmp.x = (curve[1].x + curve[2].x) / 4;
|
||||
tmp.y = (curve[1].y + curve[2].y) / 4;
|
||||
left[0].x = curve[0].x;
|
||||
left[0].y = curve[0].y;
|
||||
right[3].x = curve[3].x;
|
||||
right[3].y = curve[3].y;
|
||||
left[1].x = (curve[0].x + curve[1].x) / 2;
|
||||
left[1].y = (curve[0].y + curve[1].y) / 2;
|
||||
right[2].x = (curve[2].x + curve[3].x) / 2;
|
||||
right[2].y = (curve[2].y + curve[3].y) / 2;
|
||||
left[2].x = left[1].x / 2 + tmp.x;
|
||||
left[2].y = left[1].y / 2 + tmp.y;
|
||||
right[1].x = right[2].x / 2 + tmp.x;
|
||||
right[1].y = right[2].y / 2 + tmp.y;
|
||||
left[3].x = right[0].x = (left[2].x + right[1].x) / 2;
|
||||
left[3].y = right[0].y = (left[2].y + right[1].y) / 2;
|
||||
tmp.x = (aCurve[1].x + aCurve[2].x) / 4;
|
||||
tmp.y = (aCurve[1].y + aCurve[2].y) / 4;
|
||||
aLeft[0].x = aCurve[0].x;
|
||||
aLeft[0].y = aCurve[0].y;
|
||||
aRight[3].x = aCurve[3].x;
|
||||
aRight[3].y = aCurve[3].y;
|
||||
aLeft[1].x = (aCurve[0].x + aCurve[1].x) / 2;
|
||||
aLeft[1].y = (aCurve[0].y + aCurve[1].y) / 2;
|
||||
aRight[2].x = (aCurve[2].x + aCurve[3].x) / 2;
|
||||
aRight[2].y = (aCurve[2].y + aCurve[3].y) / 2;
|
||||
aLeft[2].x = aLeft[1].x / 2 + tmp.x;
|
||||
aLeft[2].y = aLeft[1].y / 2 + tmp.y;
|
||||
aRight[1].x = aRight[2].x / 2 + tmp.x;
|
||||
aRight[1].y = aRight[2].y / 2 + tmp.y;
|
||||
aLeft[3].x = aRight[0].x = (aLeft[2].x + aRight[1].x) / 2;
|
||||
aLeft[3].y = aRight[0].y = (aLeft[2].y + aRight[1].y) / 2;
|
||||
}
|
||||
|
||||
static gfxFloat CalcBezLengthHelper(gfxPoint *curve, PRUint32 numPts,
|
||||
PRUint32 recursion_count,
|
||||
void (*split)(const gfxPoint*, gfxPoint*, gfxPoint*))
|
||||
static gfxFloat
|
||||
CalcBezLengthHelper(gfxPoint* aCurve, PRUint32 aNumPts,
|
||||
PRUint32 aRecursionCount,
|
||||
void (*aSplit)(const gfxPoint*, gfxPoint*, gfxPoint*))
|
||||
{
|
||||
gfxPoint left[4];
|
||||
gfxPoint right[4];
|
||||
gfxFloat length = 0, dist;
|
||||
for (PRUint32 i = 0; i < numPts - 1; i++) {
|
||||
length += CalcDistanceBetweenPoints(curve[i], curve[i+1]);
|
||||
for (PRUint32 i = 0; i < aNumPts - 1; i++) {
|
||||
length += CalcDistanceBetweenPoints(aCurve[i], aCurve[i+1]);
|
||||
}
|
||||
dist = CalcDistanceBetweenPoints(curve[0], curve[numPts - 1]);
|
||||
if (length - dist > PATH_SEG_LENGTH_TOLERANCE && recursion_count < MAX_RECURSION) {
|
||||
split(curve, left, right);
|
||||
++recursion_count;
|
||||
return CalcBezLengthHelper(left, numPts, recursion_count, split) +
|
||||
CalcBezLengthHelper(right, numPts, recursion_count, split);
|
||||
dist = CalcDistanceBetweenPoints(aCurve[0], aCurve[aNumPts - 1]);
|
||||
if (length - dist > PATH_SEG_LENGTH_TOLERANCE &&
|
||||
aRecursionCount < MAX_RECURSION) {
|
||||
aSplit(aCurve, left, right);
|
||||
++aRecursionCount;
|
||||
return CalcBezLengthHelper(left, aNumPts, aRecursionCount, aSplit) +
|
||||
CalcBezLengthHelper(right, aNumPts, aRecursionCount, aSplit);
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
static inline gfxFloat
|
||||
CalcLengthOfCubicBezier(const gfxPoint &pos, const gfxPoint &cp1,
|
||||
const gfxPoint &cp2, const gfxPoint &to)
|
||||
CalcLengthOfCubicBezier(const gfxPoint& aPos, const gfxPoint &aCP1,
|
||||
const gfxPoint& aCP2, const gfxPoint &aTo)
|
||||
{
|
||||
gfxPoint curve[4] = { pos, cp1, cp2, to };
|
||||
gfxPoint curve[4] = { aPos, aCP1, aCP2, aTo };
|
||||
return CalcBezLengthHelper(curve, 4, 0, SplitCubicBezier);
|
||||
}
|
||||
|
||||
static inline gfxFloat
|
||||
CalcLengthOfQuadraticBezier(const gfxPoint &pos, const gfxPoint &cp,
|
||||
const gfxPoint &to)
|
||||
CalcLengthOfQuadraticBezier(const gfxPoint& aPos, const gfxPoint& aCP,
|
||||
const gfxPoint& aTo)
|
||||
{
|
||||
gfxPoint curve[3] = { pos, cp, to };
|
||||
gfxPoint curve[3] = { aPos, aCP, aTo };
|
||||
return CalcBezLengthHelper(curve, 3, 0, SplitQuadraticBezier);
|
||||
}
|
||||
|
||||
|
||||
static float GetLengthOfClosePath(const float *aArgs, SVGPathTraversalState &aState)
|
||||
static void
|
||||
TraverseClosePath(const float* aArgs, SVGPathTraversalState& aState)
|
||||
{
|
||||
float dist = CalcDistanceBetweenPoints(aState.pos, aState.start);
|
||||
aState.pos = aState.cp1 = aState.cp2 = aState.start;
|
||||
return dist;
|
||||
if (aState.ShouldUpdateLengthAndControlPoints()) {
|
||||
aState.length += CalcDistanceBetweenPoints(aState.pos, aState.start);
|
||||
aState.cp1 = aState.cp2 = aState.start;
|
||||
}
|
||||
aState.pos = aState.start;
|
||||
}
|
||||
|
||||
static float GetLengthOfMovetoAbs(const float *aArgs, SVGPathTraversalState &aState)
|
||||
static void
|
||||
TraverseMovetoAbs(const float* aArgs, SVGPathTraversalState& aState)
|
||||
{
|
||||
aState.start = aState.pos = aState.cp1 = aState.cp2 = gfxPoint(aArgs[0], aArgs[1]);
|
||||
return 0.0;
|
||||
aState.start = aState.pos = gfxPoint(aArgs[0], aArgs[1]);
|
||||
if (aState.ShouldUpdateLengthAndControlPoints()) {
|
||||
// aState.length is unchanged, since move commands don't affect path length.
|
||||
aState.cp1 = aState.cp2 = aState.start;
|
||||
}
|
||||
}
|
||||
|
||||
static float GetLengthOfMovetoRel(const float *aArgs, SVGPathTraversalState &aState)
|
||||
static void
|
||||
TraverseMovetoRel(const float* aArgs, SVGPathTraversalState& aState)
|
||||
{
|
||||
// aState.pos must be second from right due to +=
|
||||
aState.start = aState.cp1 = aState.cp2 = aState.pos += gfxPoint(aArgs[0], aArgs[1]);
|
||||
return 0.0;
|
||||
aState.start = aState.pos += gfxPoint(aArgs[0], aArgs[1]);
|
||||
if (aState.ShouldUpdateLengthAndControlPoints()) {
|
||||
// aState.length is unchanged, since move commands don't affect path length.
|
||||
aState.cp1 = aState.cp2 = aState.start;
|
||||
}
|
||||
}
|
||||
|
||||
static float GetLengthOfLinetoAbs(const float *aArgs, SVGPathTraversalState &aState)
|
||||
static void
|
||||
TraverseLinetoAbs(const float* aArgs, SVGPathTraversalState& aState)
|
||||
{
|
||||
gfxPoint to(aArgs[0], aArgs[1]);
|
||||
float dist = CalcDistanceBetweenPoints(aState.pos, to);
|
||||
aState.pos = aState.cp1 = aState.cp2 = to;
|
||||
return dist;
|
||||
if (aState.ShouldUpdateLengthAndControlPoints()) {
|
||||
aState.length += CalcDistanceBetweenPoints(aState.pos, to);
|
||||
aState.cp1 = aState.cp2 = to;
|
||||
}
|
||||
aState.pos = to;
|
||||
}
|
||||
|
||||
static float GetLengthOfLinetoRel(const float *aArgs, SVGPathTraversalState &aState)
|
||||
static void
|
||||
TraverseLinetoRel(const float* aArgs, SVGPathTraversalState& aState)
|
||||
{
|
||||
gfxPoint to = aState.pos + gfxPoint(aArgs[0], aArgs[1]);
|
||||
float dist = CalcDistanceBetweenPoints(aState.pos, to);
|
||||
aState.pos = aState.cp1 = aState.cp2 = to;
|
||||
return dist;
|
||||
if (aState.ShouldUpdateLengthAndControlPoints()) {
|
||||
aState.length += CalcDistanceBetweenPoints(aState.pos, to);
|
||||
aState.cp1 = aState.cp2 = to;
|
||||
}
|
||||
aState.pos = to;
|
||||
}
|
||||
|
||||
static float
|
||||
GetLengthOfLinetoHorizontalAbs(const float *aArgs, SVGPathTraversalState &aState)
|
||||
static void
|
||||
TraverseLinetoHorizontalAbs(const float* aArgs, SVGPathTraversalState& aState)
|
||||
{
|
||||
gfxPoint to(aArgs[0], aState.pos.y);
|
||||
float dist = fabs(to.x - aState.pos.x);
|
||||
aState.pos = aState.cp1 = aState.cp2 = to;
|
||||
return dist;
|
||||
if (aState.ShouldUpdateLengthAndControlPoints()) {
|
||||
aState.length += fabs(to.x - aState.pos.x);
|
||||
aState.cp1 = aState.cp2 = to;
|
||||
}
|
||||
aState.pos = to;
|
||||
}
|
||||
|
||||
static float
|
||||
GetLengthOfLinetoHorizontalRel(const float *aArgs, SVGPathTraversalState &aState)
|
||||
static void
|
||||
TraverseLinetoHorizontalRel(const float* aArgs, SVGPathTraversalState& aState)
|
||||
{
|
||||
aState.cp1 = aState.cp2 = aState.pos += gfxPoint(aArgs[0], 0.0);
|
||||
return fabs(aArgs[0]);
|
||||
aState.pos.x += aArgs[0];
|
||||
if (aState.ShouldUpdateLengthAndControlPoints()) {
|
||||
aState.length += fabs(aArgs[0]);
|
||||
aState.cp1 = aState.cp2 = aState.pos;
|
||||
}
|
||||
}
|
||||
|
||||
static float
|
||||
GetLengthOfLinetoVerticalAbs(const float *aArgs, SVGPathTraversalState &aState)
|
||||
static void
|
||||
TraverseLinetoVerticalAbs(const float* aArgs, SVGPathTraversalState& aState)
|
||||
{
|
||||
gfxPoint to(aState.pos.x, aArgs[0]);
|
||||
float dist = fabs(to.y - aState.pos.y);
|
||||
aState.pos = aState.cp1 = aState.cp2 = to;
|
||||
return dist;
|
||||
if (aState.ShouldUpdateLengthAndControlPoints()) {
|
||||
aState.length += fabs(to.y - aState.pos.y);
|
||||
aState.cp1 = aState.cp2 = to;
|
||||
}
|
||||
aState.pos = to;
|
||||
}
|
||||
|
||||
static float
|
||||
GetLengthOfLinetoVerticalRel(const float *aArgs, SVGPathTraversalState &aState)
|
||||
static void
|
||||
TraverseLinetoVerticalRel(const float* aArgs, SVGPathTraversalState& aState)
|
||||
{
|
||||
aState.cp1 = aState.cp2 = aState.pos += gfxPoint(0.0, aArgs[0]);
|
||||
return fabs(aArgs[0]);
|
||||
aState.pos.y += aArgs[0];
|
||||
if (aState.ShouldUpdateLengthAndControlPoints()) {
|
||||
aState.length += fabs(aArgs[0]);
|
||||
aState.cp1 = aState.cp2 = aState.pos;
|
||||
}
|
||||
}
|
||||
|
||||
static float GetLengthOfCurvetoCubicAbs(const float *aArgs, SVGPathTraversalState &aState)
|
||||
static void
|
||||
TraverseCurvetoCubicAbs(const float* aArgs, SVGPathTraversalState& aState)
|
||||
{
|
||||
gfxPoint cp1(aArgs[0], aArgs[1]);
|
||||
gfxPoint cp2(aArgs[2], aArgs[3]);
|
||||
gfxPoint to(aArgs[4], aArgs[5]);
|
||||
|
||||
float dist = (float)CalcLengthOfCubicBezier(aState.pos, cp1, cp2, to);
|
||||
|
||||
aState.cp2 = cp2;
|
||||
aState.pos = aState.cp1 = to;
|
||||
|
||||
return dist;
|
||||
if (aState.ShouldUpdateLengthAndControlPoints()) {
|
||||
gfxPoint cp1(aArgs[0], aArgs[1]);
|
||||
gfxPoint cp2(aArgs[2], aArgs[3]);
|
||||
aState.length += (float)CalcLengthOfCubicBezier(aState.pos, cp1, cp2, to);
|
||||
aState.cp2 = cp2;
|
||||
aState.cp1 = to;
|
||||
}
|
||||
aState.pos = to;
|
||||
}
|
||||
|
||||
static float
|
||||
GetLengthOfCurvetoCubicSmoothAbs(const float *aArgs, SVGPathTraversalState &aState)
|
||||
static void
|
||||
TraverseCurvetoCubicSmoothAbs(const float* aArgs, SVGPathTraversalState& aState)
|
||||
{
|
||||
gfxPoint cp1 = aState.pos - (aState.cp2 - aState.pos);
|
||||
gfxPoint cp2(aArgs[0], aArgs[1]);
|
||||
gfxPoint to(aArgs[2], aArgs[3]);
|
||||
|
||||
float dist = (float)CalcLengthOfCubicBezier(aState.pos, cp1, cp2, to);
|
||||
|
||||
aState.cp2 = cp2;
|
||||
aState.pos = aState.cp1 = to;
|
||||
|
||||
return dist;
|
||||
if (aState.ShouldUpdateLengthAndControlPoints()) {
|
||||
gfxPoint cp1 = aState.pos - (aState.cp2 - aState.pos);
|
||||
gfxPoint cp2(aArgs[0], aArgs[1]);
|
||||
aState.length += (float)CalcLengthOfCubicBezier(aState.pos, cp1, cp2, to);
|
||||
aState.cp2 = cp2;
|
||||
aState.cp1 = to;
|
||||
}
|
||||
aState.pos = to;
|
||||
}
|
||||
|
||||
static float
|
||||
GetLengthOfCurvetoCubicRel(const float *aArgs, SVGPathTraversalState &aState)
|
||||
static void
|
||||
TraverseCurvetoCubicRel(const float* aArgs, SVGPathTraversalState& aState)
|
||||
{
|
||||
gfxPoint cp1 = aState.pos + gfxPoint(aArgs[0], aArgs[1]);
|
||||
gfxPoint cp2 = aState.pos + gfxPoint(aArgs[2], aArgs[3]);
|
||||
gfxPoint to = aState.pos + gfxPoint(aArgs[4], aArgs[5]);
|
||||
|
||||
float dist = (float)CalcLengthOfCubicBezier(aState.pos, cp1, cp2, to);
|
||||
|
||||
aState.cp2 = cp2;
|
||||
aState.pos = aState.cp1 = to;
|
||||
|
||||
return dist;
|
||||
gfxPoint to = aState.pos + gfxPoint(aArgs[4], aArgs[5]);
|
||||
if (aState.ShouldUpdateLengthAndControlPoints()) {
|
||||
gfxPoint cp1 = aState.pos + gfxPoint(aArgs[0], aArgs[1]);
|
||||
gfxPoint cp2 = aState.pos + gfxPoint(aArgs[2], aArgs[3]);
|
||||
aState.length += (float)CalcLengthOfCubicBezier(aState.pos, cp1, cp2, to);
|
||||
aState.cp2 = cp2;
|
||||
aState.cp1 = to;
|
||||
}
|
||||
aState.pos = to;
|
||||
}
|
||||
|
||||
static float
|
||||
GetLengthOfCurvetoCubicSmoothRel(const float *aArgs, SVGPathTraversalState &aState)
|
||||
static void
|
||||
TraverseCurvetoCubicSmoothRel(const float* aArgs, SVGPathTraversalState& aState)
|
||||
{
|
||||
gfxPoint cp1 = aState.pos - (aState.cp2 - aState.pos);
|
||||
gfxPoint cp2 = aState.pos + gfxPoint(aArgs[0], aArgs[1]);
|
||||
gfxPoint to = aState.pos + gfxPoint(aArgs[2], aArgs[3]);
|
||||
|
||||
float dist = (float)CalcLengthOfCubicBezier(aState.pos, cp1, cp2, to);
|
||||
|
||||
aState.cp2 = cp2;
|
||||
aState.pos = aState.cp1 = to;
|
||||
|
||||
return dist;
|
||||
}
|
||||
|
||||
static float
|
||||
GetLengthOfCurvetoQuadraticAbs(const float *aArgs, SVGPathTraversalState &aState)
|
||||
{
|
||||
gfxPoint cp(aArgs[0], aArgs[1]);
|
||||
gfxPoint to(aArgs[2], aArgs[3]);
|
||||
|
||||
float dist = (float)CalcLengthOfQuadraticBezier(aState.pos, cp, to);
|
||||
|
||||
aState.cp1 = cp;
|
||||
aState.pos = aState.cp2 = to;
|
||||
|
||||
return dist;
|
||||
}
|
||||
|
||||
static float
|
||||
GetLengthOfCurvetoQuadraticSmoothAbs(const float *aArgs, SVGPathTraversalState &aState)
|
||||
{
|
||||
gfxPoint cp = aState.pos - (aState.cp1 - aState.pos);
|
||||
gfxPoint to(aArgs[0], aArgs[1]);
|
||||
|
||||
float dist = (float)CalcLengthOfQuadraticBezier(aState.pos, cp, to);
|
||||
|
||||
aState.cp1 = cp;
|
||||
aState.pos = aState.cp2 = to;
|
||||
|
||||
return dist;
|
||||
}
|
||||
|
||||
static float
|
||||
GetLengthOfCurvetoQuadraticRel(const float *aArgs, SVGPathTraversalState &aState)
|
||||
{
|
||||
gfxPoint cp = aState.pos + gfxPoint(aArgs[0], aArgs[1]);
|
||||
gfxPoint to = aState.pos + gfxPoint(aArgs[2], aArgs[3]);
|
||||
|
||||
float dist = (float)CalcLengthOfQuadraticBezier(aState.pos, cp, to);
|
||||
|
||||
aState.cp1 = cp;
|
||||
aState.pos = aState.cp2 = to;
|
||||
|
||||
return dist;
|
||||
if (aState.ShouldUpdateLengthAndControlPoints()) {
|
||||
gfxPoint cp1 = aState.pos - (aState.cp2 - aState.pos);
|
||||
gfxPoint cp2 = aState.pos + gfxPoint(aArgs[0], aArgs[1]);
|
||||
aState.length += (float)CalcLengthOfCubicBezier(aState.pos, cp1, cp2, to);
|
||||
aState.cp2 = cp2;
|
||||
aState.cp1 = to;
|
||||
}
|
||||
aState.pos = to;
|
||||
}
|
||||
|
||||
static float
|
||||
GetLengthOfCurvetoQuadraticSmoothRel(const float *aArgs, SVGPathTraversalState &aState)
|
||||
static void
|
||||
TraverseCurvetoQuadraticAbs(const float* aArgs, SVGPathTraversalState& aState)
|
||||
{
|
||||
gfxPoint to(aArgs[2], aArgs[3]);
|
||||
if (aState.ShouldUpdateLengthAndControlPoints()) {
|
||||
gfxPoint cp(aArgs[0], aArgs[1]);
|
||||
aState.length += (float)CalcLengthOfQuadraticBezier(aState.pos, cp, to);
|
||||
aState.cp1 = cp;
|
||||
aState.cp2 = to;
|
||||
}
|
||||
aState.pos = to;
|
||||
}
|
||||
|
||||
static void
|
||||
TraverseCurvetoQuadraticSmoothAbs(const float* aArgs,
|
||||
SVGPathTraversalState& aState)
|
||||
{
|
||||
gfxPoint to(aArgs[0], aArgs[1]);
|
||||
if (aState.ShouldUpdateLengthAndControlPoints()) {
|
||||
gfxPoint cp = aState.pos - (aState.cp1 - aState.pos);
|
||||
aState.length += (float)CalcLengthOfQuadraticBezier(aState.pos, cp, to);
|
||||
aState.cp1 = cp;
|
||||
aState.cp2 = to;
|
||||
}
|
||||
aState.pos = to;
|
||||
}
|
||||
|
||||
static void
|
||||
TraverseCurvetoQuadraticRel(const float* aArgs, SVGPathTraversalState& aState)
|
||||
{
|
||||
gfxPoint to = aState.pos + gfxPoint(aArgs[2], aArgs[3]);
|
||||
if (aState.ShouldUpdateLengthAndControlPoints()) {
|
||||
gfxPoint cp = aState.pos + gfxPoint(aArgs[0], aArgs[1]);
|
||||
aState.length += (float)CalcLengthOfQuadraticBezier(aState.pos, cp, to);
|
||||
aState.cp1 = cp;
|
||||
aState.cp2 = to;
|
||||
}
|
||||
aState.pos = to;
|
||||
}
|
||||
|
||||
static void
|
||||
TraverseCurvetoQuadraticSmoothRel(const float* aArgs,
|
||||
SVGPathTraversalState& aState)
|
||||
{
|
||||
gfxPoint cp = aState.pos - (aState.cp1 - aState.pos);
|
||||
gfxPoint to = aState.pos + gfxPoint(aArgs[0], aArgs[1]);
|
||||
|
||||
float dist = (float)CalcLengthOfQuadraticBezier(aState.pos, cp, to);
|
||||
|
||||
aState.cp1 = cp;
|
||||
aState.pos = aState.cp2 = to;
|
||||
|
||||
return dist;
|
||||
if (aState.ShouldUpdateLengthAndControlPoints()) {
|
||||
gfxPoint cp = aState.pos - (aState.cp1 - aState.pos);
|
||||
aState.length += (float)CalcLengthOfQuadraticBezier(aState.pos, cp, to);
|
||||
aState.cp1 = cp;
|
||||
aState.cp2 = to;
|
||||
}
|
||||
aState.pos = to;
|
||||
}
|
||||
|
||||
static float
|
||||
GetLengthOfArcAbs(const float *aArgs, SVGPathTraversalState &aState)
|
||||
static void
|
||||
TraverseArcAbs(const float* aArgs, SVGPathTraversalState& aState)
|
||||
{
|
||||
gfxPoint radii(aArgs[0], aArgs[1]);
|
||||
gfxPoint to(aArgs[5], aArgs[6]);
|
||||
gfxPoint bez[4] = { aState.pos, gfxPoint(0,0), gfxPoint(0,0), gfxPoint(0,0) };
|
||||
nsSVGArcConverter converter(aState.pos, to, radii, aArgs[2],
|
||||
aArgs[3] != 0, aArgs[4] != 0);
|
||||
float dist = 0;
|
||||
while (converter.GetNextSegment(&bez[1], &bez[2], &bez[3]))
|
||||
{
|
||||
dist += CalcBezLengthHelper(bez, 4, 0, SplitCubicBezier);
|
||||
bez[0] = bez[3];
|
||||
if (aState.ShouldUpdateLengthAndControlPoints()) {
|
||||
float dist = 0;
|
||||
gfxPoint radii(aArgs[0], aArgs[1]);
|
||||
gfxPoint bez[4] = { aState.pos, gfxPoint(0, 0),
|
||||
gfxPoint(0, 0), gfxPoint(0, 0) };
|
||||
nsSVGArcConverter converter(aState.pos, to, radii, aArgs[2],
|
||||
aArgs[3] != 0, aArgs[4] != 0);
|
||||
while (converter.GetNextSegment(&bez[1], &bez[2], &bez[3])) {
|
||||
dist += CalcBezLengthHelper(bez, 4, 0, SplitCubicBezier);
|
||||
bez[0] = bez[3];
|
||||
}
|
||||
aState.length += dist;
|
||||
aState.cp1 = aState.cp2 = to;
|
||||
}
|
||||
aState.pos = aState.cp1 = aState.cp2 = to;
|
||||
return dist;
|
||||
aState.pos = to;
|
||||
}
|
||||
|
||||
static float
|
||||
GetLengthOfArcRel(const float *aArgs, SVGPathTraversalState &aState)
|
||||
static void
|
||||
TraverseArcRel(const float* aArgs, SVGPathTraversalState& aState)
|
||||
{
|
||||
gfxPoint radii(aArgs[0], aArgs[1]);
|
||||
gfxPoint to = aState.pos + gfxPoint(aArgs[5], aArgs[6]);
|
||||
gfxPoint bez[4] = { aState.pos, gfxPoint(0,0), gfxPoint(0,0), gfxPoint(0,0) };
|
||||
nsSVGArcConverter converter(aState.pos, to, radii, aArgs[2],
|
||||
aArgs[3] != 0, aArgs[4] != 0);
|
||||
float dist = 0;
|
||||
while (converter.GetNextSegment(&bez[1], &bez[2], &bez[3]))
|
||||
{
|
||||
dist += CalcBezLengthHelper(bez, 4, 0, SplitCubicBezier);
|
||||
bez[0] = bez[3];
|
||||
if (aState.ShouldUpdateLengthAndControlPoints()) {
|
||||
float dist = 0;
|
||||
gfxPoint radii(aArgs[0], aArgs[1]);
|
||||
gfxPoint bez[4] = { aState.pos, gfxPoint(0, 0),
|
||||
gfxPoint(0, 0), gfxPoint(0, 0) };
|
||||
nsSVGArcConverter converter(aState.pos, to, radii, aArgs[2],
|
||||
aArgs[3] != 0, aArgs[4] != 0);
|
||||
while (converter.GetNextSegment(&bez[1], &bez[2], &bez[3])) {
|
||||
dist += CalcBezLengthHelper(bez, 4, 0, SplitCubicBezier);
|
||||
bez[0] = bez[3];
|
||||
}
|
||||
aState.length += dist;
|
||||
aState.cp1 = aState.cp2 = to;
|
||||
}
|
||||
aState.pos = aState.cp1 = aState.cp2 = to;
|
||||
return dist;
|
||||
aState.pos = to;
|
||||
}
|
||||
|
||||
|
||||
typedef float (*getLengthFunc)(const float*, SVGPathTraversalState&);
|
||||
typedef void (*TraverseFunc)(const float*, SVGPathTraversalState&);
|
||||
|
||||
/* static */ float
|
||||
SVGPathSegUtils::GetLength(const float *seg, SVGPathTraversalState &aState)
|
||||
static TraverseFunc gTraverseFuncTable[NS_SVG_PATH_SEG_TYPE_COUNT] = {
|
||||
nsnull, // 0 == PATHSEG_UNKNOWN
|
||||
TraverseClosePath,
|
||||
TraverseMovetoAbs,
|
||||
TraverseMovetoRel,
|
||||
TraverseLinetoAbs,
|
||||
TraverseLinetoRel,
|
||||
TraverseCurvetoCubicAbs,
|
||||
TraverseCurvetoCubicRel,
|
||||
TraverseCurvetoQuadraticAbs,
|
||||
TraverseCurvetoQuadraticRel,
|
||||
TraverseArcAbs,
|
||||
TraverseArcRel,
|
||||
TraverseLinetoHorizontalAbs,
|
||||
TraverseLinetoHorizontalRel,
|
||||
TraverseLinetoVerticalAbs,
|
||||
TraverseLinetoVerticalRel,
|
||||
TraverseCurvetoCubicSmoothAbs,
|
||||
TraverseCurvetoCubicSmoothRel,
|
||||
TraverseCurvetoQuadraticSmoothAbs,
|
||||
TraverseCurvetoQuadraticSmoothRel
|
||||
};
|
||||
|
||||
/* static */ void
|
||||
SVGPathSegUtils::TraversePathSegment(const float* aData,
|
||||
SVGPathTraversalState& aState)
|
||||
{
|
||||
PRUint32 type = DecodeType(seg[0]);
|
||||
|
||||
static getLengthFunc lengthFuncTable[20] = {
|
||||
nsnull, // 0 == PATHSEG_UNKNOWN
|
||||
GetLengthOfClosePath,
|
||||
GetLengthOfMovetoAbs,
|
||||
GetLengthOfMovetoRel,
|
||||
GetLengthOfLinetoAbs,
|
||||
GetLengthOfLinetoRel,
|
||||
GetLengthOfCurvetoCubicAbs,
|
||||
GetLengthOfCurvetoCubicRel,
|
||||
GetLengthOfCurvetoQuadraticAbs,
|
||||
GetLengthOfCurvetoQuadraticRel,
|
||||
GetLengthOfArcAbs,
|
||||
GetLengthOfArcRel,
|
||||
GetLengthOfLinetoHorizontalAbs,
|
||||
GetLengthOfLinetoHorizontalRel,
|
||||
GetLengthOfLinetoVerticalAbs,
|
||||
GetLengthOfLinetoVerticalRel,
|
||||
GetLengthOfCurvetoCubicSmoothAbs,
|
||||
GetLengthOfCurvetoCubicSmoothRel,
|
||||
GetLengthOfCurvetoQuadraticSmoothAbs,
|
||||
GetLengthOfCurvetoQuadraticSmoothRel
|
||||
};
|
||||
|
||||
NS_ABORT_IF_FALSE(IsValidType(type), "Seg type not recognized");
|
||||
|
||||
NS_ABORT_IF_FALSE(type > 0 && type < NS_ARRAY_LENGTH(lengthFuncTable),
|
||||
"Seg type not recognized");
|
||||
|
||||
return lengthFuncTable[type](seg + 1, aState);
|
||||
PR_STATIC_ASSERT(NS_ARRAY_LENGTH(gTraverseFuncTable) ==
|
||||
NS_SVG_PATH_SEG_TYPE_COUNT);
|
||||
PRUint32 type = DecodeType(aData[0]);
|
||||
gTraverseFuncTable[type](aData + 1, aState);
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,10 @@
|
||||
#include "nsContentUtils.h"
|
||||
#include "gfxPoint.h"
|
||||
|
||||
#define NS_SVG_PATH_SEG_MAX_ARGS 7
|
||||
#define NS_SVG_PATH_SEG_MAX_ARGS 7
|
||||
#define NS_SVG_PATH_SEG_FIRST_VALID_TYPE nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH
|
||||
#define NS_SVG_PATH_SEG_LAST_VALID_TYPE nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL
|
||||
#define NS_SVG_PATH_SEG_TYPE_COUNT (NS_SVG_PATH_SEG_LAST_VALID_TYPE + 1)
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -54,13 +57,22 @@ namespace mozilla {
|
||||
*/
|
||||
struct SVGPathTraversalState
|
||||
{
|
||||
enum TraversalMode {
|
||||
eUpdateAll,
|
||||
eUpdateOnlyStartAndCurrentPos
|
||||
};
|
||||
|
||||
SVGPathTraversalState()
|
||||
: start(0.0, 0.0)
|
||||
, pos(0.0, 0.0)
|
||||
, cp1(0.0, 0.0)
|
||||
, cp2(0.0, 0.0)
|
||||
, length(0.0)
|
||||
, mode(eUpdateAll)
|
||||
{}
|
||||
|
||||
PRBool ShouldUpdateLengthAndControlPoints() { return mode == eUpdateAll; }
|
||||
|
||||
gfxPoint start; // start point of current sub path (reset each moveto)
|
||||
|
||||
gfxPoint pos; // current position (end point of previous segment)
|
||||
@ -72,6 +84,10 @@ struct SVGPathTraversalState
|
||||
gfxPoint cp2; // cubic control point - if the previous segment was a cubic
|
||||
// bezier curve then this is set to the absolute position of
|
||||
// its second control point, otherwise it's set to pos
|
||||
|
||||
float length; // accumulated path length
|
||||
|
||||
TraversalMode mode; // indicates what to track while traversing a path
|
||||
};
|
||||
|
||||
|
||||
@ -144,6 +160,7 @@ public:
|
||||
PRUnichar('T'), // 18 == PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS
|
||||
PRUnichar('t') // 19 == PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL
|
||||
};
|
||||
PR_STATIC_ASSERT(NS_ARRAY_LENGTH(table) == NS_SVG_PATH_SEG_TYPE_COUNT);
|
||||
|
||||
return table[aType];
|
||||
}
|
||||
@ -173,6 +190,7 @@ public:
|
||||
2, // 18 == PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS
|
||||
2 // 19 == PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL
|
||||
};
|
||||
PR_STATIC_ASSERT(NS_ARRAY_LENGTH(table) == NS_SVG_PATH_SEG_TYPE_COUNT);
|
||||
|
||||
return table[aType];
|
||||
}
|
||||
@ -185,31 +203,81 @@ public:
|
||||
return ArgCountForType(DecodeType(aType));
|
||||
}
|
||||
|
||||
static inline PRBool IsValidType(PRUint32 aType) {
|
||||
return aType >= nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH &&
|
||||
aType <= nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL;
|
||||
static PRBool IsValidType(PRUint32 aType) {
|
||||
return aType >= NS_SVG_PATH_SEG_FIRST_VALID_TYPE &&
|
||||
aType <= NS_SVG_PATH_SEG_LAST_VALID_TYPE;
|
||||
}
|
||||
|
||||
static inline PRBool IsCubicType(PRUint32 aType) {
|
||||
static PRBool IsCubicType(PRUint32 aType) {
|
||||
return aType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL ||
|
||||
aType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS ||
|
||||
aType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL ||
|
||||
aType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS;
|
||||
}
|
||||
|
||||
static inline PRBool IsQuadraticType(PRUint32 aType) {
|
||||
static PRBool IsQuadraticType(PRUint32 aType) {
|
||||
return aType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL ||
|
||||
aType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS ||
|
||||
aType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL ||
|
||||
aType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user unit length of tracing along the path segment.
|
||||
*/
|
||||
static float GetLength(const float *aSeg, SVGPathTraversalState &aState);
|
||||
static PRBool IsArcType(PRUint32 aType) {
|
||||
return aType == nsIDOMSVGPathSeg::PATHSEG_ARC_ABS ||
|
||||
aType == nsIDOMSVGPathSeg::PATHSEG_ARC_REL;
|
||||
}
|
||||
|
||||
static void ToString(const float *aSeg, nsAString& aValue);
|
||||
static PRBool IsRelativeOrAbsoluteType(PRUint32 aType) {
|
||||
NS_ABORT_IF_FALSE(IsValidType(aType), "Seg type not recognized");
|
||||
|
||||
// When adding a new path segment type, ensure that the returned condition
|
||||
// below is still correct.
|
||||
PR_STATIC_ASSERT(NS_SVG_PATH_SEG_LAST_VALID_TYPE ==
|
||||
nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL);
|
||||
|
||||
return aType >= nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS;
|
||||
}
|
||||
|
||||
static PRBool IsRelativeType(PRUint32 aType) {
|
||||
NS_ABORT_IF_FALSE
|
||||
(IsRelativeOrAbsoluteType(aType),
|
||||
"IsRelativeType called with segment type that does not come in relative and absolute forms");
|
||||
|
||||
// When adding a new path segment type, ensure that the returned condition
|
||||
// below is still correct.
|
||||
PR_STATIC_ASSERT(NS_SVG_PATH_SEG_LAST_VALID_TYPE ==
|
||||
nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL);
|
||||
|
||||
return aType & 1;
|
||||
}
|
||||
|
||||
static PRUint32 RelativeVersionOfType(PRUint32 aType) {
|
||||
NS_ABORT_IF_FALSE
|
||||
(IsRelativeOrAbsoluteType(aType),
|
||||
"RelativeVersionOfType called with segment type that does not come in relative and absolute forms");
|
||||
|
||||
// When adding a new path segment type, ensure that the returned condition
|
||||
// below is still correct.
|
||||
PR_STATIC_ASSERT(NS_SVG_PATH_SEG_LAST_VALID_TYPE ==
|
||||
nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL);
|
||||
|
||||
return aType | 1;
|
||||
}
|
||||
|
||||
static PRUint32 SameTypeModuloRelativeness(PRUint32 aType1, PRUint32 aType2) {
|
||||
if (!IsRelativeOrAbsoluteType(aType1)) {
|
||||
return aType1 == aType2;
|
||||
}
|
||||
|
||||
return RelativeVersionOfType(aType1) == RelativeVersionOfType(aType2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverse the given path segment and update the SVGPathTraversalState
|
||||
* object.
|
||||
*/
|
||||
static void TraversePathSegment(const float* aData,
|
||||
SVGPathTraversalState& aState);
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -147,7 +147,8 @@ nsSVGAnimationElement::GetTargetElementContent()
|
||||
"if we don't have an xlink:href attribute");
|
||||
|
||||
// No "xlink:href" attribute --> I should target my parent.
|
||||
return nsSVGUtils::GetParentElement(this);
|
||||
nsIContent* parent = GetFlattenedTreeParent();
|
||||
return parent && parent->IsElement() ? parent->AsElement() : nsnull;
|
||||
}
|
||||
|
||||
PRBool
|
||||
|
@ -239,7 +239,7 @@ nsSVGElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
aCompileEventHandlers);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!HasFlag(NODE_MAY_HAVE_STYLE)) {
|
||||
if (!MayHaveStyle()) {
|
||||
return NS_OK;
|
||||
}
|
||||
const nsAttrValue* oldVal = mAttrsAndChildren.GetAttr(nsGkAtoms::style);
|
||||
@ -288,7 +288,7 @@ nsSVGElement::AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
|
||||
aName == nsGkAtoms::requiredExtensions ||
|
||||
aName == nsGkAtoms::systemLanguage)) {
|
||||
|
||||
nsIContent* parent = nsSVGUtils::GetParentElement(this);
|
||||
nsIContent* parent = GetFlattenedTreeParent();
|
||||
|
||||
if (parent &&
|
||||
parent->NodeInfo()->Equals(nsGkAtoms::svgSwitch, kNameSpaceID_SVG)) {
|
||||
@ -1411,7 +1411,7 @@ nsIAtom* nsSVGElement::GetEventNameForAttr(nsIAtom* aAttr)
|
||||
nsSVGSVGElement *
|
||||
nsSVGElement::GetCtx()
|
||||
{
|
||||
dom::Element* ancestor = nsSVGUtils::GetParentElement(this);
|
||||
nsIContent* ancestor = GetFlattenedTreeParent();
|
||||
|
||||
while (ancestor && ancestor->GetNameSpaceID() == kNameSpaceID_SVG) {
|
||||
nsIAtom* tag = ancestor->Tag();
|
||||
@ -1421,7 +1421,7 @@ nsSVGElement::GetCtx()
|
||||
if (tag == nsGkAtoms::svg) {
|
||||
return static_cast<nsSVGSVGElement*>(ancestor);
|
||||
}
|
||||
ancestor = nsSVGUtils::GetParentElement(ancestor);
|
||||
ancestor = ancestor->GetFlattenedTreeParent();
|
||||
}
|
||||
|
||||
// we don't have an ancestor <svg> element...
|
||||
|
@ -80,7 +80,7 @@ class SVGAnimatedPathSegList;
|
||||
class SVGAnimatedPreserveAspectRatio;
|
||||
}
|
||||
|
||||
typedef nsStyledElement nsSVGElementBase;
|
||||
typedef nsStyledElementNotElementCSSInlineStyle nsSVGElementBase;
|
||||
|
||||
class nsSVGElement : public nsSVGElementBase, // nsIContent
|
||||
public nsISVGValueObserver // :nsISupportsWeakReference
|
||||
|
@ -319,9 +319,9 @@ inline static void DidAnimateAttr(Element *aFilterPrimitive)
|
||||
inline static void DidAnimateAttrViaParent(Element *aFilterPrimitive) {
|
||||
// No frame, use parent's
|
||||
NS_ASSERTION(!aFilterPrimitive->GetPrimaryFrame(), "Not expecting a frame");
|
||||
Element *parent = nsSVGUtils::GetParentElement(aFilterPrimitive);
|
||||
if (parent) {
|
||||
DidAnimateAttr(parent);
|
||||
nsIContent *parent = aFilterPrimitive->GetFlattenedTreeParent();
|
||||
if (parent && parent->IsElement()) {
|
||||
DidAnimateAttr(parent->AsElement());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -272,7 +272,7 @@ protected:
|
||||
* another non-foreignObject SVG element.
|
||||
*/
|
||||
PRBool IsInner() {
|
||||
const mozilla::dom::Element *parent = nsSVGUtils::GetParentElement(this);
|
||||
const nsIContent *parent = GetFlattenedTreeParent();
|
||||
return parent && parent->GetNameSpaceID() == kNameSpaceID_SVG &&
|
||||
parent->Tag() != nsGkAtoms::foreignObject;
|
||||
}
|
||||
|
@ -78,6 +78,7 @@ public:
|
||||
|
||||
|
||||
// other methods:
|
||||
nsIDOMSVGTransform* GetItemWithoutAddRef(PRInt32 index);
|
||||
nsIDOMSVGTransform* ElementAt(PRInt32 index);
|
||||
PRBool AppendElement(nsIDOMSVGTransform* aElement);
|
||||
static already_AddRefed<nsIDOMSVGMatrix>
|
||||
|
@ -49,6 +49,12 @@ include $(topsrcdir)/config/rules.mk
|
||||
# test_length.xhtml \
|
||||
|
||||
_TEST_FILES = \
|
||||
test_a_href_01.xhtml \
|
||||
test_a_href_02.xhtml \
|
||||
a_href_destination.svg \
|
||||
a_href_helper_01.svg \
|
||||
a_href_helper_02_03.svg \
|
||||
a_href_helper_04.svg \
|
||||
test_animLengthObjectIdentity.xhtml \
|
||||
test_animLengthReadonly.xhtml \
|
||||
test_animLengthRelativeUnits.xhtml \
|
||||
@ -64,6 +70,7 @@ _TEST_FILES = \
|
||||
getSubStringLength-helper.svg \
|
||||
test_isSupported.xhtml \
|
||||
test_nonAnimStrings.xhtml \
|
||||
test_pathAnimInterpolation.xhtml \
|
||||
test_pathSeg.xhtml \
|
||||
test_pointer-events.xhtml \
|
||||
test_pointer-events-2.xhtml \
|
||||
@ -77,6 +84,7 @@ _TEST_FILES = \
|
||||
test_SVGPathSegList.xhtml \
|
||||
test_SVGStyleElement.xhtml \
|
||||
test_SVGxxxList.xhtml \
|
||||
test_SVGxxxListIndexing.xhtml \
|
||||
test_switch.xhtml \
|
||||
switch-helper.svg \
|
||||
test_text.html \
|
||||
@ -88,12 +96,6 @@ _TEST_FILES = \
|
||||
test_viewport.html \
|
||||
zoom-helper.svg \
|
||||
test_zoom.xhtml \
|
||||
test_a_href_01.xhtml \
|
||||
test_a_href_02.xhtml \
|
||||
a_href_destination.svg \
|
||||
a_href_helper_01.svg \
|
||||
a_href_helper_02_03.svg \
|
||||
a_href_helper_04.svg \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_TEST_FILES)
|
||||
|
83
content/svg/content/test/test_SVGxxxListIndexing.xhtml
Normal file
83
content/svg/content/test/test_SVGxxxListIndexing.xhtml
Normal file
@ -0,0 +1,83 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=631437
|
||||
-->
|
||||
<head>
|
||||
<title>Tests the array indexing and .length on SVGXXXList objects</title>
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=631437">Mozilla Bug 631437</a>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="svg">
|
||||
<text id="text" x="10 20 30" rotate="40 50 60">abcde</text>
|
||||
<path id="path" d="M0,0 L100,100"/>
|
||||
<polygon id="poly" points="50,50 70,70 90,50"/>
|
||||
</svg>
|
||||
<script type="text/javascript;version=1.8"><![CDATA[
|
||||
var text = document.getElementById("text"),
|
||||
path = document.getElementById("path"),
|
||||
poly = document.getElementById("poly");
|
||||
|
||||
function CheckList(aListObject, aExpectedListLength, aListDescription)
|
||||
{
|
||||
is(aListObject.numberOfItems, aExpectedListLength, aListDescription + ".numberOfItems");
|
||||
is(aListObject.numberOfItems, aExpectedListLength, aListDescription + ".length");
|
||||
for (let i = 0; i < aListObject.length; i++) {
|
||||
let item = aListObject.getItem(i);
|
||||
ok(aListObject[i] === item, aListDescription + "[" + i + "]");
|
||||
}
|
||||
ok(aListObject[aListObject.length] === void 0, aListDescription + "[outOfBounds]");
|
||||
}
|
||||
|
||||
var tests = [
|
||||
{ element: text,
|
||||
attribute: "x",
|
||||
listProperty: "x.baseVal",
|
||||
type: "SVGLengthList",
|
||||
subtests: [ { values: null, length: 3 },
|
||||
{ values: "40", length: 1 },
|
||||
{ values: "1em 2em 3em 4em 5em", length: 5 } ] },
|
||||
{ element: text,
|
||||
attribute: "rotate",
|
||||
listProperty: "rotate.baseVal",
|
||||
type: "SVGNumberList",
|
||||
subtests: [ { values: null, length: 3 },
|
||||
{ values: "10", length: 1 },
|
||||
{ values: "1 2 3 4 5", length: 5 } ] },
|
||||
{ element: path,
|
||||
attribute: "d",
|
||||
listProperty: "pathSegList",
|
||||
type: "SVGPathSegList",
|
||||
subtests: [ { values: null, length: 2 },
|
||||
{ values: "M50,50", length: 1 },
|
||||
{ values: "M0,0 h10 v20 h30 v40", length: 5 } ] },
|
||||
{ element: poly,
|
||||
attribute: "points",
|
||||
listProperty: "animatedPoints",
|
||||
type: "SVGPointList",
|
||||
subtests: [ { values: null, length: 3 },
|
||||
{ values: "100,100", length: 1 },
|
||||
{ values: "0,0 10,10 20,0 30,10 40,0", length: 5 } ] }
|
||||
];
|
||||
|
||||
for each (let test in tests) {
|
||||
let list = test.element;
|
||||
for each (let property in test.listProperty.split(".")) {
|
||||
list = list[property];
|
||||
}
|
||||
|
||||
for each (let subtest in test.subtests) {
|
||||
if (subtest.values) {
|
||||
test.element.setAttribute(test.attribute, subtest.values);
|
||||
}
|
||||
|
||||
CheckList(list, subtest.length,
|
||||
test.type + ": " + test.element.localName + "." +
|
||||
test.listProperty);
|
||||
}
|
||||
}
|
||||
]]></script>
|
||||
</body>
|
||||
</html>
|
345
content/svg/content/test/test_pathAnimInterpolation.xhtml
Normal file
345
content/svg/content/test/test_pathAnimInterpolation.xhtml
Normal file
@ -0,0 +1,345 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=619498
|
||||
-->
|
||||
<head>
|
||||
<title>Test interpolation between different path segment types</title>
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=619498">Mozilla Bug 619498</a>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="svg" visibility="hidden"
|
||||
onload="this.pauseAnimations()"/>
|
||||
<script type="application/javascript;version=1.8"><![CDATA[
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var gSVG = document.getElementById("svg");
|
||||
|
||||
// Array of all subtests to run. This is populated by addTest.
|
||||
var gTests = [];
|
||||
|
||||
// Array of all path segment types.
|
||||
var gTypes = "zMmLlCcQqAaHhVvSsTt".split("");
|
||||
|
||||
// Property names on the SVGPathSeg objects for the given segment type, in the
|
||||
// order that they would appear in a path data string.
|
||||
var gArgumentNames = {
|
||||
Z: [],
|
||||
M: ['x', 'y'],
|
||||
L: ['x', 'y'],
|
||||
C: ['x1', 'y1', 'x2', 'y2', 'x', 'y'],
|
||||
Q: ['x1', 'y1', 'x', 'y'],
|
||||
A: ['r1', 'r2', 'angle', 'largeArcFlag', 'sweepFlag', 'x', 'y'],
|
||||
H: ['x'],
|
||||
V: ['y'],
|
||||
S: ['x2', 'y2', 'x', 'y'],
|
||||
T: ['x', 'y']
|
||||
};
|
||||
|
||||
// All of these prefixes leave the current point at 100,100. Some of them
|
||||
// affect the implied control point if followed by a smooth quadratic or
|
||||
// cubic segment, but no valid interpolations depend on those control points.
|
||||
var gPrefixes = [
|
||||
[1, "M100,100"],
|
||||
[2, "M50,50 M100,100"],
|
||||
[2, "M50,50 m50,50"],
|
||||
[2, "M50,50 L100,100"],
|
||||
[2, "M50,50 l50,50"],
|
||||
[3, "M50,50 H100 V100"],
|
||||
[3, "M50,50 h50 V100"],
|
||||
[3, "M50,50 H100 v50"],
|
||||
[2, "M50,50 A10,10,10,0,0,100,100"],
|
||||
[2, "M50,50 a10,10,10,0,0,50,50"],
|
||||
[4, "M50,50 l50,50 z m50,50"],
|
||||
|
||||
// These leave the quadratic implied control point at 125,125.
|
||||
[2, "M50,50 Q75,75,100,100"],
|
||||
[2, "M50,50 q25,25,50,50"],
|
||||
[2, "M75,75 T100,100"],
|
||||
[2, "M75,75 t25,25"],
|
||||
[3, "M50,50 T62.5,62.5 t37.5,37.5"],
|
||||
[3, "M50,50 T62.5,62.5 T100,100"],
|
||||
[3, "M50,50 t12.5,12.5 t37.5,37.5"],
|
||||
[3, "M50,50 t12.5,12.5 T100,100"],
|
||||
[3, "M50,50 Q50,50,62.5,62.5 t37.5,37.5"],
|
||||
[3, "M50,50 Q50,50,62.5,62.5 T100,100"],
|
||||
[3, "M50,50 q0,0,12.5,12.5 t37.5,37.5"],
|
||||
[3, "M50,50 q0,0,12.5,12.5 T100,100"],
|
||||
|
||||
// These leave the cubic implied control point at 125,125.
|
||||
[2, "M50,50 C10,10,75,75,100,100"],
|
||||
[2, "M50,50 c10,10,25,25,50,50"],
|
||||
[2, "M50,50 S75,75,100,100"],
|
||||
[2, "M50,50 s25,25,50,50"],
|
||||
[3, "M50,50 S10,10,75,75 S75,75,100,100"],
|
||||
[3, "M50,50 S10,10,75,75 s0,0,25,25"],
|
||||
[3, "M50,50 s10,10,25,25 S75,75,100,100"],
|
||||
[3, "M50,50 s10,10,25,25 s0,0,25,25"],
|
||||
[3, "M50,50 C10,10,20,20,75,75 S75,75,100,100"],
|
||||
[3, "M50,50 C10,10,20,20,75,75 s0,0,25,25"],
|
||||
[3, "M50,50 c10,10,20,20,25,25 S75,75,100,100"],
|
||||
[3, "M50,50 c10,10,20,20,25,25 s0,0,25,25"]
|
||||
];
|
||||
|
||||
// These are all of the suffixes whose result is not dependent on whether the
|
||||
// preceding segment types are quadratic or cubic types. Each entry is:
|
||||
//
|
||||
// "<fromType><toType>": [fromArguments,
|
||||
// toArguments,
|
||||
// expectedArguments,
|
||||
// expectedArgumentsAdditive]
|
||||
//
|
||||
// As an example:
|
||||
//
|
||||
// "Mm": [[10, 20], [30, 40], [-30, -20], [-120, -100]]
|
||||
//
|
||||
// This will testing interpolating between "M10,20" and "m30,40". All of the
|
||||
// these tests assume that the current point is left at 100,100. So the above
|
||||
// entry represents two kinds of tests, one where additive and one not:
|
||||
//
|
||||
// <path d="... M10,20">
|
||||
// <animate attributeName="d" from="... M10,20" to="... m30,40"/>
|
||||
// </path>
|
||||
//
|
||||
// and
|
||||
//
|
||||
// <path d="... M10,20">
|
||||
// <animate attributeName="d" from="... M10,20" to="... m30,40"
|
||||
// additive="sum"/>
|
||||
// </path>
|
||||
//
|
||||
// where the "..." is some prefix that leaves the current point at 100,100.
|
||||
// Each of the suffixes here in gSuffixes will be paired with each of the
|
||||
// prefixes in gPrefixes, all of which leave the current point at 100,100.
|
||||
// (Thus the above two tests for interpolating between "M" and "m" will be
|
||||
// performed many times, with different preceding commands.)
|
||||
//
|
||||
// The expected result of the non-additive test is "m-30,-20". Since the
|
||||
// animation is from an absolute moveto to a relative moveto, we first
|
||||
// convert the "M10,20" into its relative form, which is "m-90,-80" due to the
|
||||
// current point being 100,100. Half way through the animation between
|
||||
// "m-90,-80" and "m30,40" is thus "m-30,-20".
|
||||
//
|
||||
// The expected result of the additive test is "m-120,-100". We take the
|
||||
// halfway value of the animation, "m-30,-20" and add it on to the underlying
|
||||
// value. Since the underlying value "M10,20" is an absolute moveto, we first
|
||||
// convert it to relative, "m-90,-80", and then add the "m-30,-20" to it,
|
||||
// giving us the result "m-120,-100".
|
||||
var gSuffixes = {
|
||||
// Same path segment type, no conversion required.
|
||||
MM: [[10, 20], [30, 40], [20, 30], [30, 50]],
|
||||
mm: [[10, 20], [30, 40], [20, 30], [30, 50]],
|
||||
LL: [[10, 20], [30, 40], [20, 30], [30, 50]],
|
||||
ll: [[10, 20], [30, 40], [20, 30], [30, 50]],
|
||||
CC: [[10, 20, 30, 40, 50, 60], [70, 80, 90, 100, 110, 120],
|
||||
[40, 50, 60, 70, 80, 90], [50, 70, 90, 110, 130, 150]],
|
||||
cc: [[10, 20, 30, 40, 50, 60], [70, 80, 90, 100, 110, 120],
|
||||
[40, 50, 60, 70, 80, 90], [50, 70, 90, 110, 130, 150]],
|
||||
QQ: [[10, 20, 30, 40], [50, 60, 70, 80], [30, 40, 50, 60], [40, 60, 80, 100]],
|
||||
qq: [[10, 20, 30, 40], [50, 60, 70, 80], [30, 40, 50, 60], [40, 60, 80, 100]],
|
||||
AA: [[10, 20, 30, 0, 0, 40, 50], [60, 70, 80, 0, 0, 90, 100],
|
||||
[35, 45, 55, 0, 0, 65, 75], [45, 65, 85, 0, 0, 105, 125]],
|
||||
aa: [[10, 20, 30, 0, 0, 40, 50], [60, 70, 80, 0, 0, 90, 100],
|
||||
[35, 45, 55, 0, 0, 65, 75], [45, 65, 85, 0, 0, 105, 125]],
|
||||
HH: [[10], [20], [15], [25]],
|
||||
hh: [[10], [20], [15], [25]],
|
||||
VV: [[10], [20], [15], [25]],
|
||||
vv: [[10], [20], [15], [25]],
|
||||
SS: [[10, 20, 30, 40], [50, 60, 70, 80], [30, 40, 50, 60], [40, 60, 80, 100]],
|
||||
ss: [[10, 20, 30, 40], [50, 60, 70, 80], [30, 40, 50, 60], [40, 60, 80, 100]],
|
||||
TT: [[10, 20], [30, 40], [20, 30], [30, 50]],
|
||||
tt: [[10, 20], [30, 40], [20, 30], [30, 50]],
|
||||
|
||||
// Relative <-> absolute conversion.
|
||||
Mm: [[10, 20], [30, 40], [-30, -20], [-120, -100]],
|
||||
mM: [[10, 20], [30, 40], [70, 80], [180, 200]],
|
||||
Ll: [[10, 20], [30, 40], [-30, -20], [-120, -100]],
|
||||
lL: [[10, 20], [30, 40], [70, 80], [180, 200]],
|
||||
Cc: [[10, 20, 30, 40, 50, 60], [70, 80, 90, 100, 110, 120],
|
||||
[-10, 0, 10, 20, 30, 40], [-100, -80, -60, -40, -20, 0]],
|
||||
cC: [[10, 20, 30, 40, 50, 60], [70, 80, 90, 100, 110, 120],
|
||||
[90, 100, 110, 120, 130, 140], [200, 220, 240, 260, 280, 300]],
|
||||
Qq: [[10, 20, 30, 40], [50, 60, 70, 80],
|
||||
[-20, -10, 0, 10], [-110, -90, -70, -50]],
|
||||
qQ: [[10, 20, 30, 40], [50, 60, 70, 80],
|
||||
[80, 90, 100, 110], [190, 210, 230, 250]],
|
||||
Aa: [[10, 20, 30, 0, 0, 40, 50], [60, 70, 80, 0, 0, 90, 100],
|
||||
[35, 45, 55, 0, 0, 15, 25], [45, 65, 85, 0, 0, -45, -25]],
|
||||
aA: [[10, 20, 30, 0, 0, 40, 50], [60, 70, 80, 0, 0, 90, 100],
|
||||
[35, 45, 55, 0, 0, 115, 125], [45, 65, 85, 0, 0, 255, 275]],
|
||||
Hh: [[10], [20], [-35], [-125]],
|
||||
hH: [[10], [20], [65], [175]],
|
||||
Vv: [[10], [20], [-35], [-125]],
|
||||
vV: [[10], [20], [65], [175]],
|
||||
Tt: [[10, 20], [30, 40], [-30,-20], [-120, -100]],
|
||||
tT: [[10, 20], [30, 40], [70, 80], [180, 200]],
|
||||
Ss: [[10, 20, 30, 40], [50, 60, 70, 80],
|
||||
[-20, -10, 0, 10], [-110, -90, -70, -50]],
|
||||
sS: [[10, 20, 30, 40], [50, 60, 70, 80],
|
||||
[80, 90, 100, 110], [190, 210, 230, 250]]
|
||||
};
|
||||
|
||||
// Returns an array of property names that exist on an SVGPathSeg object
|
||||
// corresponding to the given segment type, in the order that they would
|
||||
// be present in a path data string.
|
||||
function argumentNames(aType)
|
||||
{
|
||||
return gArgumentNames[aType.toUpperCase()];
|
||||
}
|
||||
|
||||
// Creates and returns a new element and sets some attributes on it.
|
||||
function newElement(aNamespaceURI, aLocalName, aAttributes)
|
||||
{
|
||||
var e = document.createElementNS(aNamespaceURI, aLocalName);
|
||||
if (aAttributes) {
|
||||
for (let [name, value] in Iterator(aAttributes)) {
|
||||
e.setAttribute(name, value);
|
||||
}
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
// Creates and returns a new SVG element and sets some attributes on it.
|
||||
function newSVGElement(aLocalName, aAttributes)
|
||||
{
|
||||
return newElement("http://www.w3.org/2000/svg", aLocalName, aAttributes);
|
||||
}
|
||||
|
||||
// Creates a subtest and adds it to the document.
|
||||
//
|
||||
// * aPrefixLength/aPrefix the prefix to use
|
||||
// * aFromType/aFromArguments the segment to interpolate from
|
||||
// * aToType/aToArguments the segment to interpolate to
|
||||
// * aExpectedType/aExpectedArguments the expected result of the interpolated
|
||||
// segment half way through the animation
|
||||
// duration
|
||||
// * aAdditive whether the subtest is for an additive
|
||||
// animation
|
||||
function addTest(aPrefixLength, aPrefix, aFromType, aFromArguments,
|
||||
aToType, aToArguments, aExpectedType, aExpectedArguments,
|
||||
aAdditive)
|
||||
{
|
||||
var fromPath = aPrefix + aFromType + aFromArguments,
|
||||
toPath = aPrefix + aToType + aToArguments;
|
||||
|
||||
var path = newSVGElement("path", { d: fromPath });
|
||||
var animate =
|
||||
newSVGElement("animate", { attributeName: "d",
|
||||
from: fromPath,
|
||||
to: toPath,
|
||||
dur: "8s",
|
||||
additive: aAdditive ? "sum" : "replace" });
|
||||
path.appendChild(animate);
|
||||
gSVG.appendChild(path);
|
||||
|
||||
gTests.push({ element: path,
|
||||
prefixLength: aPrefixLength,
|
||||
from: fromPath,
|
||||
to: toPath,
|
||||
toType: aToType,
|
||||
expectedType: aExpectedType,
|
||||
expected: aExpectedArguments,
|
||||
usesAddition: aAdditive });
|
||||
}
|
||||
|
||||
// Generates an array of path segment arguments for the given type. aOffset
|
||||
// is a number to add on to all non-Boolean segment arguments.
|
||||
function generatePathSegmentArguments(aType, aOffset)
|
||||
{
|
||||
var args = new Array(argumentNames(aType).length);
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
args[i] = i * 10 + aOffset;
|
||||
}
|
||||
if (aType == "A" || aType == "a") {
|
||||
args[3] = 0;
|
||||
args[4] = 0;
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
// Returns whether interpolating between the two given types is valid.
|
||||
function isValidInterpolation(aFromType, aToType)
|
||||
{
|
||||
return aFromType.toUpperCase() == aToType.toUpperCase();
|
||||
}
|
||||
|
||||
// Runs the test.
|
||||
function run()
|
||||
{
|
||||
for each (let additive in [false, true]) {
|
||||
let indexOfExpectedArguments = additive ? 3 : 2;
|
||||
|
||||
// Add subtests for each combination of prefix and suffix, and additive
|
||||
// or not.
|
||||
for (let [typePair, suffixEntry] in Iterator(gSuffixes)) {
|
||||
let fromType = typePair[0],
|
||||
toType = typePair[1],
|
||||
fromArguments = suffixEntry[0],
|
||||
toArguments = suffixEntry[1],
|
||||
expectedArguments = suffixEntry[indexOfExpectedArguments];
|
||||
|
||||
for each (let prefixEntry in gPrefixes) {
|
||||
let [prefixLength, prefix] = prefixEntry;
|
||||
addTest(prefixLength, prefix, fromType, fromArguments,
|
||||
toType, toArguments, toType, expectedArguments, additive);
|
||||
}
|
||||
}
|
||||
|
||||
// Test that differences in arc flag parameters cause the
|
||||
// interpolation/addition not to occur.
|
||||
addTest(1, "M100,100",
|
||||
"A", [10, 20, 30, 0, 0, 40, 50],
|
||||
"a", [60, 70, 80, 0, 1, 90, 100],
|
||||
"a", [60, 70, 80, 0, 1, 90, 100], additive);
|
||||
addTest(1, "M100,100",
|
||||
"A", [10, 20, 30, 0, 0, 40, 50],
|
||||
"a", [60, 70, 80, 1, 0, 90, 100],
|
||||
"a", [60, 70, 80, 1, 0, 90, 100], additive);
|
||||
|
||||
// Test all pairs of segment types that cannot be interpolated between.
|
||||
for each (let fromType in gTypes) {
|
||||
let fromArguments = generatePathSegmentArguments(fromType, 0);
|
||||
for each (let toType in gTypes) {
|
||||
if (!isValidInterpolation(fromType, toType)) {
|
||||
let toArguments = generatePathSegmentArguments(toType, 1000);
|
||||
addTest(1, "M100,100", fromType, fromArguments,
|
||||
toType, toArguments, toType, toArguments, additive);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Move the document time to half way through the animations.
|
||||
gSVG.setCurrentTime(4);
|
||||
|
||||
// Inspect the results of each subtest.
|
||||
for each (let test in gTests) {
|
||||
let list = test.element.animatedPathSegList;
|
||||
is(list.numberOfItems, test.prefixLength + 1,
|
||||
"Length of animatedPathSegList for interpolation " +
|
||||
(test.usesAddition ? "with addition " : "") +
|
||||
" from " + test.from + " to " + test.to);
|
||||
|
||||
let seg = list.getItem(list.numberOfItems - 1);
|
||||
let propertyNames = argumentNames(test.expectedType);
|
||||
|
||||
let actual = [];
|
||||
for (let i = 0; i < test.expected.length; i++) {
|
||||
actual.push(+seg[propertyNames[i]]);
|
||||
}
|
||||
|
||||
is(seg.pathSegTypeAsLetter + actual, test.expectedType + test.expected,
|
||||
"Path segment for interpolation " +
|
||||
(test.usesAddition ? "with addition " : "") +
|
||||
" from " + test.from + " to " + test.to);
|
||||
}
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
window.addEventListener("load", run, false);
|
||||
]]></script>
|
||||
</body>
|
||||
</html>
|
@ -89,7 +89,7 @@ nsXMLElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
|
||||
(!guard.Mutated(0) ||
|
||||
!mNodeInfo->GetIDAttributeAtom() ||
|
||||
!HasAttr(kNameSpaceID_None, GetIDAttributeName()))) {
|
||||
UnsetFlags(NODE_HAS_ID);
|
||||
ClearHasID();
|
||||
}
|
||||
|
||||
return rv;
|
||||
@ -104,7 +104,7 @@ nsXMLElement::GetIDAttributeName() const
|
||||
nsIAtom*
|
||||
nsXMLElement::DoGetID() const
|
||||
{
|
||||
NS_ASSERTION(HasFlag(NODE_HAS_ID), "Unexpected call");
|
||||
NS_ASSERTION(HasID(), "Unexpected call");
|
||||
|
||||
const nsAttrValue* attrVal = mAttrsAndChildren.GetAttr(GetIDAttributeName());
|
||||
return attrVal ? attrVal->GetAtomValue() : nsnull;
|
||||
@ -118,7 +118,7 @@ nsXMLElement::NodeInfoChanged(nsINodeInfo* aOldNodeInfo)
|
||||
"Can only change document if we're not inside one");
|
||||
nsIDocument* doc = GetCurrentDoc();
|
||||
|
||||
if (HasFlag(NODE_HAS_ID) && doc) {
|
||||
if (HasID() && doc) {
|
||||
const nsAttrValue* attrVal =
|
||||
mAttrsAndChildren.GetAttr(aOldNodeInfo->GetIDAttributeAtom());
|
||||
if (attrVal) {
|
||||
@ -126,13 +126,13 @@ nsXMLElement::NodeInfoChanged(nsINodeInfo* aOldNodeInfo)
|
||||
}
|
||||
}
|
||||
|
||||
UnsetFlags(NODE_HAS_ID);
|
||||
ClearHasID();
|
||||
|
||||
nsIAtom* IDName = GetIDAttributeName();
|
||||
if (IDName) {
|
||||
const nsAttrValue* attrVal = mAttrsAndChildren.GetAttr(IDName);
|
||||
if (attrVal) {
|
||||
SetFlags(NODE_HAS_ID);
|
||||
SetHasID();
|
||||
if (attrVal->Type() == nsAttrValue::eString) {
|
||||
nsString idVal(attrVal->GetStringValue());
|
||||
|
||||
@ -160,11 +160,11 @@ nsXMLElement::ParseAttribute(PRInt32 aNamespaceID,
|
||||
// not that it has an emptystring as the id.
|
||||
RemoveFromIdTable();
|
||||
if (aValue.IsEmpty()) {
|
||||
UnsetFlags(NODE_HAS_ID);
|
||||
ClearHasID();
|
||||
return PR_FALSE;
|
||||
}
|
||||
aResult.ParseAtom(aValue);
|
||||
SetFlags(NODE_HAS_ID);
|
||||
SetHasID();
|
||||
AddToIdTable(aResult.GetAtomValue());
|
||||
return PR_TRUE;
|
||||
}
|
||||
@ -182,7 +182,7 @@ nsXMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
aCompileEventHandlers);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (aDocument && HasFlag(NODE_HAS_ID) && !GetBindingParent()) {
|
||||
if (aDocument && HasID() && !GetBindingParent()) {
|
||||
aDocument->AddToIdTable(this, DoGetID());
|
||||
}
|
||||
|
||||
|
@ -248,8 +248,8 @@ nsXULElement::nsXULElement(already_AddRefed<nsINodeInfo> aNodeInfo)
|
||||
XUL_PROTOTYPE_ATTRIBUTE_METER(gNumElements);
|
||||
}
|
||||
|
||||
nsXULElement::nsXULSlots::nsXULSlots(PtrBits aFlags)
|
||||
: nsXULElement::nsDOMSlots(aFlags)
|
||||
nsXULElement::nsXULSlots::nsXULSlots()
|
||||
: nsXULElement::nsDOMSlots()
|
||||
{
|
||||
}
|
||||
|
||||
@ -264,7 +264,7 @@ nsXULElement::nsXULSlots::~nsXULSlots()
|
||||
nsINode::nsSlots*
|
||||
nsXULElement::CreateSlots()
|
||||
{
|
||||
return new nsXULSlots(mFlagsOrSlots);
|
||||
return new nsXULSlots();
|
||||
}
|
||||
|
||||
/* static */
|
||||
@ -279,13 +279,13 @@ nsXULElement::Create(nsXULPrototypeElement* aPrototype, nsINodeInfo *aNodeInfo,
|
||||
|
||||
element->mPrototype = aPrototype;
|
||||
if (aPrototype->mHasIdAttribute) {
|
||||
element->SetFlags(NODE_HAS_ID);
|
||||
element->SetHasID();
|
||||
}
|
||||
if (aPrototype->mHasClassAttribute) {
|
||||
element->SetFlags(NODE_MAY_HAVE_CLASS);
|
||||
}
|
||||
if (aPrototype->mHasStyleAttribute) {
|
||||
element->SetFlags(NODE_MAY_HAVE_STYLE);
|
||||
element->SetMayHaveStyle();
|
||||
}
|
||||
|
||||
NS_ASSERTION(aPrototype->mScriptTypeID != nsIProgrammingLanguage::UNKNOWN,
|
||||
@ -1408,7 +1408,7 @@ nsXULElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, PRBool aNotify)
|
||||
// XXX Know how to remove POPUP event listeners when an attribute is unset?
|
||||
|
||||
if (isId) {
|
||||
UnsetFlags(NODE_HAS_ID);
|
||||
ClearHasID();
|
||||
}
|
||||
|
||||
if (aNameSpaceID == kNameSpaceID_None) {
|
||||
@ -1778,15 +1778,15 @@ nsXULElement::GetBuilder(nsIXULTemplateBuilder** aBuilder)
|
||||
nsIAtom*
|
||||
nsXULElement::DoGetID() const
|
||||
{
|
||||
NS_ASSERTION(HasFlag(NODE_HAS_ID), "Unexpected call");
|
||||
NS_ASSERTION(HasID(), "Unexpected call");
|
||||
const nsAttrValue* attr =
|
||||
FindLocalOrProtoAttr(kNameSpaceID_None, nsGkAtoms::id);
|
||||
|
||||
// We need the nullcheck here because during unlink the prototype looses
|
||||
// We need the nullcheck here because during unlink the prototype loses
|
||||
// all of its attributes. We might want to change that.
|
||||
// The nullcheck would also be needed if we make UnsetAttr use
|
||||
// nsGenericElement::UnsetAttr as that calls out to various code between
|
||||
// removing the attribute and clearing the NODE_HAS_ID flag.
|
||||
// removing the attribute and calling ClearHasID().
|
||||
|
||||
return attr ? attr->GetAtomValue() : nsnull;
|
||||
}
|
||||
@ -1807,7 +1807,7 @@ nsXULElement::WalkContentStyleRules(nsRuleWalker* aRuleWalker)
|
||||
css::StyleRule*
|
||||
nsXULElement::GetInlineStyleRule()
|
||||
{
|
||||
if (!HasFlag(NODE_MAY_HAVE_STYLE)) {
|
||||
if (!MayHaveStyle()) {
|
||||
return nsnull;
|
||||
}
|
||||
// Fetch the cached style rule from the attributes.
|
||||
|
@ -621,7 +621,7 @@ protected:
|
||||
class nsXULSlots : public nsGenericElement::nsDOMSlots
|
||||
{
|
||||
public:
|
||||
nsXULSlots(PtrBits aFlags);
|
||||
nsXULSlots();
|
||||
virtual ~nsXULSlots();
|
||||
|
||||
nsRefPtr<nsFrameLoader> mFrameLoader;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user